pax_global_header00006660000000000000000000000064126555162130014520gustar00rootroot0000000000000052 comment=1ea8a13bf877e6e74d52da5ce923876c8cc98764 iverilog-10_1/000077500000000000000000000000001265551621300133425ustar00rootroot00000000000000iverilog-10_1/.gitattributes000066400000000000000000000000771265551621300162410ustar00rootroot00000000000000# gperf in MSYS chokes on DOS line endings *.gperf text eol=lf iverilog-10_1/.gitignore000066400000000000000000000030571265551621300153370ustar00rootroot00000000000000# Lines that start with '#' are comments. # # This file is for the development branch of Icarus Verilog. # # The following files will be ignored by git. # Normal editor rules *.swp *~ # Top level generic files tags TAGS cscope.* *.patch *.orig # Object files and libraries *.[oa] gmon*.out gmon*.txt # From autoconf configure config.log config.status Makefile /_pli_types.h config.h /tgt-pcb/pcb_config.h /tgt-pcb/fp.cc /tgt-pcb/fp.h /tgt-pcb/fp.output /tgt-pcb/fp_lex.cc /tgt-vvp/vvp_config.h /tgt-vhdl/vhdl_config.h /vpi/vpi_config.h stamp-*-h /version.h /version_tag.h # Directories autom4te.cache dep # Compiler back end and library files /tgt-vvp/*.conf *.tgt *.vpi /cadpli/cadpli.vpl /tgt-blif/Makefile # lex, yacc and gperf output /driver/cflexor.c /driver/cfparse.c /driver/cfparse.h /driver/cfparse.output /ivlpp/lexor.c /vhdlpp/lexor.cc /vhdlpp/lexor_keyword.cc /vhdlpp/parse.cc /vhdlpp/parse.h /vhdlpp/parse.output /vhdlpp/vhdlpp_config.h /vhdlpp/vhdlpp /lexor.cc /lexor_keyword.cc /parse.cc /parse.h /parse.output /syn-rules.cc /syn-rules.output /vpi/sdf_lexor.c /vpi/sdf_parse.c /vpi/sdf_parse.h /vpi/sdf_parse.output /vpi/sys_readmem_lex.c /vpi/table_mod_lexor.c /vpi/table_mod_parse.c /vpi/table_mod_parse.h /vpi/table_mod_parse.output /vvp/dump.* /vvp/lexor.cc /vvp/parse.cc /vvp/parse.h /vvp/parse.output # Program created files /vvp/tables.cc /iverilog-vpi.man /driver-vpi/res.rc /driver/iverilog.man /vvp/vvp.man # The executables. *.exe /driver/iverilog /iverilog-vpi /ivl /ivlpp/ivlpp /vvp/vvp /ivl.exp /vvp/vvp.exp # Check output /check.vvp iverilog-10_1/AStatement.cc000066400000000000000000000020431265551621300157150ustar00rootroot00000000000000/* * Copyright (c) 2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "AStatement.h" AContrib::AContrib(PExpr*lv, PExpr*rv) : lval_(lv), rval_(rv) { } AContrib::~AContrib() { delete lval_; delete rval_; } AProcess::~AProcess() { } iverilog-10_1/AStatement.h000066400000000000000000000045511265551621300155650ustar00rootroot00000000000000#ifndef IVL_AStatement_H #define IVL_AStatement_H /* * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "ivl_target.h" # include "StringHeap.h" # include "LineInfo.h" # include "Statement.h" # include "PExpr.h" class PExpr; class NetAnalog; class NetScope; class Design; /* * A contribution statement is like an assignment: there is an l-value * expression and an r-value expression. The l-value is a branch probe * expression. */ class AContrib : public Statement { public: AContrib(PExpr*lval, PExpr*rval); ~AContrib(); virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; private: PExpr*lval_; PExpr*rval_; }; /* * An analog process is not a statement, but contains an analog * statement. The process is where we attach process characteristics * such as initial vs. always, attributes.... */ class AProcess : public LineInfo { public: AProcess(ivl_process_type_t t, Statement*st) : type_(t), statement_(st) { } ~AProcess(); bool elaborate(Design*des, NetScope*scope) const; ivl_process_type_t type() const { return type_; } Statement*statement() { return statement_; } map attributes; // Dump the analog process void dump(ostream&out, unsigned ind) const; private: ivl_process_type_t type_; Statement*statement_; private: // not implemented AProcess(const AProcess&); AProcess& operator= (const AProcess&); }; #endif /* IVL_AStatement_H */ iverilog-10_1/Attrib.cc000066400000000000000000000043771265551621300151110ustar00rootroot00000000000000/* * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "Attrib.h" # include Attrib::Attrib() { nlist_ = 0; list_ = 0; } Attrib::~Attrib() { delete[] list_; } const verinum& Attrib::attribute(perm_string key) const { for (unsigned idx = 0 ; idx < nlist_ ; idx += 1) { if (key == list_[idx].key) return list_[idx].val; } static const verinum null; return null; } void Attrib::attribute(perm_string key, const verinum&value) { unsigned idx; for (idx = 0 ; idx < nlist_ ; idx += 1) { if (key == list_[idx].key) { list_[idx].val = value; return; } } struct cell_*tmp = new struct cell_[nlist_+1]; for (idx = 0 ; idx < nlist_ ; idx += 1) tmp[idx] = list_[idx]; tmp[nlist_].key = key; tmp[nlist_].val = value; nlist_ += 1; delete[]list_; list_ = tmp; } bool Attrib::has_compat_attributes(const Attrib&that) const { unsigned idx; for (idx = 0 ; idx < that.nlist_ ; idx += 1) { verinum tmp = attribute(that.list_[idx].key); if (tmp != that.list_[idx].val) return false; } return true; } unsigned Attrib::attr_cnt() const { return nlist_; } perm_string Attrib::attr_key(unsigned idx) const { assert(idx < nlist_); return list_[idx].key; } const verinum& Attrib::attr_value(unsigned idx) const { assert(idx < nlist_); return list_[idx].val; } iverilog-10_1/Attrib.h000066400000000000000000000034041265551621300147410ustar00rootroot00000000000000#ifndef IVL_Attrib_H #define IVL_Attrib_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include "verinum.h" /* * This class keeps a map of key/value pairs. The map can be set from * an STL map, or by setting individual mappings. */ class Attrib { public: Attrib(); virtual ~Attrib(); const verinum&attribute(perm_string key) const; void attribute(perm_string key, const verinum&value); bool has_compat_attributes(const Attrib&that) const; /* Provide a means of iterating over the entries in the map. */ unsigned attr_cnt() const; perm_string attr_key(unsigned idx) const; const verinum& attr_value(unsigned idx) const; private: struct cell_ { perm_string key; verinum val; }; unsigned nlist_; struct cell_*list_; private: // not implemented Attrib(const Attrib&); Attrib& operator= (const Attrib&); }; #endif /* IVL_Attrib_H */ iverilog-10_1/BUGS.txt000066400000000000000000000166021265551621300146500ustar00rootroot00000000000000 HOW TO REPORT BUGS Before I can fix an error, I need to understand what the problem is. Try to explain what is wrong and why you think it is wrong. Please try to include sample code that demonstrates the problem. Include a description of what Icarus Verilog does that is wrong, and what you expect should happen. And include the command line flags passed to the compiler to make the error happen. (This is often overlooked, and sometimes important.) * The Compiler Doesn't Compile If Icarus Verilog doesn't compile, I need to know about the compilation tools you are using. Specifically, I need to know: - Operating system and processor type, - Compiler w/ version, - Versions of any libraries being linked, and - anything else you think relevant. Be aware that I do not have at my disposal a porting lab. I have the workstation on my desk, a Mac laptop, and the Linux/Intel box with a logic analyzer and 'scope hanging off it. * The Compiler Crashes No compiler should crash, no matter what kind of garbage is fed to it. If the compiler crashes, you definitely found a bug and I need to know about it. Icarus Verilog internally checks its state while it works, and if it detects something wrong that it cannot recover from, it will abort intentionally. The "assertion failure" message that the program prints in the process of dying is very important. It tells me where in the source the bad thing happened. Include that message in the bug report. If there are no assertion messages, I need to know that as well. I also need a complete test program that demonstrates the crash. * It Doesn't Like My Perfectly Valid Program(tm) I need to know what you think is right that Icarus Verilog gets wrong. Does it reject your "Perfectly Valid Program(tm)" or does it compile it but give incorrect results? The latter is the most insidious as it doesn't scream out to be fixed unless someone is watching closely. However, if I get a sample program from you, and I can compile it, and I run it and nuclear junk doesn't fall from the sky, I'm moving on to the next problem. So, if your program doesn't compile, tell me so, tell me where the error occurs, and include a complete Perfectly Valid Test Program(tm). You tell me that it fails to compile for you, and I find that it compiles for me, then hooray I fixed it. It can happen, you know. What's on my disk is more recent than the latest snapshot. If your program does compile, but generates incorrect output, I need to know what it says and what you think it should say. From this I can take your sample program and work on Icarus Verilog until it gets the proper results. For this to work, of course, I first need to know what is wrong with the output. Spell it out, because I've been known to miss the obvious. Compiler writers often get buried in the details of the wrong problem. * It Generates Incorrect Target Code As Icarus Verilog adds target code generators, there will be cases where errors in the output netlist format occur. This is a tough nut because I might not have all the tools to test the target format you are reporting problems with. However, if you clearly explain what is right and wrong about the generated output, I will probably be able to fix the problem. It may take a few iterations. In this case, if possible include not only the sample Verilog program, but the generated netlist file(s) and a clear indication of what went wrong or what is expected. If it is not clear to me, I will ask for clarification. * The Output is Correct, But Less Than Ideal If the output is strictly correct, but just not good enough for practical use, I would like to know. These sorts of problems are likely to be more subjective than a core dump, but are worthy of consideration. However, realize that outright errors will get more attention than missed optimizations. THE MAKING OF A GOOD TEST PROGRAM If at all possible, please submit a complete source file that demonstrates the problem. If the error occurs after elaboration, please include a top level module in the program that is suitable for the target format. If I have to write the module myself, I might not write it in a way that tickles the bug. So please, send all the Verilog source that I need to invoke the error. Also, include the command line you use to invoke the compiler. For example: iverilog -o foo.out -tvvp foo.v iverilog foo.vl -s starthere If the error occurs with the null target (``-tnull'') then a top level module may not be needed as long as the ``-s '' switch is given. So when you send a test case, ask yourself "Can poor overworked Steve invoke the error without any Verilog other than what is included?" And while we are at it, please place a copyright notice in your test program and include a GPL license statement if you can. Your test program may find its way into the test suite, and the notices will make it all nice and legal. Please look at the existing tests in the test suite for examples of good test programs. RESEARCHING EXISTING/PAST BUGS, AND FILING REPORTS The URL is the main bug tracking system. Once you believe you have found a bug, you may browse the bugs database for existing bugs that may be related to yours. You might find that your bug has already been fixed in a later release or snapshot. If that's the case, then you are set. Also, consider if you are reporting a bug or really asking for a new feature, and use the appropriate tracker. The bug database supports basic keyword searches, and you can optionally limit your search to active bugs, or fixed bugs. You may also browse the bug database, just to get an idea what is still broken. You may for example find a related bug that explains your symptom. The root page of the bug report database describes how to submit your completed bug report. HOW TO SEND PATCHES Bug reports with patches are very welcome, especially if they are formatted such that I can inspect them, decide that they are obviously correct, and apply them without worry. I prefer patches generated by the git source code tracking system. If you are editing the source, you really should be using the latest version from git. Please see the developer documentation for more detailed instructions -- . When you make a patch, submit it to the "Patches" tracker at . Patches added to the "Patches" tracker enter the developer workflow, are checked, applied to the appropriate git branch, and are pushed. Then the tracker item is closed. If you send patches, *please* tell me what this patch is supposed to accomplish, which branch you intended to be patched, and if appropriate include a test program that demonstrates the efficacy of the patch. (If I have no idea what the patch is for, I will ask for clarification before applying it.) COPYRIGHT ISSUES Icarus Verilog is Copyright (c) 1998-2008 Stephen Williams except where otherwise noted. Minor patches are covered as derivative works (or editorial comment or whatever the appropriate legal term is) and folded into the rest of ivl. However, if a submission can reasonably be considered independently copyrightable, it's yours and I encourage you to claim it with appropriate copyright notices. This submission then falls under the "otherwise noted" category. I must insist that any copyright material submitted for inclusion include the GPL license notice as shown in the rest of the source. iverilog-10_1/COPYING000066400000000000000000000432541265551621300144050ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 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) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General Public License instead of this License. iverilog-10_1/COPYING.lesser000066400000000000000000000636421265551621300157040ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! iverilog-10_1/HName.cc000066400000000000000000000055211265551621300146440ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "HName.h" # include # include # include using namespace std; hname_t::hname_t() { } hname_t::hname_t(perm_string text) : name_(text) { } hname_t::hname_t(perm_string text, int num) : name_(text), number_(1) { number_[0] = num; } hname_t::hname_t(perm_string text, const vector&nums) : name_(text), number_(nums) { } hname_t::hname_t(const hname_t&that) : name_(that.name_), number_(that.number_) { } hname_t& hname_t::operator = (const hname_t&that) { name_ = that.name_; number_ = that.number_; return *this; } bool hname_t::operator < (const hname_t&r) const { int cmp = strcmp(name_, r.name_); if (cmp < 0) return true; if (cmp > 0) return false; // The text parts are equal, so compare then number // parts. Finish as soon as we find one to be less or more // than the other. size_t idx = 0; while (number_.size() > idx || r.number_.size() > idx) { // Ran out of l numbers, so less. if (number_.size() <= idx) return true; // Ran out of r numbers, so greater. if (r.number_.size() <= idx) return false; if (number_[idx] < r.number_[idx]) return true; if (number_[idx] > r.number_[idx]) return false; idx += 1; } // Fall-through means that we are equal, including all the // number parts, so not less. return false; } bool hname_t::operator == (const hname_t&r) const { if (name_ == r.name_) { if (number_.size() != r.number_.size()) return false; for (size_t idx = 0 ; idx < number_.size() ; idx += 1) if (number_[idx] != r.number_[idx]) return false; return true; } return false; } ostream& operator<< (ostream&out, const hname_t&that) { if (that.peek_name() == 0) { out << ""; return out; } out << that.peek_name(); for (size_t idx = 0 ; idx < that.number_.size() ; idx += 1) out << "[" << that.number_[idx] << "]"; return out; } iverilog-10_1/HName.h000066400000000000000000000057601265551621300145130ustar00rootroot00000000000000#ifndef IVL_HName_H #define IVL_HName_H /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "StringHeap.h" # include /* * This class represents a component of a Verilog hierarchical name. A * hierarchical component contains a name string (represented here * with a perm_string) and an optional signed number. This signed * number is used if the scope is part of an array, for example an * array of module instances or a loop generated scope. */ class hname_t { friend ostream& operator<< (ostream&out, const hname_t&that); public: hname_t (); explicit hname_t (perm_string text); explicit hname_t (perm_string text, int num); explicit hname_t (perm_string text, const std::vector&nums); hname_t (const hname_t&that); ~hname_t(); hname_t& operator= (const hname_t&); bool operator == (const hname_t&that) const; bool operator < (const hname_t&that) const; // Return the string part of the hname_t. perm_string peek_name(void) const; size_t has_numbers() const; int peek_number(size_t idx) const; const std::vector&peek_numbers() const; private: perm_string name_; // If this vector has size, then the numbers all together make // up part of the hierarchical name. std::vector number_; private: // not implemented }; inline hname_t::~hname_t() { } inline perm_string hname_t::peek_name(void) const { return name_; } inline int hname_t::peek_number(size_t idx) const { assert(number_.size() > idx); return number_[idx]; } inline const std::vector& hname_t::peek_numbers(void) const { return number_; } inline size_t hname_t::has_numbers() const { return number_.size(); } extern ostream& operator<< (ostream&, const hname_t&); inline bool operator != (const hname_t&l, const hname_t&r) { return ! (l == r); } inline ostream& operator<< (ostream&out, const list&ll) { list::const_iterator cur = ll.begin(); out << *cur; ++ cur; while (cur != ll.end()) { out << "." << *cur; ++ cur; } return out; } #endif /* IVL_HName_H */ iverilog-10_1/INSTALL000066400000000000000000000170511265551621300143770ustar00rootroot00000000000000Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. iverilog-10_1/Makefile.in000066400000000000000000000335621265551621300154200ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh # The interesting make targets are: # # make version # Force the version_tag.h file to be rebuilt. Otherwise, it will only # be built if it is missing. # # make all # make install # # The "suffix" is used as an installation suffix. It modifies certain # key install paths/files such that a build and install of Icarus Verilog # with the same $(prefix) but a different $(suffix) will not interfere. # The normal configuration leaves suffix empty suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ datarootdir = @datarootdir@ SUBDIRS = ivlpp vhdlpp vvp vpi libveriuser cadpli tgt-null tgt-stub tgt-vvp \ tgt-vhdl tgt-vlog95 tgt-pcb tgt-blif tgt-sizer driver # Only run distclean for these directories. NOTUSED = tgt-fpga tgt-pal tgt-verilog ifeq (@MINGW32@,yes) SUBDIRS += driver-vpi else NOTUSED += driver-vpi endif # To get the version headers to build correctly we only want to look # for C++ files in the source directory. All other files will require # an explicit $(srcdir). The one exception to this is if we need to # rebuild the lexor_keyword.cc file. If we do, then we want to use the # local version instead of the one is $(srcdir). vpath lexor_keyword.cc . vpath %.cc $(srcdir)/libmisc vpath %.cc $(srcdir) bindir = @bindir@ libdir = @libdir@ # This is actually the directory where we install our own header files. # It is a little different from the generic includedir. includedir = @includedir@/iverilog$(suffix) mandir = @mandir@ dllib=@DLLIB@ # For a cross compile these defines will need to be set accordingly. HOSTCC = @CC@ HOSTCFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CC = @CC@ CXX = @CXX@ DLLTOOL = @DLLTOOL@ INSTALL = @INSTALL@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ YACC = @YACC@ MAN = @MAN@ PS2PDF = @PS2PDF@ GIT = @GIT@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -Ilibmisc else INCLUDE_PATH = -I. -I$(srcdir) -I$(srcdir)/libmisc endif CPPFLAGS = @DEFS@ $(INCLUDE_PATH) @CPPFLAGS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ PICFLAGS = @PICFLAG@ LDFLAGS = @rdynamic@ @LDFLAGS@ CTARGETFLAGS = @CTARGETFLAGS@ # Source files in the libmisc directory M = LineInfo.o StringHeap.o TT = t-dll.o t-dll-api.o t-dll-expr.o t-dll-proc.o t-dll-analog.o FF = cprop.o nodangle.o synth.o synth2.o syn-rules.o O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ elab_expr.o elaborate_analog.o elab_lval.o elab_net.o \ elab_scope.o elab_sig.o elab_sig_analog.o elab_type.o \ emit.o eval.o eval_attrib.o \ eval_tree.o expr_synth.o functor.o lexor.o lexor_keyword.o link_const.o \ load_module.o netlist.o netmisc.o nettypes.o net_analog.o net_assign.o \ net_design.o netclass.o netdarray.o \ netenum.o netparray.o netqueue.o netscalar.o netstruct.o netvector.o \ net_event.o net_expr.o net_func.o \ net_func_eval.o net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ pform_disciplines.o pform_dump.o pform_package.o pform_pclass.o \ pform_class_type.o pform_string_type.o pform_struct_type.o pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o target.o \ Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PGate.o \ PGenerate.o PModport.o PPackage.o PScope.o PSpec.o PTask.o PUdp.o \ PFunction.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) all: dep config.h _pli_types.h version_tag.h ivl@EXEEXT@ version.exe iverilog-vpi.man $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true # In the windows world, the installer will need a dosify program to # dosify text files. ifeq (@MINGW32@,yes) all: dosify.exe dosify.exe: $(srcdir)/dosify.c $(HOSTCC) $(HOSTCFLAGS) -o dosify.exe $(srcdir)/dosify.c endif # This rule rules the compiler in the trivial hello.vl program to make # sure the basics were compiled properly. check: all $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true test -r check.conf || cp $(srcdir)/check.conf . driver/iverilog -B. -BPivlpp -tcheck -ocheck.vvp $(srcdir)/examples/hello.vl ifeq (@WIN32@,yes) ifeq (@install_suffix@,) vvp/vvp -M- -M./vpi ./check.vvp | grep 'Hello, World' else # On Windows if we have a suffix we must run the vvp part of # the test with a suffix since it was built/linked that way. ln vvp/vvp.exe vvp/vvp$(suffix).exe vvp/vvp$(suffix) -M- -M./vpi ./check.vvp | grep 'Hello, World' rm vvp/vvp$(suffix).exe endif else vvp/vvp -M- -M./vpi ./check.vvp | grep 'Hello, World' endif clean: $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true rm -f *.o parse.cc parse.h lexor.cc rm -f ivl.exp iverilog-vpi.man iverilog-vpi.pdf iverilog-vpi.ps rm -f parse.output syn-rules.output dosify.exe ivl@EXEEXT@ check.vvp rm -f lexor_keyword.cc libivl.a libvpi.a iverilog-vpi syn-rules.cc rm -rf dep rm -f version.exe distclean: clean $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true $(foreach dir,$(NOTUSED),$(MAKE) -C $(dir) $@ && ) true rm -f Makefile config.status config.log config.cache rm -f stamp-config-h config.h rm -f stamp-_pli_types-h _pli_types.h ifneq (@srcdir@,.) rm -f version_tag.h check.conf rmdir $(SUBDIRS) $(NOTUSED) endif rm -rf autom4te.cache cppcheck: $(O:.o=.cc) $(srcdir)/dosify.c $(srcdir)/version.c cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ cppcheck-all: $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) cppcheck && ) true $(foreach dir,$(NOTUSED),$(MAKE) -C $(dir) cppcheck && ) true $(MAKE) cppcheck Makefile: $(srcdir)/Makefile.in config.status ./config.status --file=$@ dep: mkdir dep stamp-config-h: $(srcdir)/config.h.in config.status @rm -f $@ ./config.status config.h config.h: stamp-config-h stamp-_pli_types-h: $(srcdir)/_pli_types.h.in config.status @rm -f $@ ./config.status _pli_types.h _pli_types.h: stamp-_pli_types-h $(srcdir)/configure: $(srcdir)/configure.in $(srcdir)/aclocal.m4 cd $(srcdir) && autoconf config.status: $(srcdir)/configure ./config.status --recheck ./config.status ifeq (@WIN32@,yes) # Under Windows (mingw) I need to make the ivl.exe in two steps. # The first step makes an ivl.exe that dlltool can use to make an # export and import library, and the last link makes a, ivl.exe # that really exports the things that the import library imports. ivl@EXEEXT@: $O $(srcdir)/ivl.def $(CXX) -o ivl@EXEEXT@ $O $(dllib) @EXTRALIBS@ $(DLLTOOL) --dllname ivl@EXEEXT@ --def $(srcdir)/ivl.def \ --output-lib libivl.a --output-exp ivl.exp $(CXX) $(LDFLAGS) -o ivl@EXEEXT@ ivl.exp $O $(dllib) @EXTRALIBS@ else ivl@EXEEXT@: $O $(CXX) $(LDFLAGS) -o ivl@EXEEXT@ $O $(dllib) endif ifeq (@MINGW32@,no) all: iverilog-vpi iverilog-vpi: $(srcdir)/iverilog-vpi.sh Makefile sed -e 's;@SHARED@;@shared@;' -e 's;@PIC@;@PICFLAG@;' \ -e 's;@SUFFIX@;$(suffix);' \ -e 's;@IVCC@;$(CC);' \ -e 's;@IVCXX@;$(CXX);' \ -e 's;@IVCFLAGS@;$(CFLAGS);' \ -e 's;@IVCXXFLAGS@;$(CXXFLAGS);' \ -e 's;@IVCTARGETFLAGS@;$(CTARGETFLAGS);' \ -e 's;@INCLUDEDIR@;$(includedir);' \ -e 's;@LIBDIR@;@libdir@;' $< > $@ chmod +x $@ endif version.exe: $(srcdir)/version.c $(srcdir)/version_base.h version_tag.h $(HOSTCC) $(HOSTCFLAGS) -o version.exe -I. -I$(srcdir) $(srcdir)/version.c %.o: %.cc config.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d # Here are some explicit dependencies needed to get things going. main.o: main.cc version_tag.h lexor.o: lexor.cc parse.h parse.o: parse.cc # Build this in two steps to avoid parallel build issues (see pr3462585) parse.cc: $(srcdir)/parse.y $(YACC) --verbose -t -p VL -d -o $@ $< parse.h: parse.cc mv parse.cc.h $@ 2>/dev/null || mv parse.hh $@ syn-rules.cc: $(srcdir)/syn-rules.y $(YACC) --verbose -t -p syn_ -o $@ $< lexor.cc: $(srcdir)/lexor.lex $(LEX) -s -t $< > $@ lexor_keyword.o: lexor_keyword.cc parse.h lexor_keyword.cc: $(srcdir)/lexor_keyword.gperf gperf -o -i 7 -C -k 1-4,6,9,$$ -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false) iverilog-vpi.man: $(srcdir)/iverilog-vpi.man.in version.exe ./version.exe `head -1 $(srcdir)/iverilog-vpi.man.in`'\n' > $@ tail -n +2 $(srcdir)/iverilog-vpi.man.in >> $@ iverilog-vpi.ps: iverilog-vpi.man $(MAN) -t ./iverilog-vpi.man > iverilog-vpi.ps iverilog-vpi.pdf: iverilog-vpi.ps $(PS2PDF) iverilog-vpi.ps iverilog-vpi.pdf # For VERSION_TAG in driver/main.c, first try git-describe, then look for a # version_tag.h file in the source tree (included in snapshots and releases), # and finally use nothing. # "true" and "false" in the next few lines are Unix shell command names ifeq ($(GIT),none) GIT_PRESENT = false else GIT_PRESENT = true endif version_tag.h version: @if $(GIT_PRESENT) && test -d $(srcdir)/.git; then \ echo "Using git-describe for VERSION_TAG"; \ tmp=`(cd $(srcdir) && $(GIT) describe --always --dirty) \ | sed -e 's;\(.*\);#define VERSION_TAG "\1";'`; \ echo "$$tmp" | diff - version_tag.h > /dev/null 2>&1 || echo "$$tmp" > version_tag.h || exit 1; \ elif test -r $(srcdir)/version_tag.h; then \ echo "Using $(srcdir)/version_tag.h for VERSION_TAG"; \ diff $(srcdir)/version_tag.h version_tag.h > /dev/null 2>&1 || cp $(srcdir)/version_tag.h version_tag.h; \ else \ echo "Using empty VERSION_TAG"; \ echo '#define VERSION_TAG ""' > version_tag.h; \ fi ifeq (@MINGW32@,yes) ifeq ($(MAN),none) INSTALL_DOC = $(mandir)/man1/iverilog-vpi$(suffix).1 else ifeq ($(PS2PDF),none) INSTALL_DOC = $(mandir)/man1/iverilog-vpi$(suffix).1 else INSTALL_DOC = $(prefix)/iverilog-vpi$(suffix).pdf $(mandir)/man1/iverilog-vpi$(suffix).1 all: dep iverilog-vpi.pdf endif endif INSTALL_DOCDIR = $(mandir)/man1 else INSTALL_DOC = $(mandir)/man1/iverilog-vpi$(suffix).1 INSTALL_DOCDIR = $(mandir)/man1 endif ifeq (@MINGW32@,yes) WIN32_INSTALL = else WIN32_INSTALL = $(bindir)/iverilog-vpi$(suffix) endif install: all installdirs $(libdir)/ivl$(suffix)/ivl@EXEEXT@ $(libdir)/ivl$(suffix)/include/constants.vams $(libdir)/ivl$(suffix)/include/disciplines.vams $(includedir)/ivl_target.h $(includedir)/_pli_types.h $(includedir)/sv_vpi_user.h $(includedir)/vpi_user.h $(includedir)/acc_user.h $(includedir)/veriuser.h $(WIN32_INSTALL) $(INSTALL_DOC) $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true $(bindir)/iverilog-vpi$(suffix): ./iverilog-vpi $(INSTALL_SCRIPT) ./iverilog-vpi "$(DESTDIR)$(bindir)/iverilog-vpi$(suffix)" $(libdir)/ivl$(suffix)/ivl@EXEEXT@: ./ivl@EXEEXT@ $(INSTALL_PROGRAM) ./ivl@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/ivl@EXEEXT@" $(libdir)/ivl$(suffix)/include/constants.vams: $(srcdir)/constants.vams $(INSTALL_DATA) $(srcdir)/constants.vams "$(DESTDIR)$(libdir)/ivl$(suffix)/include/constants.vams" $(libdir)/ivl$(suffix)/include/disciplines.vams: $(srcdir)/disciplines.vams $(INSTALL_DATA) $(srcdir)/disciplines.vams "$(DESTDIR)$(libdir)/ivl$(suffix)/include/disciplines.vams" $(includedir)/ivl_target.h: $(srcdir)/ivl_target.h $(INSTALL_DATA) $(srcdir)/ivl_target.h "$(DESTDIR)$(includedir)/ivl_target.h" $(includedir)/_pli_types.h: _pli_types.h $(INSTALL_DATA) $< "$(DESTDIR)$(includedir)/_pli_types.h" $(includedir)/sv_vpi_user.h: $(srcdir)/sv_vpi_user.h $(INSTALL_DATA) $(srcdir)/sv_vpi_user.h "$(DESTDIR)$(includedir)/sv_vpi_user.h" $(includedir)/vpi_user.h: $(srcdir)/vpi_user.h $(INSTALL_DATA) $(srcdir)/vpi_user.h "$(DESTDIR)$(includedir)/vpi_user.h" $(includedir)/acc_user.h: $(srcdir)/acc_user.h $(INSTALL_DATA) $(srcdir)/acc_user.h "$(DESTDIR)$(includedir)/acc_user.h" $(includedir)/veriuser.h: $(srcdir)/veriuser.h $(INSTALL_DATA) $(srcdir)/veriuser.h "$(DESTDIR)$(includedir)/veriuser.h" $(mandir)/man1/iverilog-vpi$(suffix).1: iverilog-vpi.man $(INSTALL_DATA) iverilog-vpi.man "$(DESTDIR)$(mandir)/man1/iverilog-vpi$(suffix).1" $(prefix)/iverilog-vpi$(suffix).pdf: iverilog-vpi.pdf $(INSTALL_DATA) iverilog-vpi.pdf "$(DESTDIR)$(prefix)/iverilog-vpi$(suffix).pdf" installdirs: $(srcdir)/mkinstalldirs $(srcdir)/mkinstalldirs "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(includedir)" \ "$(DESTDIR)$(libdir)/ivl$(suffix)" \ "$(DESTDIR)$(libdir)/ivl$(suffix)/include" \ "$(DESTDIR)$(mandir)" \ "$(DESTDIR)$(mandir)/man1" uninstall: $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true for f in ivl@EXEEXT@ include/constants.vams include/disciplines.vams; \ do rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/$$f"; done -rmdir "$(DESTDIR)$(libdir)/ivl$(suffix)/include" -rmdir "$(DESTDIR)$(libdir)/ivl$(suffix)" for f in verilog$(suffix) iverilog-vpi$(suffix) gverilog$(suffix)@EXEEXT@; \ do rm -f "$(DESTDIR)$(bindir)/$$f"; done for f in ivl_target.h vpi_user.h _pli_types.h sv_vpi_user.h acc_user.h veriuser.h; \ do rm -f "$(DESTDIR)$(includedir)/$$f"; done -test X$(suffix) = X || rmdir "$(DESTDIR)$(includedir)" rm -f "$(DESTDIR)$(mandir)/man1/iverilog-vpi$(suffix).1" "$(DESTDIR)$(prefix)/iverilog-vpi$(suffix).pdf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/Module.cc000066400000000000000000000065101265551621300151000ustar00rootroot00000000000000/* * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "Module.h" # include "PGate.h" # include "PWire.h" # include list Module::user_defparms; /* n is a permallocated string. */ Module::Module(LexicalScope*parent, perm_string n) : PScopeExtra(n, parent) { library_flag = false; is_cell = false; program_block = false; uc_drive = UCD_NONE; timescale_warn_done = false; time_unit = 0; time_precision = 0; time_from_timescale = false; } Module::~Module() { } void Module::add_gate(PGate*gate) { gates_.push_back(gate); } unsigned Module::port_count() const { return ports.size(); } /* * Return the array of PEIdent object that are at this port of the * module. If the port is internally unconnected, return an empty * array. */ const vector& Module::get_port(unsigned idx) const { assert(idx < ports.size()); static const vector zero; if (ports[idx]) return ports[idx]->expr; else return zero; } unsigned Module::find_port(const char*name) const { assert(name != 0); for (unsigned idx = 0 ; idx < ports.size() ; idx += 1) { if (ports[idx] == 0) { /* It is possible to have undeclared ports. These are ports that are skipped in the declaration, for example like so: module foo(x ,, y); The port between x and y is unnamed and thus inaccessible to binding by name. */ continue; } assert(ports[idx]); if (ports[idx]->name == name) return idx; } return ports.size(); } perm_string Module::get_port_name(unsigned idx) const { assert(idx < ports.size()); if (ports[idx] == 0 || ports[idx]->name.str() == 0) { /* It is possible to have undeclared ports. These are ports that are skipped in the declaration, for example like so: module foo(x ,, y); The port between x and y is unnamed and thus inaccessible to binding by name. Port references that aren't simple or escaped identifiers are also inaccessible to binding by name. */ return perm_string::literal("unnamed"); } return ports[idx]->name; } PGate* Module::get_gate(perm_string name) { for (list::iterator cur = gates_.begin() ; cur != gates_.end() ; ++ cur ) { if ((*cur)->get_name() == name) return *cur; } return 0; } const list& Module::get_gates() const { return gates_; } iverilog-10_1/Module.h000066400000000000000000000135021265551621300147410ustar00rootroot00000000000000#ifndef IVL_Module_H #define IVL_Module_H /* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "StringHeap.h" # include "HName.h" # include "named.h" # include "PScope.h" # include "LineInfo.h" # include "netlist.h" # include "pform_types.h" class PExpr; class PEIdent; class PGate; class PGenerate; class PModport; class PSpecPath; class PTask; class PFunction; class PWire; class PProcess; class Design; class NetScope; /* * A module is a named container and scope. A module holds a bunch of * semantic quantities such as wires and gates. The module is * therefore the handle for grasping the described circuit. * * SystemVerilog introduces program blocks and interfaces. These have * much in common with modules, so the Module class is used to represent * these containers as well. */ class Module : public PScopeExtra, public LineInfo { /* The module ports are in general a vector of port_t objects. Each port has a name and an ordered list of wires. The name is the means that the outside uses to access the port, the wires are the internal connections to the port. */ public: struct port_t { perm_string name; vector expr; }; public: /* The name passed here is the module name, not the instance name. This name must be a permallocated string. */ explicit Module(LexicalScope*parent, perm_string name); ~Module(); /* Initially false. This is set to true if the module has been declared as a library module. This makes the module ineligible for being chosen as an implicit root. It has no other effect. */ bool library_flag; bool is_cell; /* This is true if the module represents a program block instead of a module/cell. Program blocks have content restrictions and slightly modify scheduling semantics. */ bool program_block; /* This is true if the module represents a interface instead of a module/cell. Interfaces have different content restrictions and some extra allowed items. */ bool is_interface; enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; UCDriveType uc_drive; /* specparams are simpler than other parameters, in that they can have a range, but not an explicit type. The restrictions are enforced by the parser. */ mapspecparams; /* The module also has defparam assignments which don't create new parameters within the module, but may be used to set values within this module (when instantiated) or in other instantiated modules. */ typedef pair named_expr_t; listdefparms; static listuser_defparms; /* Parameters may be overridden at instantiation time; the overrides do not contain explicit parameter names, but rather refer to parameters in the order they appear in the instantiated module. Therefore a list of names in module-order is needed to pass from a parameter-index to its name. */ list param_names; /* This is an array of port descriptors, which is in turn a named array of PEident pointers. */ vector ports; map attributes; /* These are the timescale for this module. The default is set by the `timescale directive. */ int time_unit, time_precision; bool time_from_timescale; bool timescale_warn_done; /* The module has a list of generate schemes that appear in the module definition. These are used at elaboration time. */ list generate_schemes; /* Nested modules are placed here, and are not elaborated unless they are instantiated, implicitly or explicitly. */ std::map nested_modules; /* An interface can contain one or more named modport lists. The parser will ensure these don't appear in modules or program blocks. */ map modports; list specify_paths; // The mod_name() is the name of the module type. perm_string mod_name() const { return pscope_name(); } void add_gate(PGate*gate); unsigned port_count() const; const vector& get_port(unsigned idx) const; unsigned find_port(const char*name) const; // Return port name ("" for undeclared port) perm_string get_port_name(unsigned idx) const; PGate* get_gate(perm_string name); const list& get_gates() const; void dump(ostream&out) const; bool elaborate(Design*, NetScope*scope) const; typedef map replace_t; bool elaborate_scope(Design*, NetScope*scope, const replace_t&rep); bool elaborate_sig(Design*, NetScope*scope) const; private: void dump_specparams_(ostream&out, unsigned indent) const; list gates_; private: // Not implemented Module(const Module&); Module& operator= (const Module&); }; #endif /* IVL_Module_H */ iverilog-10_1/PClass.cc000066400000000000000000000017401265551621300150400ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PClass.h" PClass::PClass(perm_string name, LexicalScope*parent) : PScopeExtra(name, parent), type(0) { } PClass::~PClass() { } iverilog-10_1/PClass.h000066400000000000000000000026441265551621300147060ustar00rootroot00000000000000#ifndef IVL_PClass_H #define IVL_PClass_H /* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PScope.h" # include "LineInfo.h" # include "StringHeap.h" # include class PChainConstructor; /* * SystemVerilog supports class declarations with their own lexical * scope, etc. The parser arranges for these to be created and * collected. */ class PClass : public PScopeExtra, public LineInfo { public: explicit PClass (perm_string name, LexicalScope*parent); ~PClass(); void dump(std::ostream&out, unsigned indent) const; public: class_type_t*type; }; #endif /* IVL_PClass_H */ iverilog-10_1/PDelays.cc000066400000000000000000000123431265551621300152150ustar00rootroot00000000000000/* * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include "PDelays.h" # include "PExpr.h" # include "verinum.h" # include "netmisc.h" bool dly_used_no_timescale = false; bool dly_used_timescale = false; bool display_ts_dly_warning = true; PDelays::PDelays() { delete_flag_ = true; for (unsigned idx = 0 ; idx < 3 ; idx += 1) delay_[idx] = 0; } PDelays::~PDelays() { if (delete_flag_) { for (unsigned idx = 0 ; idx < 3 ; idx += 1) delete delay_[idx]; } } void PDelays::set_delay(PExpr*del) { assert(del); assert(delay_[0] == 0); delay_[0] = del; delete_flag_ = true; } void PDelays::set_delays(const list*del, bool df) { assert(del); assert(del->size() <= 3); list::const_iterator cur = del->begin(); for (unsigned idx = 0 ; cur != del->end() ; idx += 1, ++cur) delay_[idx] = *cur; delete_flag_ = df; } unsigned PDelays::delay_count() const { unsigned dly_cnt = 0; for (unsigned idx = 0 ; idx < 3 ; idx += 1) if (delay_[idx]) dly_cnt += 1; return dly_cnt; } static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) { NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based * delays in the design, since this is likely an error. */ if (scope->time_from_timescale()) dly_used_timescale = true; else dly_used_no_timescale = true; if (display_ts_dly_warning && dly_used_no_timescale && dly_used_timescale) { cerr << "warning: Found both default and " "`timescale based delays. Use" << endl; cerr << " -Wtimescale to find the " "module(s) with no `timescale." << endl; display_ts_dly_warning = false; } /* If the delay expression is a real constant or vector constant, then evaluate it, scale it to the local time units, and return an adjusted value. */ if (NetECReal*tmp = dynamic_cast(dex)) { uint64_t delay = get_scaled_time_from_real(des, scope, tmp); delete tmp; NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); tmp2->set_line(*expr); return tmp2; } if (NetEConst*tmp = dynamic_cast(dex)) { verinum fn = tmp->value(); uint64_t delay = des->scale_to_precision(fn.as_ulong64(), scope); delete tmp; NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); tmp2->set_line(*expr); return tmp2; } /* Oops, cannot evaluate down to a constant. */ return dex; } static NetExpr* make_delay_nets(Design*des, NetScope*scope, NetExpr*expr) { if (expr == 0) return 0; if (dynamic_cast (expr)) return expr; if (dynamic_cast (expr)) return expr; NetNet*sig = expr->synthesize(des, scope, expr); if (sig == 0) { cerr << expr->get_fileline() << ": error: Expression " << *expr << " is not suitable as a delay expression." << endl; des->errors += 1; return 0; } expr = new NetESignal(sig); return expr; } static NetExpr* calc_decay_time(NetExpr *rise, NetExpr *fall) { NetEConst *c_rise = dynamic_cast(rise); NetEConst *c_fall = dynamic_cast(fall); if (c_rise && c_fall) { if (c_rise->value() < c_fall->value()) return rise; else return fall; } return 0; } void PDelays::eval_delays(Design*des, NetScope*scope, NetExpr*&rise_time, NetExpr*&fall_time, NetExpr*&decay_time, bool as_nets_flag) const { assert(scope); if (delay_[0]) { rise_time = calculate_val(des, scope, delay_[0]); if (as_nets_flag) rise_time = make_delay_nets(des, scope, rise_time); if (delay_[1]) { fall_time = calculate_val(des, scope, delay_[1]); if (as_nets_flag) fall_time = make_delay_nets(des, scope, fall_time); if (delay_[2]) { decay_time = calculate_val(des, scope, delay_[2]); if (as_nets_flag) decay_time = make_delay_nets(des, scope, decay_time); } else { // If this is zero then we need to do the min() // at run time. decay_time = calc_decay_time(rise_time, fall_time); } } else { assert(delay_[2] == 0); fall_time = rise_time; decay_time = rise_time; } } else { rise_time = 0; fall_time = 0; decay_time = 0; } } iverilog-10_1/PDelays.h000066400000000000000000000040601265551621300150540ustar00rootroot00000000000000#ifndef IVL_PDelays_H #define IVL_PDelays_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "svector.h" # include # include # include #ifdef __GNUC__ #if __GNUC__ > 2 using namespace std; #endif #endif class Design; class NetScope; class NetExpr; class PExpr; /* * Various PForm objects can carry delays. These delays include rise, * fall and decay times. This class arranges to carry the triplet. */ class PDelays { public: PDelays(); ~PDelays(); /* Set the delay expressions. If the delete_flag is true, then this object takes ownership of the expressions, and will delete it in the destructor. */ void set_delay(PExpr*); void set_delays(const list*del, bool delete_flag=true); unsigned delay_count() const; void eval_delays(Design*des, NetScope*scope, NetExpr*&rise_time, NetExpr*&fall_time, NetExpr*&decay_time, bool as_nets_flag =false) const; void dump_delays(ostream&out) const; private: PExpr* delay_[3]; bool delete_flag_; private: // not implemented PDelays(const PDelays&); PDelays& operator= (const PDelays&); }; ostream& operator << (ostream&o, const PDelays&); #endif /* IVL_PDelays_H */ iverilog-10_1/PEvent.cc000066400000000000000000000017771265551621300150660ustar00rootroot00000000000000/* * Copyright (c) 2004 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PEvent.h" PEvent::PEvent(perm_string n) : name_(n) { } PEvent::~PEvent() { } perm_string PEvent::name() const { return name_; } iverilog-10_1/PEvent.h000066400000000000000000000032031265551621300147120ustar00rootroot00000000000000#ifndef IVL_PEvent_H #define IVL_PEvent_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "StringHeap.h" # include class Design; class NetScope; /* * The PEvent class represents event objects. These are things that * are declared in Verilog as ``event foo;'' The name passed to the * constructor is the "foo" part of the declaration. */ class PEvent : public LineInfo { public: // The name is a perm-allocated string. It is the simple name // of the event, without any scope. explicit PEvent(perm_string name); ~PEvent(); perm_string name() const; void elaborate_scope(Design*des, NetScope*scope) const; private: perm_string name_; private: // not implemented PEvent(const PEvent&); PEvent& operator= (const PEvent&); }; #endif /* IVL_PEvent_H */ iverilog-10_1/PExpr.cc000066400000000000000000000305541265551621300147160ustar00rootroot00000000000000/* * Copyright (c) 1998-2012 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include "compiler.h" # include "PExpr.h" # include "PWire.h" # include "Module.h" # include "netmisc.h" # include "util.h" # include PExpr::PExpr() { expr_type_ = IVL_VT_NO_TYPE; expr_width_ = 0; min_width_ = 0; signed_flag_ = false; } PExpr::~PExpr() { } void PExpr::declare_implicit_nets(LexicalScope*, NetNet::Type) { } bool PExpr::has_aa_term(Design*, NetScope*) const { return false; } bool PExpr::is_the_same(const PExpr*that) const { return typeid(this) == typeid(that); } NetNet* PExpr::elaborate_lnet(Design*, NetScope*) const { cerr << get_fileline() << ": error: " << "expression not valid in assign l-value: " << *this << endl; return 0; } NetNet* PExpr::elaborate_bi_net(Design*, NetScope*) const { cerr << get_fileline() << ": error: " << "expression not valid as argument to inout port: " << *this << endl; return 0; } bool PExpr::is_collapsible_net(Design*, NetScope*) const { return false; } const char* PExpr::width_mode_name(width_mode_t mode) { switch (mode) { case PExpr::SIZED: return "sized"; case PExpr::UNSIZED: return "unsized"; case PExpr::EXPAND: return "expand"; case PExpr::LOSSLESS: return "lossless"; case PExpr::UPSIZE: return "upsize"; default: return "??"; } } PEAssignPattern::PEAssignPattern() { } PEAssignPattern::PEAssignPattern(const list&p) : parms_(p.size()) { size_t idx = 0; for (list::const_iterator cur = p.begin() ; cur != p.end() ; ++cur) { parms_[idx] = *cur; idx += 1; } } PEAssignPattern::~PEAssignPattern() { } PEBinary::PEBinary(char op, PExpr*l, PExpr*r) : op_(op), left_(l), right_(r) { } PEBinary::~PEBinary() { } void PEBinary::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { assert(left_ && right_); left_->declare_implicit_nets(scope, type); right_->declare_implicit_nets(scope, type); } bool PEBinary::has_aa_term(Design*des, NetScope*scope) const { assert(left_ && right_); return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope); } PECastSize::PECastSize(unsigned si, PExpr*b) : size_(si), base_(b) { } PECastSize::~PECastSize() { } PECastType::PECastType(data_type_t*t, PExpr*b) : target_(t), base_(b) { } PECastType::~PECastType() { } PEBComp::PEBComp(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { l_width_ = 0; r_width_ = 0; } PEBComp::~PEBComp() { } PEBLogic::PEBLogic(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { assert(op == 'a' || op == 'o'); } PEBLogic::~PEBLogic() { } PEBLeftWidth::PEBLeftWidth(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { } PEBLeftWidth::~PEBLeftWidth() { } PEBPower::PEBPower(char op, PExpr*l, PExpr*r) : PEBLeftWidth(op, l, r) { } PEBPower::~PEBPower() { } PEBShift::PEBShift(char op, PExpr*l, PExpr*r) : PEBLeftWidth(op, l, r) { } PEBShift::~PEBShift() { } PECallFunction::PECallFunction(const pform_name_t&n, const vector &parms) : package_(0), path_(n), parms_(parms) { } PECallFunction::PECallFunction(PPackage*pkg, const pform_name_t&n, const vector &parms) : package_(pkg), path_(n), parms_(parms) { } static pform_name_t pn_from_ps(perm_string n) { name_component_t tmp_name (n); pform_name_t tmp; tmp.push_back(tmp_name); return tmp; } PECallFunction::PECallFunction(PPackage*pkg, perm_string n, const list &parms) : package_(pkg), path_(pn_from_ps(n)), parms_(parms.size()) { int tmp_idx = 0; assert(parms_.size() == parms.size()); for (list::const_iterator idx = parms.begin() ; idx != parms.end() ; ++idx) parms_[tmp_idx++] = *idx; } PECallFunction::PECallFunction(perm_string n, const vector&parms) : package_(0), path_(pn_from_ps(n)), parms_(parms) { } PECallFunction::PECallFunction(perm_string n) : package_(0), path_(pn_from_ps(n)) { } // NOTE: Anachronism. Try to work all use of svector out. PECallFunction::PECallFunction(const pform_name_t&n, const list &parms) : package_(0), path_(n), parms_(parms.size()) { int tmp_idx = 0; assert(parms_.size() == parms.size()); for (list::const_iterator idx = parms.begin() ; idx != parms.end() ; ++idx) parms_[tmp_idx++] = *idx; } PECallFunction::PECallFunction(perm_string n, const list&parms) : package_(0), path_(pn_from_ps(n)), parms_(parms.size()) { int tmp_idx = 0; assert(parms_.size() == parms.size()); for (list::const_iterator idx = parms.begin() ; idx != parms.end() ; ++idx) parms_[tmp_idx++] = *idx; } PECallFunction::~PECallFunction() { } void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { parms_[idx]->declare_implicit_nets(scope, type); } } bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const { bool flag = false; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { flag = parms_[idx]->has_aa_term(des, scope) || flag; } return flag; } PEConcat::PEConcat(const list&p, PExpr*r) : parms_(p.size()), width_modes_(SIZED, p.size()), repeat_(r) { int tmp_idx = 0; assert(parms_.size() == p.size()); for (list::const_iterator idx = p.begin() ; idx != p.end() ; ++idx) parms_[tmp_idx++] = *idx; tested_scope_ = 0; repeat_count_ = 1; } PEConcat::~PEConcat() { delete repeat_; } void PEConcat::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { parms_[idx]->declare_implicit_nets(scope, type); } } bool PEConcat::has_aa_term(Design*des, NetScope*scope) const { bool flag = false; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { flag = parms_[idx]->has_aa_term(des, scope) || flag; } if (repeat_) flag = repeat_->has_aa_term(des, scope) || flag; return flag; } PEEvent::PEEvent(PEEvent::edge_t t, PExpr*e) : type_(t), expr_(e) { } PEEvent::~PEEvent() { } PEEvent::edge_t PEEvent::type() const { return type_; } bool PEEvent::has_aa_term(Design*des, NetScope*scope) const { assert(expr_); return expr_->has_aa_term(des, scope); } PExpr* PEEvent::expr() const { return expr_; } PENull::PENull(void) { } PENull::~PENull() { } PEFNumber::PEFNumber(verireal*v) : value_(v) { } PEFNumber::~PEFNumber() { delete value_; } const verireal& PEFNumber::value() const { return *value_; } PEIdent::PEIdent(const pform_name_t&that) : package_(0), path_(that), no_implicit_sig_(false) { } PEIdent::PEIdent(perm_string s, bool no_implicit_sig) : package_(0), no_implicit_sig_(no_implicit_sig) { path_.push_back(name_component_t(s)); } PEIdent::PEIdent(PPackage*pkg, const pform_name_t&that) : package_(pkg), path_(that), no_implicit_sig_(true) { } PEIdent::~PEIdent() { } void PEIdent::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { /* We create an implicit wire if: - this is a simple identifier - an identifier of that name has not already been declared in any enclosing scope. - this is not an implicit named port connection */ if (no_implicit_sig_) return; if ((path_.size() == 1) && (path_.front().index.size() == 0)) { perm_string name = path_.front().name; LexicalScope*ss = scope; while (ss) { if (ss->wires.find(name) != ss->wires.end()) return; if (ss->localparams.find(name) != ss->localparams.end()) return; if (ss->parameters.find(name) != ss->parameters.end()) return; if (ss->genvars.find(name) != ss->genvars.end()) return; if (ss->events.find(name) != ss->events.end()) return; /* Strictly speaking, we should also check for name clashes with tasks, functions, named blocks, module instances, and generate blocks. However, this information is not readily available. As these names would not be legal in this context, we can declare implicit nets here and rely on later checks for name clashes to report the error. */ ss = ss->parent_scope(); } PWire*net = new PWire(name, type, NetNet::NOT_A_PORT, IVL_VT_LOGIC); net->set_file(get_file()); net->set_lineno(get_lineno()); net->set_range_scalar(SR_NET); scope->wires[name] = net; if (warn_implicit) { cerr << get_fileline() << ": warning: implicit " "definition of wire '" << name << "'." << endl; } } } bool PEIdent::has_aa_term(Design*des, NetScope*scope) const { NetNet* net = 0; const NetExpr*par = 0; NetEvent* eve = 0; const NetExpr*ex1, *ex2; scope = symbol_search(this, des, scope, path_, net, par, eve, ex1, ex2); if (scope) return scope->is_auto(); else return false; } PENewArray::PENewArray(PExpr*size_expr, PExpr*init_expr) : size_(size_expr), init_(init_expr) { } PENewArray::~PENewArray() { delete size_; } PENewClass::PENewClass(void) { } PENewClass::PENewClass(const list&p) : parms_(p.size()) { size_t tmp_idx = 0; for (list::const_iterator cur = p.begin() ; cur != p.end() ; ++ cur) { parms_[tmp_idx++] = *cur; } } PENewClass::~PENewClass() { } PENewCopy::PENewCopy(PExpr*src) : src_(src) { } PENewCopy::~PENewCopy() { } PENumber::PENumber(verinum*vp) : value_(vp) { assert(vp); } PENumber::~PENumber() { delete value_; } const verinum& PENumber::value() const { return *value_; } bool PENumber::is_the_same(const PExpr*that) const { const PENumber*obj = dynamic_cast(that); if (obj == 0) return false; return *value_ == *obj->value_; } PEString::PEString(char*s) : text_(s) { } PEString::~PEString() { delete[]text_; } string PEString::value() const { return text_; } PETernary::PETernary(PExpr*e, PExpr*t, PExpr*f) : expr_(e), tru_(t), fal_(f) { } PETernary::~PETernary() { } void PETernary::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { assert(expr_ && tru_ && fal_); expr_->declare_implicit_nets(scope, type); tru_->declare_implicit_nets(scope, type); fal_->declare_implicit_nets(scope, type); } bool PETernary::has_aa_term(Design*des, NetScope*scope) const { assert(expr_ && tru_ && fal_); return expr_->has_aa_term(des, scope) || tru_->has_aa_term(des, scope) || fal_->has_aa_term(des, scope); } PETypename::PETypename(data_type_t*dt) : data_type_(dt) { } PETypename::~PETypename() { } PEUnary::PEUnary(char op, PExpr*ex) : op_(op), expr_(ex) { } PEUnary::~PEUnary() { } void PEUnary::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { assert(expr_); expr_->declare_implicit_nets(scope, type); } bool PEUnary::has_aa_term(Design*des, NetScope*scope) const { assert(expr_); return expr_->has_aa_term(des, scope); } PEVoid::PEVoid() { } PEVoid::~PEVoid() { } iverilog-10_1/PExpr.h000066400000000000000000001037371265551621300145640ustar00rootroot00000000000000#ifndef IVL_PExpr_H #define IVL_PExpr_H /* * Copyright (c) 1998-2014 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "netlist.h" # include "verinum.h" # include "LineInfo.h" # include "pform_types.h" class Design; class Module; class LexicalScope; class NetNet; class NetExpr; class NetScope; class PPackage; /* * The PExpr class hierarchy supports the description of * expressions. The parser can generate expression objects from the * source, possibly reducing things that it knows how to reduce. */ class PExpr : public LineInfo { public: // Mode values used by test_width() (see below for description). enum width_mode_t { SIZED, UNSIZED, EXPAND, LOSSLESS, UPSIZE }; // Flag values that can be passed to elaborate_expr(). static const unsigned NO_FLAGS = 0x0; static const unsigned NEED_CONST = 0x1; static const unsigned SYS_TASK_ARG = 0x2; static const unsigned ANNOTATABLE = 0x4; // Convert width mode to human-readable form. static const char*width_mode_name(width_mode_t mode); PExpr(); virtual ~PExpr(); virtual void dump(ostream&) const; // This method tests whether the expression contains any identifiers // that have not been previously declared in the specified scope or // in any containing scope. Any such identifiers are added to the // specified scope as scalar nets of the specified type. // // This operation must be performed by the parser, to ensure that // subsequent declarations do not affect the decision to create an // implicit net. virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); // This method tests whether the expression contains any // references to automatically allocated variables. virtual bool has_aa_term(Design*des, NetScope*scope) const; // This method tests the type and width that the expression wants // to be. It should be called before elaborating an expression to // figure out the type and width of the expression. It also figures // out the minimum width that can be used to evaluate the expression // without changing the result. This allows the expression width to // be pruned when not all bits of the result are used. // // Normally mode should be initialized to SIZED before starting to // test the width of an expression. In SIZED mode the expression // width will be calculated strictly according to the IEEE standard // rules for expression width. // // If the expression is found to contain an unsized literal number // and gn_strict_expr_width_flag is set, mode will be changed to // UNSIZED. In UNSIZED mode the expression width will be calculated // exactly as in SIZED mode - the change in mode simply flags that // the expression contains an unsized numbers. // // If the expression is found to contain an unsized literal number // and gn_strict_expr_width_flag is not set, mode will be changed // to LOSSLESS. In LOSSLESS mode the expression width will be // calculated as the minimum width necessary to avoid arithmetic // overflow or underflow. // // Once in LOSSLESS mode, if the expression is found to contain // an operation that coerces a vector operand to a different type // (signed <-> unsigned), mode will be changed to UPSIZE. UPSIZE // mode is the same as LOSSLESS, except that the final expression // width will be forced to be at least integer_width. This is // necessary to ensure compatibility with the IEEE standard, which // requires unsized numbers to be treated as having the same width // as an integer. The lossless width calculation is inadequate in // this case because coercing an operand to a different type means // that the expression no longer obeys the normal rules of arithmetic. // // If mode is initialized to EXPAND instead of SIZED, the expression // width will be calculated as the minimum width necessary to avoid // arithmetic overflow or underflow, even if it contains no unsized // literals. mode will be changed LOSSLESS or UPSIZE as described // above. This supports a non-standard mode of expression width // calculation. // // When the final value of mode is UPSIZE, the width returned by // this method is the calculated lossless width, but the width // returned by a subsequent call to the expr_width method will be // the final expression width. virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); // After the test_width method is complete, these methods // return valid results. ivl_variable_type_t expr_type() const { return expr_type_; } unsigned expr_width() const { return expr_width_; } unsigned min_width() const { return min_width_; } bool has_sign() const { return signed_flag_; } // This method allows the expression type (signed/unsigned) // to be propagated down to any context-dependant operands. void cast_signed(bool flag) { signed_flag_ = flag; } // This is the more generic form of the elaborate_expr method // below. The plan is to replace the simpler elaborate_expr // method with this version, which can handle more advanced // types. But for now, this is only implemented in special cases. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; // Procedural elaboration of the expression. The expr_width is // the required width of the expression. // // The sys_task_arg flag is true if expressions are allowed to // be incomplete. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; // This is similar to elaborate_lnet, except that the // expression is evaluated to be bi-directional. This is // useful for arguments to inout ports of module instances and // ports of tran primitives. virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; // Expressions that can be in the l-value of procedural // assignments can be elaborated with this method. If the // is_cassign or is_force flags are true, then the set of // valid l-value types is slightly modified to accommodate // the Verilog procedural continuous assignment statements. virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_cassign, bool is_force) const; // This attempts to evaluate a constant expression, and return // a verinum as a result. If the expression cannot be // evaluated, return 0. virtual verinum* eval_const(Design*des, NetScope*sc) const; // This method returns true if the expression represents a // structural net that can have multiple drivers. This is // used to test whether an input port connection can be // collapsed to a single wire. virtual bool is_collapsible_net(Design*des, NetScope*scope) const; // This method returns true if that expression is the same as // this expression. This method is used for comparing // expressions that must be structurally "identical". virtual bool is_the_same(const PExpr*that) const; protected: unsigned fix_width_(width_mode_t mode); // The derived class test_width methods should fill these in. ivl_variable_type_t expr_type_; unsigned expr_width_; unsigned min_width_; bool signed_flag_; private: // not implemented PExpr(const PExpr&); PExpr& operator= (const PExpr&); }; ostream& operator << (ostream&, const PExpr&); class PEAssignPattern : public PExpr { public: explicit PEAssignPattern(); explicit PEAssignPattern(const std::list&p); ~PEAssignPattern(); void dump(std::ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; private: NetExpr* elaborate_expr_darray_(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; private: std::vectorparms_; }; class PEConcat : public PExpr { public: PEConcat(const list&p, PExpr*r =0); ~PEConcat(); virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual void dump(ostream&) const; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_cassign, bool is_force) const; virtual bool is_collapsible_net(Design*des, NetScope*scope) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, bool bidirectional_flag) const; private: vectorparms_; std::valarraywidth_modes_; PExpr*repeat_; NetScope*tested_scope_; unsigned repeat_count_; }; /* * Event expressions are expressions that can be combined with the * event "or" operator. These include "posedge foo" and similar, and * also include named events. "edge" events are associated with an * expression, whereas named events simply have a name, which * represents an event variable. */ class PEEvent : public PExpr { public: enum edge_t {ANYEDGE, POSEDGE, NEGEDGE, POSITIVE}; // Use this constructor to create events based on edges or levels. PEEvent(edge_t t, PExpr*e); ~PEEvent(); edge_t type() const; PExpr* expr() const; virtual void dump(ostream&) const; virtual bool has_aa_term(Design*des, NetScope*scope) const; private: edge_t type_; PExpr *expr_; }; /* * This holds a floating point constant in the source. */ class PEFNumber : public PExpr { public: explicit PEFNumber(verireal*vp); ~PEFNumber(); const verireal& value() const; /* The eval_const method as applied to a floating point number gets the *integer* value of the number. This accounts for any rounding that is needed to get the value. */ virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; virtual void dump(ostream&) const; private: verireal*value_; }; class PEIdent : public PExpr { public: explicit PEIdent(perm_string, bool no_implicit_sig=false); explicit PEIdent(PPackage*pkg, const pform_name_t&name); explicit PEIdent(const pform_name_t&); ~PEIdent(); // Add another name to the string of hierarchy that is the // current identifier. void append_name(perm_string); virtual void dump(ostream&) const; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); // Identifiers are allowed (with restrictions) is assign l-values. virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; // Identifiers are also allowed as procedural assignment l-values. virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_cassign, bool is_force) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. NetNet* elaborate_subport(Design*des, NetScope*sc) const; // Elaborate the identifier allowing for unpacked arrays. This // method only applies to Ident expressions because only Ident // expressions can can be unpacked arrays. NetNet* elaborate_unpacked_net(Design*des, NetScope*sc) const; verinum* eval_const(Design*des, NetScope*sc) const; virtual bool is_collapsible_net(Design*des, NetScope*scope) const; const pform_name_t& path() const { return path_; } private: PPackage*package_; pform_name_t path_; bool no_implicit_sig_; private: // Common functions to calculate parts of part/bit // selects. These methods return true if the expressions // elaborate/calculate, or false if there is some sort of // source error. bool calculate_bits_(Design*, NetScope*, long&msb, bool&defined) const; // The calculate_parts_ method calculates the range // expressions of a part select for the current object. The // part select expressions are elaborated and evaluated, and // the values written to the msb/lsb arguments. If there are // invalid bits (xz) in either expression, then the defined // flag is set to *false*. bool calculate_parts_(Design*, NetScope*, long&msb, long&lsb, bool&defined) const; NetExpr* calculate_up_do_base_(Design*, NetScope*, bool need_const) const; bool calculate_param_range_(Design*, NetScope*, const NetExpr*msb_ex, long&msb, const NetExpr*lsb_ex, long&lsb, long length) const; bool calculate_up_do_width_(Design*, NetScope*, unsigned long&wid) const; // Evaluate the prefix indices. All but the final index in a // chain of indices must be a single value and must evaluate // to constants at compile time. For example: // [x] - OK // [1][2][x] - OK // [1][x:y] - OK // [2:0][x] - BAD // [y][x] - BAD // Leave the last index for special handling. bool calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, std::list&prefix_indices) const; private: NetAssign_*elaborate_lval_method_class_member_(Design*, NetScope*) const; NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*, bool need_const_idx) const; bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*, bool need_const_idx) const; bool elaborate_lval_net_part_(Design*, NetScope*, NetAssign_*) const; bool elaborate_lval_net_idx_(Design*, NetScope*, NetAssign_*, index_component_t::ctype_t, bool need_const_idx) const; NetAssign_*elaborate_lval_net_class_member_(Design*, NetScope*, NetNet*, const perm_string&) const; bool elaborate_lval_net_packed_member_(Design*, NetScope*, NetAssign_*, const name_component_t&) const; bool elaborate_lval_darray_bit_(Design*, NetScope*, NetAssign_*) const; private: NetExpr*elaborate_expr_param_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, unsigned expr_wid, unsigned flags) const; NetExpr*elaborate_expr_param_bit_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, bool need_const) const; NetExpr*elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, unsigned expr_wid) const; NetExpr*elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, bool need_const) const; NetExpr*elaborate_expr_param_idx_do_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, bool need_const) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found, unsigned expr_wid, unsigned flags) const; NetExpr*elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found, unsigned expr_wid, unsigned flags) const; NetExpr*elaborate_expr_net_part_(Design*des, NetScope*scope, NetESignal*net, NetScope*found, unsigned expr_wid) const; NetExpr*elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, NetScope*found, bool need_const) const; NetExpr*elaborate_expr_net_idx_do_(Design*des, NetScope*scope, NetESignal*net, NetScope*found, bool need_const) const; NetExpr*elaborate_expr_net_bit_(Design*des, NetScope*scope, NetESignal*net, NetScope*found, bool need_const) const; NetExpr*elaborate_expr_net_bit_last_(Design*des, NetScope*scope, NetESignal*net, NetScope*found, bool need_const) const; NetExpr*elaborate_expr_class_member_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; unsigned test_width_method_(Design*des, NetScope*scope, width_mode_t&mode); NetExpr*elaborate_expr_method_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, bool bidirectional_flag) const; NetAssign_*scan_lname_for_nested_members_(Design*des, NetScope*scope, const pform_name_t&path) const; bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig, long&midx, long&lidx) const; }; class PENewArray : public PExpr { public: explicit PENewArray (PExpr*s, PExpr*i); ~PENewArray(); virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; private: PExpr*size_; PExpr*init_; }; class PENewClass : public PExpr { public: // New without (or with default) constructor explicit PENewClass (); // New with constructor arguments explicit PENewClass (const std::list&p); ~PENewClass(); virtual void dump(ostream&) const; // Class objects don't have a useful width, but the expression // is IVL_VT_CLASS. virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); // Note that class (new) expressions only appear in context // that uses this form of the elaborate_expr method. In fact, // the type argument is going to be a netclass_t object. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; private: NetExpr* elaborate_expr_constructor_(Design*des, NetScope*scope, const netclass_t*ctype, NetExpr*obj, unsigned flags) const; private: std::vectorparms_; }; class PENewCopy : public PExpr { public: explicit PENewCopy(PExpr*src); ~PENewCopy(); virtual void dump(ostream&) const; // Class objects don't have a useful width, but the expression // is IVL_VT_CLASS. virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); // Note that class (new) expressions only appear in context // that uses this form of the elaborate_expr method. In fact, // the type argument is going to be a netclass_t object. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; private: PExpr*src_; }; class PENull : public PExpr { public: explicit PENull(); ~PENull(); virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; }; class PENumber : public PExpr { public: explicit PENumber(verinum*vp); ~PENumber(); const verinum& value() const; virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr *elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetEConst*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_cassign, bool is_force) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual bool is_the_same(const PExpr*that) const; private: verinum*const value_; }; /* * This represents a string constant in an expression. * * The s parameter to the PEString constructor is a C string that this * class instance will take for its own. The caller should not delete * the string, the destructor will do it. */ class PEString : public PExpr { public: explicit PEString(char*s); ~PEString(); string value() const; virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetEConst*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned) const; verinum* eval_const(Design*, NetScope*) const; private: char*text_; }; class PETypename : public PExpr { public: explicit PETypename(data_type_t*data_type); ~PETypename(); virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; inline data_type_t* get_type() const { return data_type_; } private: data_type_t*data_type_; }; class PEUnary : public PExpr { public: explicit PEUnary(char op, PExpr*ex); ~PEUnary(); virtual void dump(ostream&out) const; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; public: inline char get_op() const { return op_; } inline PExpr*get_expr() const { return expr_; } private: NetExpr* elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const; private: char op_; PExpr*expr_; }; class PEBinary : public PExpr { public: explicit PEBinary(char op, PExpr*l, PExpr*r); ~PEBinary(); virtual void dump(ostream&out) const; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; protected: char op_; PExpr*left_; PExpr*right_; NetExpr*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; NetExpr*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; NetExpr*elaborate_expr_base_bits_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; NetExpr*elaborate_expr_base_div_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; NetExpr*elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; NetExpr*elaborate_expr_base_add_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; }; /* * Here are a few specialized classes for handling specific binary * operators. */ class PEBComp : public PEBinary { public: explicit PEBComp(char op, PExpr*l, PExpr*r); ~PEBComp(); virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; private: unsigned l_width_; unsigned r_width_; }; /* * This derived class is for handling logical expressions: && and ||. */ class PEBLogic : public PEBinary { public: explicit PEBLogic(char op, PExpr*l, PExpr*r); ~PEBLogic(); virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; }; /* * A couple of the binary operands have a special sub-expression rule * where the expression width is carried entirely by the left * expression, and the right operand is self-determined. */ class PEBLeftWidth : public PEBinary { public: explicit PEBLeftWidth(char op, PExpr*l, PExpr*r); ~PEBLeftWidth() =0; virtual NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const =0; protected: virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; }; class PEBPower : public PEBLeftWidth { public: explicit PEBPower(char op, PExpr*l, PExpr*r); ~PEBPower(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; }; class PEBShift : public PEBLeftWidth { public: explicit PEBShift(char op, PExpr*l, PExpr*r); ~PEBShift(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const; }; /* * This class supports the ternary (?:) operator. The operator takes * three expressions, the test, the true result and the false result. */ class PETernary : public PExpr { public: explicit PETernary(PExpr*e, PExpr*t, PExpr*f); ~PETernary(); virtual void dump(ostream&out) const; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope, PExpr*expr, unsigned expr_wid, unsigned flags, bool short_cct) const; private: PExpr*expr_; PExpr*tru_; PExpr*fal_; }; /* * This class represents a parsed call to a function, including calls * to system functions. The parameters in the parms list are the * expressions that are passed as input to the ports of the function. */ class PECallFunction : public PExpr { public: explicit PECallFunction(const pform_name_t&n, const vector &parms); // Call function defined in package. explicit PECallFunction(PPackage*pkg, perm_string n, const std::vector &parms); explicit PECallFunction(PPackage*pkg, perm_string n, const std::list &parms); // Used to convert a user function called as a task explicit PECallFunction(PPackage*pkg, const pform_name_t&n, const std::vector &parms); // Call of system function (name is not hierarchical) explicit PECallFunction(perm_string n, const vector &parms); explicit PECallFunction(perm_string n); // std::list versions. Should be removed! explicit PECallFunction(const pform_name_t&n, const list &parms); explicit PECallFunction(perm_string n, const list &parms); ~PECallFunction(); virtual void dump(ostream &) const; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type); virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); private: PPackage*package_; pform_name_t path_; std::vector parms_; bool check_call_matches_definition_(Design*des, NetScope*dscope) const; NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; NetExpr*elaborate_expr_pkg_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags)const; NetExpr*elaborate_expr_method_(Design*des, NetScope*scope, unsigned expr_wid, bool add_this_flag = false) const; #if 0 NetExpr*elaborate_expr_string_method_(Design*des, NetScope*scope) const; NetExpr*elaborate_expr_enum_method_(Design*des, NetScope*scope, unsigned expr_wid) const; #endif NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t, unsigned expr_wid) const; unsigned test_width_sfunc_(Design*des, NetScope*scope, width_mode_t&mode); unsigned test_width_method_(Design*des, NetScope*scope, width_mode_t&mode); NetExpr*elaborate_base_(Design*des, NetScope*scope, NetScope*dscope, unsigned expr_wid, unsigned flags) const; unsigned elaborate_arguments_(Design*des, NetScope*scope, NetFuncDef*def, bool need_const, std::vector&parms, unsigned parm_off) const; }; /* * Support the SystemVerilog cast to size. */ class PECastSize : public PExpr { public: explicit PECastSize(unsigned expr_wid, PExpr*base); ~PECastSize(); void dump(ostream &out) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); private: unsigned size_; PExpr* base_; }; /* * Support the SystemVerilog cast to a different type. */ class PECastType : public PExpr { public: explicit PECastType(data_type_t*target, PExpr*base); ~PECastType(); void dump(ostream &out) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); private: data_type_t* target_; PExpr* base_; }; /* * This class is used for error recovery. All methods do nothing and return * null or default values. */ class PEVoid : public PExpr { public: explicit PEVoid(); ~PEVoid(); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; }; #endif /* IVL_PExpr_H */ iverilog-10_1/PFunction.cc000066400000000000000000000044741265551621300155670ustar00rootroot00000000000000/* * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PTask.h" # include "Statement.h" # include # include "ivl_assert.h" PFunction::PFunction(perm_string name, LexicalScope*parent, bool is_auto__) : PTaskFunc(name, parent), statement_(0) { is_auto_ = is_auto__; return_type_ = 0; } PFunction::~PFunction() { } void PFunction::set_statement(Statement*s) { assert(s != 0); assert(statement_ == 0); statement_ = s; } void PFunction::push_statement_front(Statement*stmt) { // This can only happen after the statement is initially set. ivl_assert(*this, statement_); // Get the PBlock of the statement. If it is not a PBlock, // then create one to wrap the existing statement and the new // statement that we're pushing. PBlock*blk = dynamic_cast (statement_); if (blk == 0) { PBlock*tmp = new PBlock(PBlock::BL_SEQ); tmp->set_line(*this); vectortmp_list(1); tmp_list[0] = statement_; tmp->set_statement(tmp_list); statement_ = tmp; blk = tmp; } // Now do the push. blk->push_statement_front(stmt); } void PFunction::set_return(data_type_t*t) { return_type_ = t; } PChainConstructor* PFunction::extract_chain_constructor() { PChainConstructor*res = 0; if ((res = dynamic_cast (statement_))) { statement_ = 0; } else if (PBlock*blk = dynamic_cast(statement_)) { res = blk->extract_chain_constructor(); } return res; } iverilog-10_1/PGate.cc000066400000000000000000000137221265551621300146560ustar00rootroot00000000000000/* * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PGate.h" # include "PExpr.h" # include "verinum.h" # include void PGate::set_pins_(list*pins) { assert(pins); assert(pins->size() == pins_.size()); for (size_t idx = 0 ; idx < pins_.size() ; idx += 1) { pins_[idx] = pins->front(); pins->pop_front(); } assert(pins->empty()); delete pins; } PGate::PGate(perm_string name, list*pins, const list*del) : name_(name), pins_(pins? pins->size() : 0) { if (pins) set_pins_(pins); if (del) delay_.set_delays(del); str0_ = IVL_DR_STRONG; str1_ = IVL_DR_STRONG; } PGate::PGate(perm_string name, list*pins, PExpr*del) : name_(name), pins_(pins? pins->size() : 0) { if (pins) set_pins_(pins); if (del) delay_.set_delay(del); str0_ = IVL_DR_STRONG; str1_ = IVL_DR_STRONG; } PGate::PGate(perm_string name, list*pins) : name_(name), pins_(pins? pins->size() : 0) { if (pins) set_pins_(pins); str0_ = IVL_DR_STRONG; str1_ = IVL_DR_STRONG; } PGate::~PGate() { } ivl_drive_t PGate::strength0() const { return str0_; } void PGate::strength0(ivl_drive_t s) { str0_ = s; } ivl_drive_t PGate::strength1() const { return str1_; } void PGate::strength1(ivl_drive_t s) { str1_ = s; } void PGate::elaborate_scope(Design*, NetScope*) const { } /* * This method is used during elaboration to calculate the * rise/fall/decay times for the gate. These values were set in pform * by the constructor, so here I evaluate the expression in the given * design context and save the calculated delays into the output * parameters. This method understands how to handle the different * numbers of expressions. */ void PGate::eval_delays(Design*des, NetScope*scope, NetExpr*&rise_expr, NetExpr*&fall_expr, NetExpr*&decay_expr, bool as_net_flag) const { delay_.eval_delays(des, scope, rise_expr, fall_expr, decay_expr, as_net_flag); } unsigned PGate::delay_count() const { return delay_.delay_count(); } PGAssign::PGAssign(list*pins) : PGate(perm_string(), pins) { assert(pin_count() == 2); } PGAssign::PGAssign(list*pins, list*dels) : PGate(perm_string(), pins, dels) { assert(pin_count() == 2); } PGAssign::~PGAssign() { } PGBuiltin::PGBuiltin(Type t, perm_string name, list*pins, list*del) : PGate(name, pins, del), type_(t), msb_(0), lsb_(0) { } PGBuiltin::PGBuiltin(Type t, perm_string name, list*pins, PExpr*del) : PGate(name, pins, del), type_(t), msb_(0), lsb_(0) { } PGBuiltin::~PGBuiltin() { } void PGBuiltin::set_range(PExpr*msb, PExpr*lsb) { assert(msb_ == 0); assert(lsb_ == 0); msb_ = msb; lsb_ = lsb; } const char* PGBuiltin::gate_name() const { switch(type_) { case AND: return "AND"; break; case NAND: return "NAND"; break; case OR: return "OR"; break; case NOR: return "NOR"; break; case XOR: return "XOR"; break; case XNOR: return "XNOR"; break; case BUF: return "BUF"; break; case NOT: return "NOT"; break; case BUFIF0: return "BUFIF0"; break; case NOTIF0: return "NOTIF0"; break; case BUFIF1: return "BUFIF1"; break; case NOTIF1: return "NOTIF1"; break; case NMOS: return "NMOS"; break; case RNMOS: return "RNMOS"; break; case PMOS: return "PMOS"; break; case RPMOS: return "RPMOS"; break; case TRAN: return "TRAN"; break; case RTRAN: return "RTRAN"; break; case TRANIF0: return "TRANIF0"; break; case RTRANIF0: return "RTRANIF0"; break; case TRANIF1: return "TRANIF1"; break; case RTRANIF1: return "RTRANIF1"; break; case CMOS: return "CMOS"; break; case RCMOS: return "RCMOS"; break; case PULLUP: return "PULLUP"; break; case PULLDOWN: return "PULLDOWN"; break; } return ""; } PGModule::PGModule(perm_string type, perm_string name, list*pins) : PGate(name, pins), bound_type_(0), type_(type), overrides_(0), pins_(0), npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0) { } PGModule::PGModule(perm_string type, perm_string name, named*pins, unsigned npins) : PGate(name, 0), bound_type_(0), type_(type), overrides_(0), pins_(pins), npins_(npins), parms_(0), nparms_(0), msb_(0), lsb_(0) { } PGModule::PGModule(Module*type, perm_string name) : PGate(name, 0), bound_type_(type), overrides_(0), pins_(0), npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0) { } PGModule::~PGModule() { } void PGModule::set_parameters(list*o) { assert(overrides_ == 0); overrides_ = o; } void PGModule::set_parameters(named*pa, unsigned npa) { assert(parms_ == 0); assert(overrides_ == 0); parms_ = pa; nparms_ = npa; } void PGModule::set_range(PExpr*msb, PExpr*lsb) { assert(msb_ == 0); assert(lsb_ == 0); msb_ = msb; lsb_ = lsb; } perm_string PGModule::get_type() const { return type_; } iverilog-10_1/PGate.h000066400000000000000000000211511265551621300145130ustar00rootroot00000000000000#ifndef IVL_PGate_H #define IVL_PGate_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "svector.h" # include "StringHeap.h" # include "named.h" # include "LineInfo.h" # include "PDelays.h" # include "netlist.h" # include # include # include # include class PExpr; class PUdp; class Module; /* * A PGate represents a Verilog gate. The gate has a name and other * properties, and a set of pins that connect to wires. It is known at * the time a gate is constructed how many pins the gate has. * * This pins of a gate are connected to expressions. The elaboration * step will need to convert expressions to a network of gates in * order to elaborate expression inputs, but that can easily be done. * * The PGate base class also carries the strength0 and strength1 * strengths for those gates where the driver[s] can be described by a * single strength pair. There is a strength of the 0 drive, and a * strength of the 1 drive. */ class PGate : public LineInfo { public: explicit PGate(perm_string name, list*pins, const list*del); explicit PGate(perm_string name, list*pins, PExpr*del); explicit PGate(perm_string name, list*pins); virtual ~PGate(); perm_string get_name() const { return name_; } // This evaluates the delays as far as possible, but returns // an expression, and do not signal errors. void eval_delays(Design*des, NetScope*scope, NetExpr*&rise_time, NetExpr*&fall_time, NetExpr*&decay_time, bool as_net_flag =false) const; unsigned delay_count() const; unsigned pin_count() const { return pins_.size(); } PExpr*pin(unsigned idx) const { return pins_[idx]; } ivl_drive_t strength0() const; ivl_drive_t strength1() const; void strength0(ivl_drive_t); void strength1(ivl_drive_t); map attributes; virtual void dump(ostream&out, unsigned ind =4) const; virtual void elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*sc) const; virtual bool elaborate_sig(Design*des, NetScope*scope) const; protected: const vector& get_pins() const { return pins_; } void dump_pins(ostream&out) const; void dump_delays(ostream&out) const; private: perm_string name_; PDelays delay_; vectorpins_; ivl_drive_t str0_, str1_; void set_pins_(list*pins); private: // not implemented PGate(const PGate&); PGate& operator= (const PGate&); }; /* A continuous assignment has a single output and a single input. The input is passed directly to the output. This is different from a BUF because elaboration may need to turn this into a vector of gates. */ class PGAssign : public PGate { public: explicit PGAssign(list*pins); explicit PGAssign(list*pins, list*dels); ~PGAssign(); void dump(ostream&out, unsigned ind =4) const; virtual void elaborate(Design*des, NetScope*scope) const; virtual bool elaborate_sig(Design*des, NetScope*scope) const; private: void elaborate_unpacked_array_(Design*des, NetScope*scope, NetNet*lval) const; }; /* * The Builtin class is specifically a gate with one of the builtin * types. The parser recognizes these types during parse. These types * have special properties that allow them to be treated specially. * * A PGBuiltin can be grouped into an array of devices. If this is * done, the msb_ and lsb_ are set to the indices of the array * range. Elaboration causes a gate to be created for each element of * the array, and a name will be generated for each gate. */ class PGBuiltin : public PGate { public: enum Type { AND, NAND, OR, NOR, XOR, XNOR, BUF, BUFIF0, BUFIF1, NOT, NOTIF0, NOTIF1, PULLDOWN, PULLUP, NMOS, RNMOS, PMOS, RPMOS, CMOS, RCMOS, TRAN, RTRAN, TRANIF0, TRANIF1, RTRANIF0, RTRANIF1 }; public: explicit PGBuiltin(Type t, perm_string name, list*pins, list*del); explicit PGBuiltin(Type t, perm_string name, list*pins, PExpr*del); ~PGBuiltin(); Type type() const { return type_; } const char * gate_name() const; void set_range(PExpr*msb, PExpr*lsb); virtual void dump(ostream&out, unsigned ind =4) const; virtual void elaborate(Design*, NetScope*scope) const; virtual bool elaborate_sig(Design*des, NetScope*scope) const; private: unsigned calculate_array_count_(Design*, NetScope*, long&high, long&low) const; void calculate_gate_and_lval_count_(unsigned&gate_count, unsigned&lval_count) const; NetNode* create_gate_for_output_(Design*, NetScope*, perm_string gate_name, unsigned instance_width) const; bool check_delay_count(Design*des) const; Type type_; PExpr*msb_; PExpr*lsb_; }; /* * This kind of gate is an instantiation of a module. The stored type * is the name of a module definition somewhere in the pform. This * type also handles UDP devices, because it is generally not known at * parse time whether a name belongs to a module or a UDP. */ class PGModule : public PGate { public: // The name is the *instance* name of the gate. // If the binding of ports is by position, this constructor // builds everything all at once. explicit PGModule(perm_string type, perm_string name, list*pins); // If the binding of ports is by name, this constructor takes // the bindings and stores them for later elaboration. explicit PGModule(perm_string type, perm_string name, named*pins, unsigned npins); // If the module type is known by design, then use this // constructor. explicit PGModule(Module*type, perm_string name); ~PGModule(); // Parameter overrides can come as an ordered list, or a set // of named expressions. void set_parameters(list*o); void set_parameters(named*pa, unsigned npa); // Modules can be instantiated in ranges. The parser uses this // method to pass the range to the pform. void set_range(PExpr*msb, PExpr*lsb); virtual void dump(ostream&out, unsigned ind =4) const; virtual void elaborate(Design*, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*sc) const; virtual bool elaborate_sig(Design*des, NetScope*scope) const; // This returns the module name of this module. It is a // permallocated string. perm_string get_type() const; private: Module*bound_type_; perm_string type_; list*overrides_; named*pins_; unsigned npins_; // These members support parameter override by name named*parms_; unsigned nparms_; // Arrays of modules are give if these are set. PExpr*msb_; PExpr*lsb_; friend class delayed_elaborate_scope_mod_instances; void elaborate_mod_(Design*, Module*mod, NetScope*scope) const; void elaborate_udp_(Design*, PUdp *udp, NetScope*scope) const; unsigned calculate_instance_count_(Design*, NetScope*, long&high, long&low, perm_string name) const; void elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const; void elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const; bool elaborate_sig_mod_(Design*des, NetScope*scope, Module*mod) const; // Not currently used. #if 0 bool elaborate_sig_udp_(Design*des, NetScope*scope, PUdp*udp) const; #endif NetNet*resize_net_to_port_(Design*des, NetScope*scope, NetNet*sig, unsigned port_wid, NetNet::PortType dir, bool as_signed) const; }; #endif /* IVL_PGate_H */ iverilog-10_1/PGenerate.cc000066400000000000000000000061071265551621300155270ustar00rootroot00000000000000/* * Copyright (c) 2006-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PGenerate.h" # include "PWire.h" # include "ivl_assert.h" PGenerate::PGenerate(LexicalScope*parent, unsigned id) : LexicalScope(parent), id_number(id) { direct_nested_ = false; scheme_type = GS_NONE; loop_init = 0; loop_test = 0; loop_step = 0; } PGenerate::~PGenerate() { } void PGenerate::add_gate(PGate*gate) { gates.push_back(gate); } void PGenerate::probe_for_direct_nesting_(void) { direct_nested_ = false; ivl_assert(*this, scheme_type==GS_CASE_ITEM || scheme_type==GS_CONDIT || scheme_type==GS_ELSE); // If this scheme has received an explicit name, then it // cannot be direct nested. if (scope_name[0] != '$') return; if (! tasks.empty()) return; if (! funcs.empty()) return; if (! gates.empty()) return; if (! parameters.empty()) return; if (! localparams.empty()) return; if (! events.empty()) return; if (! wires.empty()) return; if (! genvars.empty()) return; if (! behaviors.empty()) return; if (! analog_behaviors.empty()) return; if (generate_schemes.empty()) return; switch (generate_schemes.size()) { case 1: { PGenerate*child = generate_schemes.front(); if (child->scheme_type == GS_CONDIT) direct_nested_ = true; if (child->scheme_type == GS_CASE) direct_nested_ = true; break; } case 2: { PGenerate*child1 = generate_schemes.front(); PGenerate*child2 = generate_schemes.back(); if (child1->scheme_type==GS_CONDIT && child2->scheme_type==GS_ELSE) direct_nested_ = true; if (child2->scheme_type==GS_CONDIT && child1->scheme_type==GS_ELSE) direct_nested_ = true; break; } } } ostream& operator << (ostream&out, PGenerate::scheme_t type) { switch (type) { case PGenerate::GS_NONE: out << "GS_NONE"; break; case PGenerate::GS_LOOP: out << "GS_LOOP"; break; case PGenerate::GS_CONDIT: out << "GS_CONDIT"; break; case PGenerate::GS_ELSE: out << "GS_ELSE"; break; case PGenerate::GS_CASE: out << "GS_CASE"; break; case PGenerate::GS_CASE_ITEM: out << "GS_CASE_ITEM"; break; case PGenerate::GS_NBLOCK: out << "GS_NBLOCK"; break; } return out; } iverilog-10_1/PGenerate.h000066400000000000000000000114121265551621300153640ustar00rootroot00000000000000#ifndef IVL_PGenerate_H #define IVL_PGenerate_H /* * Copyright (c) 2006-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "StringHeap.h" # include "HName.h" # include "PScope.h" # include # include # include # include "pform_types.h" class Design; class NetScope; class PExpr; class PFunction; class PProcess; class PTask; class PGate; class PWire; /* * This represents a generate scheme. The interpretation of the * members depends on the scheme_type. * * GS_LOOP * * GS_CASE * loop_test is the expression to be compared. * generates contains only GS_CASE_ITEM schemes. * GS_CASE_ITEM * The parent points to the GS_CASE that contains this item. * the loop_test is compared with the parent->loop_test expression. */ class PGenerate : public LineInfo, public LexicalScope { public: explicit PGenerate(LexicalScope*parent, unsigned id_number); ~PGenerate(); // Generate schemes have an ID number, for when the scope is // implicit. const unsigned id_number; perm_string scope_name; // This is used during parsing to stack lexical scopes within // this generate scheme. // LexicalScope*lexical_scope; enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE, GS_CASE, GS_CASE_ITEM, GS_NBLOCK}; scheme_t scheme_type; // generate loops have an index variable and three // expressions: for (index = ; ; index=) perm_string loop_index; PExpr*loop_init; PExpr*loop_test; PExpr*loop_step; // Case items may have multiple guard expression values. It is // enough for any on of the guards to match the case statement // test value. std::valarray item_test; // defparam assignments found in this scope. typedef pair named_expr_t; listdefparms; list gates; void add_gate(PGate*); // Tasks instantiated within this scheme. map tasks; mapfuncs; // Generate schemes can contain further generate schemes. list generate_schemes; // PGenerate*parent; // This method is called by the elaboration of a module to // generate scopes. the container is the scope that is to // contain the generated scope. bool generate_scope(Design*des, NetScope*container); // Elaborate signals within any of the generated scopes that // were made by this generate block within the given container scope. bool elaborate_sig(Design*des, NetScope*container) const; bool elaborate(Design*des, NetScope*container) const; void dump(ostream&out, unsigned indent) const; private: bool generate_scope_loop_(Design*des, NetScope*container); bool generate_scope_condit_(Design*des, NetScope*container, bool else_flag); bool generate_scope_case_(Design*des, NetScope*container); bool generate_scope_nblock_(Design*des, NetScope*container); // Call probe during elaborate_scope to calculate the // direct_nested_ flag. It is OK to store the direct_nested_ // information here because "direct nested" is a property of // the lexical generate code. void probe_for_direct_nesting_(void); bool direct_nested_; // Elaborate_scope within a generated scope. void elaborate_subscope_(Design*des, NetScope*scope); void elaborate_subscope_direct_(Design*des, NetScope*scope); // These are the scopes created by generate_scope. listscope_list_; // internal function called on each scope generated by this scheme. bool elaborate_sig_(Design*des, NetScope*scope) const; bool elaborate_sig_direct_(Design*des, NetScope*scope) const; bool elaborate_(Design*des, NetScope*scope) const; bool elaborate_direct_(Design*des, NetScope*scope) const; private: // not implemented PGenerate(const PGenerate&); PGenerate& operator= (const PGenerate&); }; extern std::ostream& operator << (std::ostream&, PGenerate::scheme_t); #endif /* IVL_PGenerate_H */ iverilog-10_1/PModport.cc000066400000000000000000000017151265551621300154210ustar00rootroot00000000000000/* * Copyright (c) 2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PModport.h" PModport::PModport(perm_string n) : name_(n) { } PModport::~PModport() { } iverilog-10_1/PModport.h000066400000000000000000000031551265551621300152630ustar00rootroot00000000000000#ifndef IVL_PModport_H #define IVL_PModport_H /* * Copyright (c) 2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "PScope.h" # include "StringHeap.h" # include "netlist.h" # include /* * The PModport class represents a parsed SystemVerilog modport list. */ class PModport : public LineInfo { public: // The name is a perm-allocated string. It is the simple name // of the modport, without any scope. explicit PModport(perm_string name); ~PModport(); perm_string name() const { return name_; } typedef pair simple_port_t; map simple_ports; private: perm_string name_; private: // not implemented PModport(const PModport&); PModport& operator= (const PModport&); }; #endif /* IVL_PModport_H */ iverilog-10_1/PPackage.cc000066400000000000000000000020351265551621300153240ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PPackage.h" PPackage::PPackage(perm_string name, LexicalScope*parent) : PScopeExtra(name, parent) { } PPackage::~PPackage() { } iverilog-10_1/PPackage.h000066400000000000000000000031171265551621300151700ustar00rootroot00000000000000#ifndef IVL_PPackage_H #define IVL_PPackage_H /* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PScope.h" # include "LineInfo.h" # include "StringHeap.h" # include /* * SystemVerilog supports class declarations with their own lexical * scope, etc. The parser arranges for these to be created and * collected. */ class PPackage : public PScopeExtra, public LineInfo { public: explicit PPackage (perm_string name, LexicalScope*parent); ~PPackage(); bool elaborate_scope(Design*des, NetScope*scope); bool elaborate_sig(Design*des, NetScope*scope) const; bool elaborate(Design*des, NetScope*scope) const; void pform_dump(std::ostream&out) const; }; #endif /* IVL_PPackage_H */ iverilog-10_1/PScope.cc000066400000000000000000000030441265551621300150430ustar00rootroot00000000000000/* * Copyright (c) 2008,2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PScope.h" PScope::PScope(perm_string n, LexicalScope*parent) : LexicalScope(parent), name_(n) { } PScope::PScope(perm_string n) : LexicalScope(0), name_(n) { } PScope::~PScope() { for(map::iterator it = typedefs.begin(); it != typedefs.end(); ++it) delete it->second; } PWire* LexicalScope::wires_find(perm_string name) { map::const_iterator cur = wires.find(name); if (cur == wires.end()) return 0; else return (*cur).second; } PScopeExtra::PScopeExtra(perm_string n, LexicalScope*parent) : PScope(n, parent) { } PScopeExtra::PScopeExtra(perm_string n) : PScope(n) { } PScopeExtra::~PScopeExtra() { } iverilog-10_1/PScope.h000066400000000000000000000134631265551621300147130ustar00rootroot00000000000000#ifndef IVL_PScope_H #define IVL_PScope_H /* * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "StringHeap.h" # include "pform_types.h" # include "ivl_target.h" # include # include # include class PEvent; class PExpr; class PFunction; class PPackage; class AProcess; class PProcess; class PClass; class PTask; class PWire; class Design; class NetScope; /* * The PScope class is a base representation of an object that * represents lexical scope. For example, a module, a function/task, a * named block is derived from a PScope. * * NOTE: This is not the same concept as the "scope" of an elaborated * hierarchy. That is represented by NetScope objects after elaboration. */ class LexicalScope { public: explicit LexicalScope(LexicalScope*parent) : parent_(parent) { } // A virtual destructor is so that dynamic_cast can work. virtual ~LexicalScope() { } struct range_t { // True if this is an exclude bool exclude_flag; // lower bound // If low_open_flag is false and low_expr=0, then use -inf bool low_open_flag; PExpr*low_expr; // upper bound // If high_open_flag is false and high_expr=0, then use +inf bool high_open_flag; PExpr*high_expr; // Next range description in list struct range_t*next; }; /* The scope has parameters that are evaluated when the scope is elaborated. During parsing, I put the parameters into this map. */ struct param_expr_t : public LineInfo { param_expr_t() : type(IVL_VT_NO_TYPE), msb(0), lsb(0), signed_flag(false), expr(0), range(0) { } // Type information ivl_variable_type_t type; PExpr*msb; PExpr*lsb; bool signed_flag; // Value expression PExpr*expr; // If there are range constraints, list them here range_t*range; }; mapparameters; maplocalparams; // Defined types in the scope. maptypedefs; // Named events in the scope. mapevents; // Symbols that are imported. Bind the imported name to the // package from which the name is imported. std::mapimports; // Nets and variables (wires) in the scope mapwires; PWire* wires_find(perm_string name); // Genvars in the scope. These will only be present in module // scopes, but are listed here to allow them to be found when // creating implicit nets. map genvars; // Behaviors (processes) in this scope list behaviors; list analog_behaviors; // Enumeration sets. std::set enum_sets; LexicalScope* parent_scope() const { return parent_; } protected: void dump_typedefs_(ostream&out, unsigned indent) const; void dump_parameters_(ostream&out, unsigned indent) const; void dump_localparams_(ostream&out, unsigned indent) const; void dump_enumerations_(ostream&out, unsigned indent) const; void dump_events_(ostream&out, unsigned indent) const; void dump_wires_(ostream&out, unsigned indent) const; private: LexicalScope*parent_; }; class PScope : public LexicalScope { public: // When created, a scope has a name and a parent. The name is // the name of the definition. For example, if this is a // module declaration, the name is the name after the "module" // keyword, and if this is a task scope, the name is the task // name. The parent is the lexical parent of this scope. Since // modules do not nest in Verilog, the parent must be nil for // modules. Scopes for tasks and functions point to their // containing module. PScope(perm_string name, LexicalScope*parent); PScope(perm_string name); virtual ~PScope(); perm_string pscope_name() const { return name_; } protected: bool elaborate_sig_wires_(Design*des, NetScope*scope) const; bool elaborate_behaviors_(Design*des, NetScope*scope) const; private: perm_string name_; }; /* * Some scopes can carry definitions. These include Modules and PClass * scopes. These derive from PScopeExtra so that they hold the maps of * extra definitions. */ class PScopeExtra : public PScope { public: PScopeExtra(perm_string, LexicalScope*parent); PScopeExtra(perm_string); ~PScopeExtra(); /* Task definitions within this module */ std::map tasks; std::map funcs; /* class definitions within this module. */ std::map classes; /* This is the lexical order of the classes, and is used by elaboration to choose an elaboration order. */ std::vector classes_lexical; protected: void dump_classes_(ostream&out, unsigned indent) const; void dump_tasks_(ostream&out, unsigned indent) const; void dump_funcs_(ostream&out, unsigned indent) const; }; #endif /* IVL_PScope_H */ iverilog-10_1/PSpec.cc000066400000000000000000000022341265551621300146640ustar00rootroot00000000000000/* * Copyright (c) 2006-2011 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PSpec.h" PSpecPath::PSpecPath(unsigned src_cnt, unsigned dst_cnt, char polarity, bool full_flag) : conditional(false), condition(0), edge(0), src(src_cnt), dst(dst_cnt), data_source_expression(0) { full_flag_ = full_flag; polarity_ = polarity; } PSpecPath::~PSpecPath() { } iverilog-10_1/PSpec.h000066400000000000000000000055741265551621300145400ustar00rootroot00000000000000#ifndef IVL_PSpec_H #define IVL_PSpec_H /* * Copyright (c) 2006-2014 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "StringHeap.h" # include class PExpr; /* * The PSpecPath is the parse of a specify path, which is in its most * general form = . The are collected into the * "delays" vector in all cases, and the variety is in the other * members. * * All paths also have a list of source names in the src vector, and a * list of destination names in the dst vector. These pairs are the * actual paths. * * If the path is a simple path, then: * condition == nil * edge == 0 * data_source_expression == nil * * If the path is conditional, then conditional == true and condition * is the condition expression. If the condition expression is nil, * then this is an ifnone conditional path. * * If data_source_expression != nil, then the path is edge sensitive * and the edge might not be 0. * * The full flag is used to verify that only vectors of the same size * are used in a parallel connection. Icarus always creates a full * connection between the source and destination. The polarity is for * informational (display) purposes only. The polarity is either '+', * '-' or 0. */ class PSpecPath : public LineInfo { public: PSpecPath(unsigned src_cnt, unsigned dst_cnt, char polarity, bool full_flag); ~PSpecPath(); void elaborate(class Design*des, class NetScope*scope) const; void dump(std::ostream&out, unsigned ind) const; public: // Condition expression, if present. bool conditional; class PExpr* condition; // Edge specification (-1==negedge, 0 = no edge, 1==posedge) int edge; // Is this a full connection. bool full_flag_; // What is the polarity of the connection. char polarity_; // Ordered set of source nodes of a path std::vector src; // Ordered set of destination nodes of a path std::vector dst; // Data source expression class PExpr* data_source_expression; std::vectordelays; }; #endif /* IVL_PSpec_H */ iverilog-10_1/PTask.cc000066400000000000000000000035301265551621300146740ustar00rootroot00000000000000/* * Copyright (c) 1999-2008,2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PTask.h" # include PTaskFunc::PTaskFunc(perm_string n, LexicalScope*p) : PScope(n,p), this_type_(0), ports_(0) { } PTaskFunc::~PTaskFunc() { } void PTaskFunc::set_ports(vector*p) { assert(ports_ == 0); ports_ = p; } void PTaskFunc::set_this(class_type_t*type, PWire*this_wire) { assert(this_type_ == 0); this_type_ = type; // Push a synthesis argument that is the "this" value. if (ports_==0) ports_ = new vector; size_t use_size = ports_->size(); ports_->resize(use_size + 1); for (size_t idx = use_size ; idx > 0 ; idx -= 1) ports_->at(idx) = ports_->at(idx-1); ports_->at(0) = pform_tf_port_t(this_wire); } PTask::PTask(perm_string name, LexicalScope*parent, bool is_auto__) : PTaskFunc(name, parent), statement_(0) { is_auto_ = is_auto__; } PTask::~PTask() { } void PTask::set_statement(Statement*s) { assert(statement_ == 0); statement_ = s; } iverilog-10_1/PTask.h000066400000000000000000000110571265551621300145410ustar00rootroot00000000000000#ifndef IVL_PTask_H #define IVL_PTask_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "PScope.h" # include "StringHeap.h" # include # include # include class Design; class NetExpr; class NetNet; class NetScope; class PChainConstructor; class PWire; class Statement; class PExpr; class PTaskFunc : public PScope, public LineInfo { public: PTaskFunc(perm_string name, LexicalScope*parent); ~PTaskFunc(); void set_ports(std::vector*p); void set_this(class_type_t*use_type, PWire*this_wire); // If this task is a method of a class, this returns a pointer // to the class type. inline class_type_t* method_of() const { return this_type_; } virtual void elaborate_sig(Design*des, NetScope*scope) const =0; virtual void elaborate(Design*des, NetScope*scope) const =0; virtual void dump(std::ostream&, unsigned) const =0; protected: // Elaborate the ports list. Write into the ports vector the // NetNet pointers for the ports, and write into the pdefs the // default value expressions, if any. void elaborate_sig_ports_(Design*des, NetScope*scope, std::vector&ports, std::vector&pdefs) const; void dump_ports_(std::ostream&out, unsigned ind) const; private: class_type_t*this_type_; std::vector*ports_; }; /* * The PTask holds the parsed definitions of a task. */ class PTask : public PTaskFunc { public: explicit PTask(perm_string name, LexicalScope*parent, bool is_auto); ~PTask(); void set_statement(Statement *s); // Tasks introduce scope, to need to be handled during the // scope elaboration pass. The scope passed is my scope, // created by the containing scope. I fill it in with stuff if // I need to. void elaborate_scope(Design*des, NetScope*scope) const; // Bind the ports to the regs that are the ports. void elaborate_sig(Design*des, NetScope*scope) const; // Elaborate the statement to finish off the task definition. void elaborate(Design*des, NetScope*scope) const; bool is_auto() const { return is_auto_; }; void dump(ostream&, unsigned) const; private: Statement*statement_; bool is_auto_; private: // Not implemented PTask(const PTask&); PTask& operator=(const PTask&); }; /* * The function is similar to a task (in this context) but there is a * single output port and a set of input ports. The output port is the * function return value. * * The output value is not elaborated until elaborate_sig. */ class PFunction : public PTaskFunc { public: explicit PFunction(perm_string name, LexicalScope*parent, bool is_auto); ~PFunction(); void set_statement(Statement *s); void set_return(data_type_t*t); inline Statement* get_statement() { return statement_; } // Push this statement to the front of the existing // definition. If the statement is a simple statement, make a // block to contain the statements. void push_statement_front(Statement*stmt); // This is only used if this function is a constructor. In // that case, this method looks for a PChainConstructor in the // statement and extracts it if found. PChainConstructor*extract_chain_constructor(); void elaborate_scope(Design*des, NetScope*scope) const; /* elaborate the ports and return value. */ void elaborate_sig(Design *des, NetScope*) const; /* Elaborate the behavioral statement. */ void elaborate(Design *des, NetScope*) const; bool is_auto() const { return is_auto_; }; void dump(ostream&, unsigned) const; private: data_type_t* return_type_; Statement *statement_; bool is_auto_; }; #endif /* IVL_PTask_H */ iverilog-10_1/PUdp.cc000066400000000000000000000022431265551621300145220ustar00rootroot00000000000000/* * Copyright (c) 2003-2004 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "PUdp.h" PUdp::PUdp(perm_string n, unsigned nports) : ports(nports), sequential(false), initial(verinum::Vx), name_(n) { } unsigned PUdp::find_port(const char*name) { for (unsigned idx = 0 ; idx < ports.count() ; idx += 1) { if (ports[idx] == name) return idx; } return ports.count(); } iverilog-10_1/PUdp.h000066400000000000000000000045351265551621300143720ustar00rootroot00000000000000#ifndef IVL_PUdp_H #define IVL_PUdp_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@picturel.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "LineInfo.h" # include "StringHeap.h" # include "svector.h" # include "verinum.h" class PExpr; /* * This class represents a parsed UDP. This is a much simpler object * than a module or macromodule. * * - all ports are scalar, * - pin 0 (the first port) is always output, * and the remaining pins are input. * * Thus, the ports can be represented as an ordered list of pin names. * If the output port is declared as a register in the Verilog source, * then this is a sequential UDP and the sequential flag is set to true. * * STATE TABLE * Each entry in the state table is given as a string with the same * number of characters as inputs. If the UDP is sequential, a * character is also included at the end of the string to represent * the current output. * * If the UDP is sequential, the "initial" member is taken to be the * initial value assigned in the source, or 'x' if none is given. */ class PUdp : public LineInfo { public: explicit PUdp(perm_string n, unsigned nports); svectorports; unsigned find_port(const char*name); bool sequential; svectortinput; svector tcurrent; svector toutput; verinum::V initial; map attributes; void dump(ostream&out) const; perm_string name_; private: private: // Not implemented PUdp(const PUdp&); PUdp& operator= (const PUdp&); }; #endif /* IVL_PUdp_H */ iverilog-10_1/PWire.cc000066400000000000000000000141501265551621300147000ustar00rootroot00000000000000/* * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PWire.h" # include "PExpr.h" # include PWire::PWire(perm_string n, NetNet::Type t, NetNet::PortType pt, ivl_variable_type_t dt) : name_(n), type_(t), port_type_(pt), data_type_(dt), signed_(false), isint_(false), port_set_(false), net_set_(false), is_scalar_(false), error_cnt_(0), set_data_type_(0), discipline_(0) { if (t == NetNet::INTEGER) { type_ = NetNet::REG; signed_ = true; isint_ = true; } } NetNet::Type PWire::get_wire_type() const { return type_; } perm_string PWire::basename() const { return name_; } bool PWire::set_wire_type(NetNet::Type t) { assert(t != NetNet::IMPLICIT); switch (type_) { case NetNet::IMPLICIT: type_ = t; return true; case NetNet::IMPLICIT_REG: if (t == NetNet::REG) { type_ = t; return true; } if (t == NetNet::INTEGER) { type_ = NetNet::REG; isint_ = true; return true; } return false; case NetNet::REG: if (t == NetNet::INTEGER) { isint_ = true; return true; } if (t == NetNet::REG) return true; return false; default: if (type_ != t) return false; else return true; } } NetNet::PortType PWire::get_port_type() const { return port_type_; } bool PWire::set_port_type(NetNet::PortType pt) { assert(pt != NetNet::NOT_A_PORT); assert(pt != NetNet::PIMPLICIT); switch (port_type_) { case NetNet::PIMPLICIT: port_type_ = pt; return true; case NetNet::NOT_A_PORT: return false; default: if (port_type_ != pt) return false; else return true; } } bool PWire::set_data_type(ivl_variable_type_t dt) { if (data_type_ != IVL_VT_NO_TYPE) { if (data_type_ != dt) return false; else return true; } assert(data_type_ == IVL_VT_NO_TYPE); data_type_ = dt; return true; } ivl_variable_type_t PWire::get_data_type() const { return data_type_; } void PWire::set_signed(bool flag) { signed_ = flag; } bool PWire::get_signed() const { return signed_; } bool PWire::get_isint() const { if (isint_) return true; if (vector_type_t*tmp = dynamic_cast(set_data_type_)) { return tmp->integer_flag; } return false; } bool PWire::get_scalar() const { return is_scalar_; } void PWire::set_range_scalar(PWSRType type) { is_scalar_ = true; switch (type) { case SR_PORT: if (port_set_) { cerr << get_fileline() << ": error: Port ``" << name_ << "'' has already been declared a port." << endl; error_cnt_ += 1; } else { port_set_ = true; } return; case SR_NET: if (net_set_) { cerr << get_fileline() << ": error: Net ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } else { net_set_ = true; } return; case SR_BOTH: if (port_set_ || net_set_) { if (port_set_) { cerr << get_fileline() << ": error: Port ``" << name_ << "'' has already been declared a port." << endl; error_cnt_ += 1; } if (net_set_) { cerr << get_fileline() << ": error: Net ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } } else { port_set_ = true; net_set_ = true; } return; } } void PWire::set_range(const list&rlist, PWSRType type) { switch (type) { case SR_PORT: if (port_set_) { cerr << get_fileline() << ": error: Port ``" << name_ << "'' has already been declared a port." << endl; error_cnt_ += 1; } else { port_ = rlist; port_set_ = true; is_scalar_ = false; } return; case SR_NET: if (net_set_) { cerr << get_fileline() << ": error: Net ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } else { net_ = rlist; net_set_ = true; is_scalar_ = false; } return; case SR_BOTH: if (port_set_ || net_set_) { if (port_set_) { cerr << get_fileline() << ": error: Port ``" << name_ << "'' has already been declared a port." << endl; error_cnt_ += 1; } if (net_set_) { cerr << get_fileline() << ": error: Net ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } } else { port_ = rlist; port_set_ = true; net_ = rlist; net_set_ = true; is_scalar_ = false; } return; } } void PWire::set_unpacked_idx(const list&ranges) { if (! unpacked_.empty()) { cerr << get_fileline() << ": error: Array ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } else { unpacked_ = ranges; } } void PWire::set_data_type(data_type_t*type) { assert(set_data_type_ == 0); set_data_type_ = type; if (vector_type_t*tmp = dynamic_cast(type)) { if (tmp->integer_flag) isint_ = true; } } void PWire::set_discipline(ivl_discipline_t d) { assert(discipline_ == 0); discipline_ = d; } ivl_discipline_t PWire::get_discipline(void) const { return discipline_; } iverilog-10_1/PWire.h000066400000000000000000000077131265551621300145510ustar00rootroot00000000000000#ifndef IVL_PWire_H #define IVL_PWire_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netlist.h" # include "LineInfo.h" # include # include # include "StringHeap.h" #ifdef HAVE_IOSFWD # include #else class ostream; #endif class PExpr; class Design; class netdarray_t; /* * The different type of PWire::set_range() calls. */ enum PWSRType {SR_PORT, SR_NET, SR_BOTH}; /* * Wires include nets, registers and ports. A net or register becomes * a port by declaration, so ports are not separate. The module * identifies a port by keeping it in its port list. * * The hname parameter to the constructor is a hierarchical name. It * is the name of the wire within a module, so does not include the * current scope or any instances. Modules contain all the wires, so * from that perspective, sub-scopes within the module are a part of * the wire name. */ class PWire : public LineInfo { public: PWire(perm_string name, NetNet::Type t, NetNet::PortType pt, ivl_variable_type_t dt); // Return a hierarchical name. perm_string basename() const; NetNet::Type get_wire_type() const; bool set_wire_type(NetNet::Type); NetNet::PortType get_port_type() const; bool set_port_type(NetNet::PortType); void set_signed(bool flag); bool get_signed() const; bool get_isint() const; bool get_scalar() const; bool set_data_type(ivl_variable_type_t dt); ivl_variable_type_t get_data_type() const; void set_range_scalar(PWSRType type); void set_range(const std::list&ranges, PWSRType type); void set_unpacked_idx(const std::list&ranges); void set_data_type(data_type_t*type); void set_discipline(ivl_discipline_t); ivl_discipline_t get_discipline(void) const; map attributes; // Write myself to the specified stream. void dump(ostream&out, unsigned ind=4) const; NetNet* elaborate_sig(Design*, NetScope*scope) const; private: perm_string name_; NetNet::Type type_; NetNet::PortType port_type_; ivl_variable_type_t data_type_; bool signed_; bool isint_; // original type of integer // These members hold expressions for the bit width of the // wire. If they do not exist, the wire is 1 bit wide. If they // do exist, they represent the packed dimensions of the // bit. The first item in the list is the first range, and so // on. For example "reg [3:0][7:0] ..." will contains the // range_t object for [3:0] first and [7:0] last. std::listport_; bool port_set_; std::listnet_; bool net_set_; bool is_scalar_; unsigned error_cnt_; // If this wire is actually a memory, these indices will give // me the size and address ranges of the memory. std::listunpacked_; // This is the complex type of the wire. the data_type_ may // modify how this is interpreted. data_type_t*set_data_type_; ivl_discipline_t discipline_; private: // not implemented PWire(const PWire&); PWire& operator= (const PWire&); }; #endif /* IVL_PWire_H */ iverilog-10_1/QUICK_START.txt000066400000000000000000000057211265551621300157410ustar00rootroot00000000000000 * Getting Started with Icarus Verilog Icarus Verilog is a Verilog compiler. It is suitable for use as a simulator, and, to some degree, synthesizer. Icarus Verilog runs under Linux and a variety of UNIX systems, as well as Windows as a command line tool, so the instructions are generally applicable to all environments. Note that this is only a quick start. For more detailed documentation, see the manual page for the iverilog command. * Hello, World! The first thing you want to do as a user is learn how to compile and execute even the most trivial design. For the purposes of simulation, we use as our example *the* most trivial simulation: module main; initial begin $display("Hello, World"); $finish ; end endmodule By a text editor (or copy hello.vl from the Icarus Verilog examples directory) arrange for this program to be in a text file, "hello.vl". Next, compile this program with a command like this: % iverilog -o hello hello.vl The results of this compile are placed into the file "hello", as the "-o" flag tells the compiler where to place the compiled result. Next, execute the compiled program like so: % vvp hello Hello, World And there it is, the program has been executed. So what happened? The first step, the "iverilog" command, read and interpreted the source file, then generated a compiled result. The compiled form may be selected by command line switches, but the default form is the VVP format, which is actually run by the "vvp" command. The "iverilog" and "vvp" commands are the only commands that users use to invoke Icarus Verilog. What the compiler actually does is controlled by command line switches. In our little example, we asked the compiler to compile the source program to the default vvp form, which is in turn executed by the vvp program. * Windows Install The easiest way to install under Windows is to get a precompiled installer for the version you wish to install. Icarus Verilog is distributed for Windows users as a self-installing .exe. Just execute the installer and follow the instructions. During the install, take note of the directory where the program is installed: for example, C:\iverilog is a good place to install. Once the binary is installed, you need to add the bin directory to your execution path. The executables you need are in C:\iverilog\bin, where the "C:\iverilog" part is actually the root of where you installed the package. The programs are in the bin subdirectory. Put this directory in your PATH environment variable, and the above commands become accessible to you at the command line prompt, or even in batch files. * Linux Install Under Linux, the install is even easier. For RedHat and Mandrake based systems, there is the appropriate RPM file. Just install the package with the "rpm -U " command. Debian users should get Icarus Verilog packages from the main Debian software site. * Install From Source In this case, see README.txt and other documentation that comes with the source. iverilog-10_1/README.txt000066400000000000000000000424161265551621300150470ustar00rootroot00000000000000 THE ICARUS VERILOG COMPILATION SYSTEM Copyright 2000-2004 Stephen Williams 1.0 What is ICARUS Verilog? Icarus Verilog is intended to compile ALL of the Verilog HDL as described in the IEEE-1364 standard. Of course, it's not quite there yet. It does currently handle a mix of structural and behavioral constructs. For a view of the current state of Icarus Verilog, see its home page at . Icarus Verilog is not aimed at being a simulator in the traditional sense, but a compiler that generates code employed by back-end tools. For instructions on how to run Icarus Verilog, see the ``iverilog'' man page. 2.0 Building/Installing Icarus Verilog From Source If you are starting from source, the build process is designed to be as simple as practical. Someone basically familiar with the target system and C/C++ compilation should be able to build the source distribution with little effort. Some actual programming skills are not required, but helpful in case of problems. If you are building for Windows, see the mingw.txt file. 2.1 Compile Time Prerequisites You need the following software to compile Icarus Verilog from source on a UNIX-like system: - GNU Make The Makefiles use some GNU extensions, so a basic POSIX make will not work. Linux systems typically come with a satisfactory make. BSD based systems (i.e., NetBSD, FreeBSD) typically have GNU make as the gmake program. - ISO C++ Compiler The ivl and ivlpp programs are written in C++ and make use of templates and some of the standard C++ library. egcs and recent gcc compilers with the associated libstdc++ are known to work. MSVC++ 5 and 6 are known to definitely *not* work. - bison and flex - gperf 2.7 The lexical analyzer doesn't recognize keywords directly, but instead matches symbols and looks them up in a hash table in order to get the proper lexical code. The gperf program generates the lookup table. A version problem with this program is the most common cause of difficulty. See the Icarus Verilog FAQ. - readline 4.2 On Linux systems, this usually means the readline-devel rpm. In any case, it is the development headers of readline that are needed. - termcap The readline library in turn uses termcap. If you are building from git, you will also need software to generate the configure scripts. - autoconf 2.53 This generates configure scripts from configure.in. The 2.53 or later versions are known to work, autoconf 2.13 is reported to *not* work. 2.2 Compilation Unpack the tar-ball and cd into the verilog-######### directory (presumably that is how you got to this README) and compile the source with the commands: ./configure make Normally, this command automatically figures out everything it needs to know. It generally works pretty well. There are a few flags to the configure script that modify its behavior: --prefix= The default is /usr/local, which causes the tool suite to be compiled for install in /usr/local/bin, /usr/local/share/ivl, etc. I recommend that if you are configuring for precompiled binaries, use --prefix=/usr. On Solaris systems, it is common to use --prefix=/opt. You can configure for a non-root install with --prefix=$HOME. --enable-suffix --enable-suffix= --disable-suffix Enable/disable changing the names of install files to use a suffix string so that this version or install can co- exist with other versions. This renames the installed commands (iverilog, iverilog-vpi, vvp) and the installed library files and include directory so that installations with the same prefix but different suffix are guaranteed to not interfere with each other. 2.3 (Optional) Testing To run a simple test before installation, execute make check The commands printed by this run might help you in running Icarus Verilog on your own Verilog sources before the package is installed by root. 2.4 Installation Now install the files in an appropriate place. (The makefiles by default install in /usr/local unless you specify a different prefix with the --prefix= flag to the configure command.) You may need to do this as root to gain access to installation directories. make install 2.5 Uninstallation The generated Makefiles also include the uninstall target. This should remove all the files that ``make install'' creates. 3.0 How Icarus Verilog Works This tool includes a parser which reads in Verilog (plus extensions) and generates an internal netlist. The netlist is passed to various processing steps that transform the design to more optimal/practical forms, then is passed to a code generator for final output. The processing steps and the code generator are selected by command line switches. 3.1 Preprocessing There is a separate program, ivlpp, that does the preprocessing. This program implements the `include and `define directives producing output that is equivalent but without the directives. The output is a single file with line number directives, so that the actual compiler only sees a single input file. See ivlpp/ivlpp.txt for details. 3.2 Parse The Verilog compiler starts by parsing the Verilog source file. The output of the parse is a list of Module objects in "pform". The pform (see pform.h) is mostly a direct reflection of the compilation step. There may be dangling references, and it is not yet clear which module is the root. One can see a human readable version of the final pform by using the ``-P '' flag to the ``ivl'' subcommand. This will cause ivl to dump the pform into the file named . (Note that this is not normally done, unless debugging the ``ivl'' subcommand.) 3.3 Elaboration This phase takes the pform and generates a netlist. The driver selects (by user request or lucky guess) the root module to elaborate, resolves references and expands the instantiations to form the design netlist. (See netlist.txt.) Final semantic checks are performed during elaboration, and some simple optimizations are performed. The netlist includes all the behavioral descriptions, as well as gates and wires. The elaborate() function performs the elaboration. One can see a human readable version of the final, elaborated and optimized netlist by using the ``-N '' flag to the compiler. If elaboration succeeds, the final netlist (i.e., after optimizations but before code generation) will be dumped into the file named . Elaboration is actually performed in two steps: scopes and parameters first, followed by the structural and behavioral elaboration. 3.3.1 Scope Elaboration This pass scans through the pform looking for scopes and parameters. A tree of NetScope objects is built up and placed in the Design object, with the root module represented by the root NetScope object. The elab_scope.cc file contains most of the code for handling this phase. The tail of the elaborate_scope behavior (after the pform is traversed) includes a scan of the NetScope tree to locate defparam assignments that were collected during scope elaboration. This is when the defparam overrides are applied to the parameters. 3.3.2 Netlist Elaboration After the scopes and parameters are generated and the NetScope tree fully formed, the elaboration runs through the pform again, this time generating the structural and behavioral netlist. Parameters are elaborated and evaluated by now so all the constants of code generation are now known locally, so the netlist can be generated by simply passing through the pform. 3.4 Optimization This is actually a collection of processing steps that perform optimizations that do not depend on the target technology. Examples of some useful transformations are - eliminate null effect circuitry - combinational reduction - constant propagation The actual functions performed are specified on the ivl command line by the -F flags (see below). 3.5 Code Generation This step takes the design netlist and uses it to drive the code generator (see target.h). This may require transforming the design to suit the technology. The emit() method of the Design class performs this step. It runs through the design elements, calling target functions as need arises to generate actual output. The user selects the target code generator with the -t flag on the command line. 3.6 ATTRIBUTES NOTE: The $attribute syntax will soon be deprecated in favor of the Verilog-2001 attribute syntax, which is cleaner and standardized. The parser accepts, as an extension to Verilog, the $attribute module item. The syntax of the $attribute item is: $attribute (, , ); The $attribute keyword looks like a system task invocation. The difference here is that the parameters are more restricted than those of a system task. The must be an identifier. This will be the item to get an attribute. The and are strings, not expressions, that give the key and the value of the attribute to be attached to the identified object. Attributes are [ ] pairs and are used to communicate with the various processing steps. See the documentation for the processing step for a list of the pertinent attributes. Attributes can also be applied to gate types. When this is done, the attribute is given to every instantiation of the primitive. The syntax for the attribute statement is the same, except that the names a primitive earlier in the compilation unit and the statement is placed in global scope, instead of within a module. The semicolon is not part of a type attribute. Note that attributes are also occasionally used for communication between processing steps. Processing steps that are aware of others may place attributes on netlist objects to communicate information to later steps. Icarus Verilog also accepts the Verilog 2001 syntax for attributes. They have the same general meaning as with the $attribute syntax, but they are attached to objects by position instead of by name. Also, the key is a Verilog identifier instead of a string. 4.0 Running iverilog The preferred way to invoke the compiler is with the iverilog(1) command. This program invokes the preprocessor (ivlpp) and the compiler (ivl) with the proper command line options to get the job done in a friendly way. See the iverilog(1) man page for usage details. 4.1 EXAMPLES Example: Compiling "hello.vl" ------------------------ hello.vl ---------------------------- module main(); initial begin $display("Hi there"); $finish ; end endmodule -------------------------------------------------------------- Ensure that "iverilog" is on your search path, and the vpi library is available. To compile the program: iverilog hello.vl (The above presumes that /usr/local/include and /usr/local/lib are part of the compiler search path, which is usually the case for gcc.) To run the program: ./a.out You can use the "-o" switch to name the output command to be generated by the compiler. See the iverilog(1) man page. 5.0 Unsupported Constructs Icarus Verilog is in development - as such it still only supports a (growing) subset of Verilog. Below is a description of some of the currently unsupported Verilog features. This list is not exhaustive, and does not account for errors in the compiler. See the Icarus Verilog web page for the current state of support for Verilog, and in particular, browse the bug report database for reported unsupported constructs. - System functions are supported, but the return value is a little tricky. See SYSTEM FUNCTION TABLE FILES in the iverilog man page. - Specify blocks are parsed but ignored in general. - trireg is not supported. tri0 and tri1 are supported. - tran primitives, i.e. tran, tranif1, tranif0, rtran, rtranif1 and rtranif0 are not supported. - Net delays, of the form "wire #N foo;" do not work. Delays in every other context do work properly, including the V2001 form "wire #5 foo = bar;" - Event controls inside non-blocking assignments are not supported. i.e.: a <= @(posedge clk) b; - Macro arguments are not supported. `define macros are supported, but they cannot take arguments. 5.1 Nonstandard Constructs or Behaviors Icarus Verilog includes some features that are not part of the IEEE1364 standard, but have well defined meaning, and also sometimes gives nonstandard (but extended) meanings to some features of the language that are defined. See the "extensions.txt" documentation for more details. $is_signed() This system function returns 1 if the expression contained is signed, or 0 otherwise. This is mostly of use for compiler regression tests. $sizeof() $bits() The $bits system function returns the size in bits of the expression that is its argument. The result of this function is undefined if the argument doesn't have a self-determined size. The $sizeof function is deprecated in favor of $bits, which is the same thing, but included in the SystemVerilog definition. $simtime The $simtime system function returns as a 64bit value the simulation time, unscaled by the time units of local scope. This is different from the $time and $stime functions which return the scaled times. This function is added for regression testing of the compiler and run time, but can be used by applications who really want the simulation time. Note that the simulation time can be confusing if there are lots of different `timescales within a design. It is not in general possible to predict what the simulation precision will turn out to be. $mti_random() $mti_dist_uniform These functions are similar to the IEEE1364 standard $random functions, but they use the Mersenne Twister (MT19937) algorithm. This is considered an excellent random number generator, but does not generate the same sequence as the standardized $random. Builtin system functions Certain of the system functions have well defined meanings, so can theoretically be evaluated at compile time, instead of using runtime VPI code. Doing so means that VPI cannot override the definitions of functions handled in this manner. On the other hand, this makes them synthesizable, and also allows for more aggressive constant propagation. The functions handled in this manner are: $bits $signed $sizeof $unsigned Implementations of these system functions in VPI modules will be ignored. Preprocessing Library Modules Icarus Verilog does preprocess modules that are loaded from libraries via the -y mechanism. However, the only macros defined during compilation of that file are those that it defines itself (or includes) or that are defined on the command line or command file. Specifically, macros defined in the non-library source files are not remembered when the library module is loaded. This is intentional. If it were otherwise, then compilation results might vary depending on the order that libraries are loaded, and that is too unpredictable. It is said that some commercial compilers do allow macro definitions to span library modules. That's just plain weird. Width in %t Time Formats Standard Verilog does not allow width fields in the %t formats of display strings. For example, this is illegal: $display("Time is %0t", %time); Standard Verilog instead relies on the $timeformat to completely specify the format. Icarus Verilog allows the programmer to specify the field width. The "%t" format in Icarus Verilog works exactly as it does in standard Verilog. However, if the programmer chooses to specify a minimum width (i.e., "%5t"), then for that display Icarus Verilog will override the $timeformat minimum width and use the explicit minimum width. vpiScope iterator on vpiScope objects. In the VPI, the normal way to iterate over vpiScope objects contained within a vpiScope object, is the vpiInternalScope iterator. Icarus Verilog adds support for the vpiScope iterator of a vpiScope object, that iterates over *everything* the is contained in the current scope. This is useful in cases where one wants to iterate over all the objects in a scope without iterating over all the contained types explicitly. time 0 race resolution. Combinational logic is routinely modeled using always blocks. However, this can lead to race conditions if the inputs to the combinational block are initialized in initial statements. Icarus Verilog slightly modifies time 0 scheduling by arranging for always statements with ANYEDGE sensitivity lists to be scheduled before any other threads. This causes combinational always blocks to be triggered when the values in the sensitivity list are initialized by initial threads. Nets with Types Icarus Verilog support an extension syntax that allows nets and regs to be explicitly typed. The currently supported types are logic, bool and real. This implies that "logic" and "bool" are new keywords. Typical syntax is: wire real foo = 1.0; reg logic bar, bat; ... and so forth. The syntax can be turned off by using the -g2 flag to iverilog, and turned on explicitly with the -g2x flag to iverilog. 6.0 CREDITS Except where otherwise noted, Icarus Verilog, ivl and ivlpp are Copyright Stephen Williams. The proper notices are in the head of each file. However, I have early on received aid in the form of fixes, Verilog guidance, and especially testing from many people. Testers in particular include a larger community of people interested in a GPL Verilog for Linux. iverilog-10_1/Statement.cc000066400000000000000000000174341265551621300156260ustar00rootroot00000000000000/* * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "Statement.h" # include "PExpr.h" # include "ivl_assert.h" Statement::~Statement() { } PAssign_::PAssign_(PExpr*lval__, PExpr*ex, bool is_constant) : event_(0), count_(0), lval_(lval__), rval_(ex), is_constant_(is_constant) { delay_ = 0; } PAssign_::PAssign_(PExpr*lval__, PExpr*de, PExpr*ex) : event_(0), count_(0), lval_(lval__), rval_(ex), is_constant_(false) { delay_ = de; } PAssign_::PAssign_(PExpr*lval__, PExpr*cnt, PEventStatement*ev, PExpr*ex) : event_(ev), count_(cnt), lval_(lval__), rval_(ex), is_constant_(false) { delay_ = 0; } PAssign_::~PAssign_() { delete lval_; delete rval_; } PAssign::PAssign(PExpr*lval__, PExpr*ex) : PAssign_(lval__, ex, false), op_(0) { } PAssign::PAssign(PExpr*lval__, char op, PExpr*ex) : PAssign_(lval__, ex, false), op_(op) { } PAssign::PAssign(PExpr*lval__, PExpr*d, PExpr*ex) : PAssign_(lval__, d, ex), op_(0) { } PAssign::PAssign(PExpr*lval__, PExpr*cnt, PEventStatement*d, PExpr*ex) : PAssign_(lval__, cnt, d, ex), op_(0) { } PAssign::PAssign(PExpr*lval__, PExpr*ex, bool is_constant) : PAssign_(lval__, ex, is_constant), op_(0) { } PAssign::~PAssign() { } PAssignNB::PAssignNB(PExpr*lval__, PExpr*ex) : PAssign_(lval__, ex, false) { } PAssignNB::PAssignNB(PExpr*lval__, PExpr*d, PExpr*ex) : PAssign_(lval__, d, ex) { } PAssignNB::PAssignNB(PExpr*lval__, PExpr*cnt, PEventStatement*d, PExpr*ex) : PAssign_(lval__, cnt, d, ex) { } PAssignNB::~PAssignNB() { } PBlock::PBlock(perm_string n, LexicalScope*parent, BL_TYPE t) : PScope(n, parent), bl_type_(t) { } PBlock::PBlock(BL_TYPE t) : PScope(perm_string()), bl_type_(t) { } PBlock::~PBlock() { for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) delete list_[idx]; } PChainConstructor* PBlock::extract_chain_constructor() { if (list_.empty()) return 0; if (PChainConstructor*res = dynamic_cast (list_[0])) { for (size_t idx = 0 ; idx < list_.size()-1 ; idx += 1) list_[idx] = list_[idx+1]; list_.resize(list_.size()-1); return res; } return 0; } void PBlock::set_join_type(PBlock::BL_TYPE type) { assert(bl_type_ == BL_PAR); assert(type==BL_PAR || type==BL_JOIN_NONE || type==BL_JOIN_ANY); bl_type_ = type; } void PBlock::set_statement(const vector&st) { list_ = st; } void PBlock::push_statement_front(Statement*that) { ivl_assert(*this, bl_type_==BL_SEQ); list_.resize(list_.size()+1); for (size_t idx = list_.size()-1 ; idx > 0 ; idx -= 1) list_[idx] = list_[idx-1]; list_[0] = that; } PCallTask::PCallTask(const pform_name_t&n, const list&p) : package_(0), path_(n), parms_(p.size()) { list::const_iterator cur = p.begin(); for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { parms_[idx] = *cur; ++cur; } assert(cur == p.end()); } PCallTask::PCallTask(PPackage*pkg, const pform_name_t&n, const list&p) : package_(pkg), path_(n), parms_(p.size()) { list::const_iterator cur = p.begin(); for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { parms_[idx] = *cur; ++cur; } assert(cur == p.end()); } PCallTask::PCallTask(perm_string n, const list&p) : package_(0), parms_(p.size()) { list::const_iterator cur = p.begin(); for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { parms_[idx] = *cur; ++cur; } assert(cur == p.end()); path_.push_back(name_component_t(n)); } PCallTask::~PCallTask() { } const pform_name_t& PCallTask::path() const { return path_; } PCase::PCase(NetCase::TYPE t, PExpr*ex, svector*l) : type_(t), expr_(ex), items_(l) { } PCase::~PCase() { delete expr_; for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) if ((*items_)[idx]->stat) delete (*items_)[idx]->stat; delete[]items_; } PCAssign::PCAssign(PExpr*l, PExpr*r) : lval_(l), expr_(r) { } PCAssign::~PCAssign() { delete lval_; delete expr_; } PChainConstructor::PChainConstructor(const list&parms) : parms_(parms.size()) { list::const_iterator cur = parms.begin(); for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { parms_[idx] = *cur; ++cur; } assert(cur == parms.end()); } PChainConstructor::~PChainConstructor() { } PCondit::PCondit(PExpr*ex, Statement*i, Statement*e) : expr_(ex), if_(i), else_(e) { } PCondit::~PCondit() { delete expr_; delete if_; delete else_; } PDeassign::PDeassign(PExpr*l) : lval_(l) { } PDeassign::~PDeassign() { delete lval_; } PDelayStatement::PDelayStatement(PExpr*d, Statement*st) : delay_(d), statement_(st) { } PDelayStatement::~PDelayStatement() { } PDisable::PDisable(const pform_name_t&sc) : scope_(sc) { } PDisable::~PDisable() { } PDoWhile::PDoWhile(PExpr*ex, Statement*st) : cond_(ex), statement_(st) { } PDoWhile::~PDoWhile() { delete cond_; delete statement_; } PEventStatement::PEventStatement(const svector&ee) : expr_(ee), statement_(0) { assert(expr_.count() > 0); } PEventStatement::PEventStatement(PEEvent*ee) : expr_(1), statement_(0) { expr_[0] = ee; } PEventStatement::PEventStatement(void) : statement_(0) { } PEventStatement::~PEventStatement() { // delete the events and the statement? } void PEventStatement::set_statement(Statement*st) { statement_ = st; } bool PEventStatement::has_aa_term(Design*des, NetScope*scope) { bool flag = false; for (unsigned idx = 0 ; idx < expr_.count() ; idx += 1) { flag = expr_[idx]->has_aa_term(des, scope) || flag; } return flag; } PForce::PForce(PExpr*l, PExpr*r) : lval_(l), expr_(r) { } PForce::~PForce() { delete lval_; delete expr_; } PForeach::PForeach(perm_string av, const list&ix, Statement*s) : array_var_(av), index_vars_(ix.size()), statement_(s) { size_t idx = 0; for (list::const_iterator cur = ix.begin() ; cur != ix.end() ; ++cur) index_vars_[idx++] = *cur; } PForeach::~PForeach() { delete statement_; } PForever::PForever(Statement*s) : statement_(s) { } PForever::~PForever() { delete statement_; } PForStatement::PForStatement(PExpr*n1, PExpr*e1, PExpr*cond, Statement*step, Statement*st) : name1_(n1), expr1_(e1), cond_(cond), step_(step), statement_(st) { } PForStatement::~PForStatement() { } PProcess::~PProcess() { delete statement_; } PRelease::PRelease(PExpr*l) : lval_(l) { } PRelease::~PRelease() { delete lval_; } PRepeat::PRepeat(PExpr*e, Statement*s) : expr_(e), statement_(s) { } PRepeat::~PRepeat() { delete expr_; delete statement_; } PReturn::PReturn(PExpr*e) : expr_(e) { } PReturn::~PReturn() { delete expr_; } PTrigger::PTrigger(const pform_name_t&e) : event_(e) { } PTrigger::~PTrigger() { } PWhile::PWhile(PExpr*ex, Statement*st) : cond_(ex), statement_(st) { } PWhile::~PWhile() { delete cond_; delete statement_; } iverilog-10_1/Statement.h000066400000000000000000000410341265551621300154610ustar00rootroot00000000000000#ifndef IVL_Statement_H #define IVL_Statement_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "ivl_target.h" # include "svector.h" # include "StringHeap.h" # include "PDelays.h" # include "PExpr.h" # include "PScope.h" # include "HName.h" # include "LineInfo.h" class PExpr; class PChainConstructor; class PPackage; class Statement; class PEventStatement; class Design; class NetAssign_; class NetCAssign; class NetDeassign; class NetForce; class NetScope; /* * The PProcess is the root of a behavioral process. Each process gets * one of these, which contains its type (initial, always, or final) * and a pointer to the single statement that is the process. A module * may have several concurrent processes. */ class PProcess : public LineInfo { public: PProcess(ivl_process_type_t t, Statement*st) : type_(t), statement_(st) { } virtual ~PProcess(); bool elaborate(Design*des, NetScope*scope) const; ivl_process_type_t type() const { return type_; } Statement*statement() { return statement_; } map attributes; virtual void dump(ostream&out, unsigned ind) const; private: ivl_process_type_t type_; Statement*statement_; }; /* * The PProcess is a process, the Statement is the actual action. In * fact, the Statement class is abstract and represents all the * possible kinds of statements that exist in Verilog. */ class Statement : public LineInfo { public: Statement() { } virtual ~Statement() =0; virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; map attributes; }; /* * Assignment statements of the various forms are handled by this * type. The rvalue is an expression. The lvalue needs to be figured * out by the parser as much as possible. */ class PAssign_ : public Statement { public: explicit PAssign_(PExpr*lval, PExpr*ex, bool is_constant); explicit PAssign_(PExpr*lval, PExpr*de, PExpr*ex); explicit PAssign_(PExpr*lval, PExpr*cnt, PEventStatement*de, PExpr*ex); virtual ~PAssign_() =0; const PExpr* lval() const { return lval_; } PExpr* rval() const { return rval_; } protected: NetAssign_* elaborate_lval(Design*, NetScope*scope) const; NetExpr* elaborate_rval_(Design*, NetScope*, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, unsigned lv_width) const; NetExpr* elaborate_rval_(Design*, NetScope*, ivl_type_t ntype) const; NetExpr* elaborate_rval_obj_(Design*, NetScope*, ivl_variable_type_t type) const; PExpr* delay_; PEventStatement*event_; PExpr* count_; private: PExpr* lval_; PExpr* rval_; bool is_constant_; }; class PAssign : public PAssign_ { public: // lval - assignment l-value // ex - assignment r-value // op - compressed assignment operator (i.e. '+', '-', ...) // de - delayed assignment delay expression explicit PAssign(PExpr*lval, PExpr*ex); explicit PAssign(PExpr*lval, char op, PExpr*ex); explicit PAssign(PExpr*lval, PExpr*de, PExpr*ex); explicit PAssign(PExpr*lval, PExpr*cnt, PEventStatement*de, PExpr*ex); explicit PAssign(PExpr*lval, PExpr*ex, bool is_constant); ~PAssign(); virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; private: NetProc* elaborate_compressed_(Design*des, NetScope*scope) const; char op_; }; class PAssignNB : public PAssign_ { public: explicit PAssignNB(PExpr*lval, PExpr*ex); explicit PAssignNB(PExpr*lval, PExpr*de, PExpr*ex); explicit PAssignNB(PExpr*lval, PExpr*cnt, PEventStatement*de, PExpr*ex); ~PAssignNB(); virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; private: NetProc*assign_to_memory_(class NetMemory*, PExpr*, Design*des, NetScope*scope) const; }; /* * A block statement is an ordered list of statements that make up the * block. The block can be sequential or parallel, which only affects * how the block is interpreted. The parser collects the list of * statements before constructing this object, so it knows a priori * what is contained. */ class PBlock : public PScope, public Statement { public: enum BL_TYPE { BL_SEQ, BL_PAR, BL_JOIN_NONE, BL_JOIN_ANY }; // If the block has a name, it is a scope and also has a parent. explicit PBlock(perm_string n, LexicalScope*parent, BL_TYPE t); // If it doesn't have a name, it's not a scope explicit PBlock(BL_TYPE t); ~PBlock(); BL_TYPE bl_type() const { return bl_type_; } // This is only used if this block is the statement list for a // constructor. We look for a PChainConstructor as the first // statement, and if it is there, extract it. PChainConstructor*extract_chain_constructor(); // If the bl_type() is BL_PAR, it is possible to replace it // with JOIN_NONE or JOIN_ANY. This is to help the parser. void set_join_type(BL_TYPE); void set_statement(const std::vector&st); // Copy the statement from that block to the front of this // block. void push_statement_front(Statement*that); virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; private: BL_TYPE bl_type_; std::vectorlist_; }; class PCallTask : public Statement { public: explicit PCallTask(PPackage*pkg, const pform_name_t&n, const list&parms); explicit PCallTask(const pform_name_t&n, const list&parms); explicit PCallTask(perm_string n, const list&parms); ~PCallTask(); const pform_name_t& path() const; virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; private: NetProc* elaborate_sys(Design*des, NetScope*scope) const; NetProc* elaborate_usr(Design*des, NetScope*scope) const; NetProc*elaborate_method_(Design*des, NetScope*scope, bool add_this_flag = false) const; NetProc*elaborate_function_(Design*des, NetScope*scope) const; NetProc*elaborate_build_call_(Design*des, NetScope*scope, NetScope*task, NetExpr*use_this) const; NetProc*elaborate_sys_task_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, const char*sys_task_name) const; bool test_task_calls_ok_(Design*des, NetScope*scope) const; PPackage*package_; pform_name_t path_; vector parms_; }; class PCase : public Statement { public: struct Item { listexpr; Statement*stat; }; PCase(NetCase::TYPE, PExpr*ex, svector*); ~PCase(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: NetCase::TYPE type_; PExpr*expr_; svector*items_; private: // not implemented PCase(const PCase&); PCase& operator= (const PCase&); }; class PCAssign : public Statement { public: explicit PCAssign(PExpr*l, PExpr*r); ~PCAssign(); virtual NetCAssign* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*lval_; PExpr*expr_; }; /* * This represents the syntax "super.new(...)". This is not really an * executable statement, but the elaborator will handle these * specially and will remove them from the statement stream. If any */ class PChainConstructor : public Statement { public: PChainConstructor(const list&parms); ~PChainConstructor(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; inline const std::vector& chain_args(void) const { return parms_; } private: std::vector parms_; }; class PCondit : public Statement { public: PCondit(PExpr*ex, Statement*i, Statement*e); ~PCondit(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*expr_; Statement*if_; Statement*else_; private: // not implemented PCondit(const PCondit&); PCondit& operator= (const PCondit&); }; class PDeassign : public Statement { public: explicit PDeassign(PExpr*l); ~PDeassign(); virtual NetDeassign* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*lval_; }; class PDelayStatement : public Statement { public: PDelayStatement(PExpr*d, Statement*st); ~PDelayStatement(); virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; private: PExpr*delay_; Statement*statement_; }; /* * This represents the parsing of a disable statement. */ class PDisable : public Statement { public: explicit PDisable(const pform_name_t&sc); ~PDisable(); virtual void dump(ostream&out, unsigned ind) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; private: pform_name_t scope_; }; class PDoWhile : public Statement { public: PDoWhile(PExpr*ex, Statement*st); ~PDoWhile(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*cond_; Statement*statement_; }; /* * The event statement represents the event delay in behavioral * code. It comes from such things as: * * @name ; * @(expr) ; * @* ; */ class PEventStatement : public Statement { public: explicit PEventStatement(const svector&ee); explicit PEventStatement(PEEvent*ee); // Make an @* statement. explicit PEventStatement(void); ~PEventStatement(); void set_statement(Statement*st); virtual void dump(ostream&out, unsigned ind) const; // Call this with a NULL statement only. It is used to print // the event expression for inter-assignment event controls. virtual void dump_inline(ostream&out) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; bool has_aa_term(Design*des, NetScope*scope); // This method is used to elaborate, but attach a previously // elaborated statement to the event. NetProc* elaborate_st(Design*des, NetScope*scope, NetProc*st) const; NetProc* elaborate_wait(Design*des, NetScope*scope, NetProc*st) const; NetProc* elaborate_wait_fork(Design*des, NetScope*scope) const; private: svectorexpr_; Statement*statement_; }; ostream& operator << (ostream&o, const PEventStatement&obj); class PForce : public Statement { public: explicit PForce(PExpr*l, PExpr*r); ~PForce(); virtual NetForce* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*lval_; PExpr*expr_; }; class PForeach : public Statement { public: explicit PForeach(perm_string var, const std::list&ix, Statement*stmt); ~PForeach(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: NetProc* elaborate_static_array_(Design*des, NetScope*scope, const std::vector&dims) const; private: perm_string array_var_; std::vector index_vars_; Statement*statement_; }; class PForever : public Statement { public: explicit PForever(Statement*s); ~PForever(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: Statement*statement_; }; class PForStatement : public Statement { public: PForStatement(PExpr*n1, PExpr*e1, PExpr*cond, Statement*step, Statement*body); ~PForStatement(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr* name1_; PExpr* expr1_; PExpr*cond_; Statement*step_; Statement*statement_; }; class PNoop : public Statement { public: PNoop() { } ~PNoop() { } }; class PRepeat : public Statement { public: explicit PRepeat(PExpr*expr, Statement*s); ~PRepeat(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*expr_; Statement*statement_; }; class PRelease : public Statement { public: explicit PRelease(PExpr*l); ~PRelease(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*lval_; }; class PReturn : public Statement { public: explicit PReturn(PExpr*e); ~PReturn(); NetProc* elaborate(Design*des, NetScope*scope) const; virtual void dump(std::ostream&out, unsigned ind) const; private: PExpr*expr_; }; /* * The PTrigger statement sends a trigger to a named event. Take the * name here. */ class PTrigger : public Statement { public: explicit PTrigger(const pform_name_t&ev); ~PTrigger(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: pform_name_t event_; }; class PWhile : public Statement { public: PWhile(PExpr*ex, Statement*st); ~PWhile(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: PExpr*cond_; Statement*statement_; }; #endif /* IVL_Statement_H */ iverilog-10_1/_pli_types.h.in000066400000000000000000000046101265551621300162700ustar00rootroot00000000000000#ifndef PLI_TYPES_H #define PLI_TYPES_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # undef HAVE_INTTYPES_H #ifdef HAVE_INTTYPES_H /* * If the host environment has the stdint.h header file, * then use that to size our PLI types. */ #ifndef __STDC_FORMAT_MACROS # define __STDC_FORMAT_MACROS #endif # include typedef uint64_t PLI_UINT64; typedef int64_t PLI_INT64; typedef uint32_t PLI_UINT32; typedef int32_t PLI_INT32; typedef signed short PLI_INT16; typedef unsigned short PLI_UINT16; typedef char PLI_BYTE8; typedef unsigned char PLI_UBYTE8; # define PLI_UINT64_FMT PRIu64 #else /* * If we do not have the c99 stdint.h header file, then use * configure detection to guess the pli types ourselves. */ # define SIZEOF_UNSIGNED_LONG_LONG 8 # define SIZEOF_UNSIGNED_LONG 8 # define SIZEOF_UNSIGNED 4 #if SIZEOF_UNSIGNED >= 8 typedef unsigned PLI_UINT64; typedef int PLI_INT64; # define PLI_UINT64_FMT "u" #else # if SIZEOF_UNSIGNED_LONG >= 8 typedef unsigned long PLI_UINT64; typedef long PLI_INT64; # define PLI_UINT64_FMT "lu" # else # if SIZEOF_UNSIGNED_LONG_LONG > SIZEOF_UNSIGNED_LONG typedef unsigned long long PLI_UINT64; typedef long long PLI_INT64; # define PLI_UINT64_FMT "llu" # else typedef unsigned long PLI_UINT64; typedef long PLI_INT64; # define PLI_UINT64_FMT "lu" # endif # endif #endif typedef signed int PLI_INT32; typedef unsigned int PLI_UINT32; typedef signed short PLI_INT16; typedef unsigned short PLI_UINT16; typedef char PLI_BYTE8; typedef unsigned char PLI_UBYTE8; #endif #endif /* PLI_TYPES_H */ iverilog-10_1/acc_user.h000066400000000000000000000167521265551621300153120ustar00rootroot00000000000000#ifndef ACC_USER_H #define ACC_USER_H /* * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This header file contains the definitions and declarations needed * by an Icarus Verilog user using acc_ routines. * * NOTE: Icarus Verilog does not support acc_ routines. This is just a * stub. The functions that are implemented here are actually * implemented using VPI routines. */ #ifdef __cplusplus # define EXTERN_C_START extern "C" { # define EXTERN_C_END } #else # define EXTERN_C_START # define EXTERN_C_END # define bool int #endif EXTERN_C_START # include "_pli_types.h" /* * This is a declaration of the "handle" type that is compatible with * the vpiHandle from vpi_user.h. The libveriuser library implements * the acc handle type as a vpiHandle, to prevent useless indirection. */ typedef struct __vpiHandle *handle; /* OBJECT TYPES */ #define accModule 20 #define accScope 21 #define accNet 25 #define accReg 30 #define accIntegerParam 200 #define accRealParam 202 #define accStringParam 204 #define accParameter 220 #define accTopModule 224 #define accModuleInstance 226 #define accWire 260 #define accNamedEvent 280 #define accIntegerVar 281 #define accRealVar 282 #define accTimeVar 283 #define accIntVar accIntegerVar #define accScalar 300 #define accVector 302 #define accUnknown 412 #define accConstant 600 /* type VALUES FOR t_setval_delay STRUCTURE */ #define accNoDelay 0 #define accInertialDelay 1 #define accTransportDelay 2 #define accPureTransportDelay 3 #define accForceFlag 4 #define accReleaseFlag 5 /* type VALUES FOR t_setval_value STRUCTURE */ #define accBinStrVal 1 #define accOctStrVal 2 #define accDecStrVal 3 #define accHexStrVal 4 #define accScalarVal 5 #define accIntVal 6 #define accRealVal 7 #define accStringVal 8 #define accVectorVal 9 /* Scalar values */ #define acc0 0 #define acc1 1 #define accX 2 #define accZ 3 /* type VALUES FOR t_acc_time STRUCTURE */ #define accTime 1 #define accSimTime 2 #define accRealTime 3 /* reason codes */ #define logic_value_change 1 #define strength_value_change 2 #define real_value_change 3 #define vector_value_change 4 #define event_value_change 5 #define integer_value_change 6 #define time_value_change 7 #define sregister_value_change 8 #define vregister_value_change 9 #define realtime_value_change 10 /* VCL strength values */ #define vclSupply 7 #define vclStrong 6 #define vclPull 5 #define vclLarge 4 #define vclWeak 3 #define vclMedium 2 #define vclSmall 1 #define vclHighZ 0 /* Constants used by acc_vcl_add */ #define vcl_verilog_logic 2 #define VCL_VERILOG_LOGIC vcl_verilog_logic #define vcl_verilog_strength 3 #define VCL_VERILOG_STRENGTH vcl_verilog_strength typedef struct t_acc_time { int type; int low, high; double real; } s_acc_time, *p_acc_time; typedef struct t_setval_delay { s_acc_time time; int model; } s_setval_delay, *p_setval_delay; typedef struct t_acc_vecval { int aval; int bval; } s_acc_vecval, *p_acc_vecval; typedef struct t_setval_value { int format; union { char*str; int scalar; int integer; double real; p_acc_vecval vector; } value; } s_setval_value, *p_setval_value, s_acc_value, *p_acc_value; typedef struct t_strengths { PLI_UBYTE8 logic_value; PLI_UBYTE8 strength1; PLI_UBYTE8 strength2; } s_strengths, *p_strengths; typedef struct t_vc_record { PLI_INT32 vc_reason; PLI_INT32 vc_hightime; PLI_INT32 vc_lowtime; void* user_data; union { PLI_UBYTE8 logic_value; double real_value; handle vector_handle; s_strengths strengths_s; } out_value; } s_vc_record, *p_vc_record; typedef struct t_location { PLI_INT32 line_no; const char*filename; } s_location, *p_location; extern int acc_error_flag; extern int acc_initialize(void); extern void acc_close(void); /* * This is the acc_configure command, and the config_param * codes that are accepted. */ extern int acc_configure(PLI_INT32 config_param, const char*value); #define accEnableArgs 6 #define accDevelopmentVersion 11 extern int acc_fetch_argc(void); extern char**acc_fetch_argv(void); extern PLI_INT32 acc_fetch_direction(handle obj); /* XXXX FIXME: Values returned by acc_fetch_direction */ # define accInout 2 extern char* acc_fetch_fullname(handle obj); extern int acc_fetch_location(p_location loc, handle obj); extern char* acc_fetch_name(handle obj); extern char* acc_fetch_defname(handle obj); extern double acc_fetch_paramval(handle obj); extern double acc_fetch_tfarg(PLI_INT32); extern double acc_fetch_itfarg(PLI_INT32, handle); extern PLI_INT32 acc_fetch_tfarg_int(PLI_INT32); extern PLI_INT32 acc_fetch_itfarg_int(PLI_INT32, handle); extern char* acc_fetch_tfarg_str(PLI_INT32); extern char* acc_fetch_itfarg_str(PLI_INT32, handle); typedef struct t_timescale_info { PLI_INT16 unit; PLI_INT16 precision; } s_timescale_info, *p_timescale_info; extern void acc_fetch_timescale_info(handle obj, p_timescale_info info); extern PLI_INT32 acc_fetch_size(handle obj); extern PLI_INT32 acc_fetch_type(handle obj); extern PLI_INT32 acc_fetch_fulltype(handle obj); extern PLI_INT32 acc_fetch_paramtype(handle obj); extern PLI_INT32 acc_fetch_range(handle object, int *msb, int *lsb); extern const char* acc_fetch_type_str(PLI_INT32 type); extern char* acc_fetch_value(handle obj, const char*fmt, s_acc_value*value); extern handle acc_handle_by_name(const char*name, handle scope); extern handle acc_handle_hiconn(handle port_ref_handle); extern handle acc_handle_object(const char*name); extern handle acc_handle_parent(handle obj); extern handle acc_handle_scope(handle obj); extern handle acc_handle_simulated_net(handle net); extern handle acc_handle_tfarg(int n); extern handle acc_handle_tfinst(void); extern PLI_INT32 acc_compare_handles(handle, handle); extern handle acc_next(PLI_INT32 *, handle, handle); extern handle acc_next_bit(handle ref, handle bit); extern handle acc_next_port(handle ref, handle bit); extern handle acc_next_scope(handle, handle); extern handle acc_next_topmod(handle prev_topmod); extern int acc_object_in_typelist(handle object, PLI_INT32*typelist); extern int acc_object_of_type(handle object, PLI_INT32 type); extern char*acc_product_version(void); extern char*acc_set_scope(handle ref, ...); extern int acc_set_value(handle obj, p_setval_value value, p_setval_delay delay); extern void acc_vcl_add(handle obj, PLI_INT32(*consumer)(p_vc_record), void*data, PLI_INT32 vcl_flag); extern void acc_vcl_delete(handle obj, PLI_INT32(*consumer)(p_vc_record), void*data, PLI_INT32 vcl_flag); extern char* acc_version(void); EXTERN_C_END #endif /* ACC_USER_H */ iverilog-10_1/aclocal.m4000066400000000000000000000152511265551621300152060ustar00rootroot00000000000000 # AX_ENABLE_SUFFIX # ---------------- # Create the configure option --enable-suffix[=suffix] to generate suffix # strings for the installed commands. This allows for shared installs of # different builds. Remember to change the default suffix string to some # value appropriate for the current version. AC_DEFUN([AX_ENABLE_SUFFIX], [AC_ARG_ENABLE([suffix],[AC_HELP_STRING([--enable-suffix], [Use/set the installation command suffix])], [true],[enable_suffix=no]) if test X$enable_suffix = Xyes; then install_suffix='-0.10' elif test X$enable_suffix = Xno; then install_suffix='' else install_suffix="$enable_suffix" fi AC_SUBST(install_suffix) ])# AX_ENABLE_SUFFIX # _AX_C_UNDERSCORES_MATCH_IFELSE(PATTERN, ACTION-IF-MATCH, ACTION-IF-NOMATCH) # ------------------------------ # Sub-macro for AX_C_UNDERSCORES_LEADING and AX_C_UNDERSCORES_TRAILING. # Unwarranted assumptions: # - the object file produced by AC_COMPILE_IFELSE is called "conftest.$ac_objext" # - the nm(1) utility is available, and its name is "nm". AC_DEFUN([_AX_C_UNDERSCORES_MATCH_IF], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([void underscore(void){}])], [AS_IF([nm conftest.$ac_objext|grep $1 >/dev/null 2>/dev/null],[$2],[$3])], [AC_MSG_ERROR([underscore test crashed])] )]) # AX_C_UNDERSCORES_LEADING # --------------------------------- # Check if symbol names in object files produced by C compiler have # leading underscores. Define NEED_LU if so. AC_DEFUN([AX_C_UNDERSCORES_LEADING], [AC_CACHE_CHECK([for leading underscores], ax_cv_c_underscores_leading, [_AX_C_UNDERSCORES_MATCH_IF([_underscore], [AS_VAR_SET(ax_cv_c_underscores_leading, yes)], [AS_VAR_SET(ax_cv_c_underscores_leading, no)])]) if test $ax_cv_c_underscores_leading = yes -a "$CYGWIN" != "yes" -a "$MINGW32" != "yes"; then AC_DEFINE([NEED_LU], [1], [Symbol names in object files produced by C compiler have leading underscores.]) fi ])# AX_C_UNDERSCORES_LEADING # AX_C_UNDERSCORES_TRAILING # --------------------------------- # Check if symbol names in object files produced by C compiler have # trailing underscores. Define NEED_TU if so. AC_DEFUN([AX_C_UNDERSCORES_TRAILING], [AC_CACHE_CHECK([for trailing underscores], ax_cv_c_underscores_trailing, [_AX_C_UNDERSCORES_MATCH_IF([underscore_], [AS_VAR_SET(ax_cv_c_underscores_trailing, yes)], [AS_VAR_SET(ax_cv_c_underscores_trailing, no)])]) if test $ax_cv_c_underscores_trailing = yes; then AC_DEFINE([NEED_TU], [1], [Symbol names in object files produced by C compiler have trailing underscores.]) fi ])# AX_C_UNDERSCORES_TRAILING # AX_WIN32 # -------- # Combined check for several flavors of Microsoft Windows so # their "issues" can be dealt with AC_DEFUN([AX_WIN32], [AC_MSG_CHECKING([for Microsoft Windows]) AC_REQUIRE([AC_CANONICAL_HOST]) []dnl case $host_os in *cygwin*) MINGW32=no; WIN32=yes;; *mingw*) MINGW32=yes; WIN32=yes;; *) MINGW32=no; WIN32=no;; esac AC_SUBST(MINGW32) AC_SUBST(WIN32) AC_MSG_RESULT($WIN32) if test $WIN32 = yes; then AC_MSG_CHECKING([for MinGW]) AC_MSG_RESULT($MINGW32) fi ])# AX_WIN32 # AX_LD_EXTRALIBS # --------------- # mingw needs to link with libiberty.a, but cygwin alone can't tolerate it AC_DEFUN([AX_LD_EXTRALIBS], [AC_MSG_CHECKING([for extra libs needed]) EXTRALIBS= case "${host}" in *-*-cygwin* ) if test "$MINGW32" = "yes"; then EXTRALIBS="-liberty" fi ;; esac AC_SUBST(EXTRALIBS) AC_MSG_RESULT($EXTRALIBS) ])# AX_LD_EXTRALIBS # AX_LD_SHAREDLIB_OPTS # -------------------- # linker options when building a shared library AC_DEFUN([AX_LD_SHAREDLIB_OPTS], [AC_MSG_CHECKING([for shared library link flag]) shared=-shared case "${host}" in *-*-cygwin*) shared="-shared -Wl,--enable-auto-image-base" ;; *-*-mingw*) shared="-shared -Wl,--enable-auto-image-base" ;; *-*-hpux*) shared="-b" ;; *-*-darwin1.[0123]) shared="-bundle -undefined suppress" ;; *-*-darwin*) shared="-bundle -undefined suppress -flat_namespace" ;; *-*-solaris*) if test ${using_sunpro_c} = 1 then shared="-G" fi ;; esac AC_SUBST(shared) AC_MSG_RESULT($shared) ])# AX_LD_SHAREDLIB_OPTS # AX_C_PICFLAG # ------------ # The -fPIC flag is used to tell the compiler to make position # independent code. It is needed when making shared objects. AC_DEFUN([AX_C_PICFLAG], [AC_MSG_CHECKING([for flag to make position independent code]) PICFLAG=-fPIC case "${host}" in *-*-cygwin*) PICFLAG= ;; *-*-mingw*) PICFLAG= ;; *-*-hpux*) PICFLAG=+z ;; *-*-solaris*) if test ${using_sunpro_c} = 1 then PICFLAG=-G fi ;; esac AC_SUBST(PICFLAG) AC_MSG_RESULT($PICFLAG) ])# AX_C_PICFLAG # AX_LD_RDYNAMIC # -------------- # The -rdynamic flag is used by iverilog when compiling the target, # to know how to export symbols of the main program to loadable modules # that are brought in by -ldl AC_DEFUN([AX_LD_RDYNAMIC], [AC_MSG_CHECKING([for -rdynamic compiler flag]) rdynamic=-rdynamic case "${host}" in *-*-netbsd*) rdynamic="-Wl,--export-dynamic" ;; *-*-openbsd*) rdynamic="-Wl,--export-dynamic" ;; *-*-solaris*) rdynamic="" ;; *-*-cygwin*) rdynamic="" ;; *-*-mingw*) rdynamic="" ;; *-*-hpux*) rdynamic="-E" ;; *-*-darwin*) rdynamic="-Wl,-all_load" strip_dynamic="-SX" ;; esac AC_SUBST(rdynamic) AC_MSG_RESULT($rdynamic) AC_SUBST(strip_dynamic) # since we didn't tell them we're "checking", no good place to tell the answer # AC_MSG_RESULT($strip_dynamic) ])# AX_LD_RDYNAMIC # AX_C99_STRTOD # ------------- AC_DEFUN([AX_C99_STRTOD], [# On MinGW we need to jump through hoops to get a C99 compliant strtod(). # mingw-w64 doesn't need this, and the 64-bit version doesn't support it. case "${host}" in *-*-mingw32) LDFLAGS+=" -Wl,--undefined=___strtod,--wrap,strtod,--defsym,___wrap_strtod=___strtod" ;; esac ])# AX_C99_STRTOD # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp file name are based on the header name. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [ _config_header=$1 _stamp_name=stamp-`expr //$_config_header : '.*/\([[^./]]*\)\.[[^./]]*$'`-h echo "timestamp for $_config_header" > `AS_DIRNAME(["$_config_header"])`/[]$_stamp_name ]) #_AC_AM_CONFIG_HEADER_HOOK iverilog-10_1/async.cc000066400000000000000000000050001265551621300147610ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "functor.h" # include "netlist.h" # include bool NetAssign::is_asynchronous() { return true; } bool NetCondit::is_asynchronous() { return false; } /* * NetEvWait statements come from statements of the form @(...) in the * Verilog source. These event waits are considered asynchronous if * all of the events in the list are ANYEDGE, and all the inputs to * the statement are included in the sensitivity list. If any of the * events are posedge or negedge, the statement is synchronous * (i.e. an edge-triggered flip-flop) and if any of the inputs are * unaccounted for in the sensitivity list then the statement is a * latch. */ bool NetEvWait::is_asynchronous() { /* The "sense" set contains the set of Nexa that are in the sensitivity list. We also require that the events are all level sensitive, but the nex_async_ method takes care of that test. */ NexusSet*sense = new NexusSet; for (unsigned idx = 0 ; idx < events_.size() ; idx += 1) { NexusSet*tmp = events_[idx]->nex_async_(); if (tmp == 0) { delete sense; return false; } sense->add(*tmp); delete tmp; } NexusSet*inputs = statement_->nex_input(); if (! sense->contains(*inputs)) { delete sense; delete inputs; return false; } delete sense; delete inputs; /* If it passes all the other tests, then this statement is asynchronous. */ return true; } bool NetProc::is_asynchronous() { return false; } bool NetProcTop::is_asynchronous() const { if (type_ == IVL_PR_INITIAL) return false; return statement_->is_asynchronous(); } iverilog-10_1/attributes.txt000066400000000000000000000055371265551621300163030ustar00rootroot00000000000000 ATTRIBUTE NAMING CONVENTIONS Attributes that are specific to Icarus Verilog, and are intended to be of use to programmers, start with the prefix "ivl_". Attributes with the "_ivl_" prefix are set aside for internal use. They may be generated internally by the compiler. They need not be documented here. ATTRIBUTES TO CONTROL SYNTHESIS The following is a summary of Verilog attributes that Icarus Verilog understands within Verilog source files to control synthesis behavior. This section documents generic synthesis attributes. For target specific attributes, see target specific documentation. These attributes only effect the behavior of the synthesizer. For example, the ivl_combinational will not generate an error message if the Verilog is being compiled for simulation. (It may generate a warning.) * Attributes for "always" and "initial" statements (* ivl_combinational *) This attribute tells the compiler that the statement models combinational logic. If the compiler finds that it cannot make combinational logic out of a marked always statement, it will report an error. This attribute can be used to prevent accidentally inferring latches or flip-flops where the user intended combinational logic. (* ivl_synthesis_on *) This attribute tells the compiler that the marked always statement is synthesizable. The compiler will attempt to synthesize the code in the marked "always" statement. If it cannot in any way synthesize it, then it will report an error. (* ivl_synthesis_off *) If this value is attached to an "always" statement, then the compiler will *not* synthesize the "always" statement. This can be used, for example, to mark embedded test bench code. * Attributes for modules (* ivl_synthesis_cell *) If this value is attached to a module during synthesis, that module will be considered a target architecture primitive, and its interior will not be synthesized further. The module can therefore hold a model for simulation purposes. * Attributes for signals (wire/reg/integer/tri/etc.) (* PAD = "" *) If this attribute is attached to a signal that happens to be a root module port, then targets that support it will use the string value as a list of pin assignments for the port/signal. The format is a comma separated list of location tokens, with the format of the token itself defined by the back-end tools in use. * Other Attributes [ none defined yet ] MISC (* _ivl_schedule_push *) If this attribute is attached to a thread object (always or initial statement) then the vvp code generator will generate code that causes the scheduler to push this thread at compile time. The compiler may internally add this attribute to always statements if it detects that it is combinational. This helps resolve time-0 races. iverilog-10_1/autoconf.sh000066400000000000000000000011641265551621300155160ustar00rootroot00000000000000#!/bin/sh # # This shell script exists to run autoconf on source distributions # that are pulled from git The configure script is not included # in git, so it is easiest to just run this script whenever needed # to generate the configure script. # echo "Autoconf in root..." autoconf -f echo "Precompiling lexor_keyword.gperf" gperf -o -i 7 -C -k 1-4,6,9,\$ -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf > lexor_keyword.cc echo "Precompiling vhdlpp/lexor_keyword.gperf" (cd vhdlpp ; gperf -o -i 7 --ignore-case -C -k 1-4,6,9,\$ -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf > lexor_keyword.cc ) iverilog-10_1/cadpli/000077500000000000000000000000001265551621300145765ustar00rootroot00000000000000iverilog-10_1/cadpli/Makefile.in000066400000000000000000000044631265551621300166520ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include vpidir = @libdir@/ivl$(suffix) CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = cadpli.o all: dep cadpli.vpl $(ALL32) check: all clean: rm -rf *.o dep cadpli.vpl distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=cadpli/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< mv $*.d dep SYSTEM_VPI_LDFLAGS = -L../vvp -lvpi ifeq (@MINGW32@,yes) SYSTEM_VPI_LDFLAGS += @EXTRALIBS@ endif cadpli.vpl: $O ../vvp/libvpi.a ../libveriuser/libveriuser.o $(CC) @shared@ $(LDFLAGS) -o $@ $O ../libveriuser/libveriuser.o $(SYSTEM_VPI_LDFLAGS) install: all installdirs $(vpidir)/cadpli.vpl $(vpidir)/cadpli.vpl: ./cadpli.vpl $(INSTALL_PROGRAM) ./cadpli.vpl "$(DESTDIR)$(vpidir)/cadpli.vpl" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(vpidir)" uninstall: $(UNINSTALL32) rm -f "$(DESTDIR)$(vpidir)/cadpli.vpl" uninstall32: -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/cadpli/cadpli.c000066400000000000000000000041701265551621300162000ustar00rootroot00000000000000/* * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include # include "config.h" # include "ivl_dlfcn.h" # include "ivl_alloc.h" typedef void* (*funcvp)(void); static void thunker_register(void) { struct t_vpi_vlog_info vlog_info; void*mod; void*boot; struct t_tfcell*tf; int idx; vpi_get_vlog_info(&vlog_info); for (idx = 0 ; idx < vlog_info.argc ; idx += 1) { char*module, *cp, *bp; if (strncmp("-cadpli=", vlog_info.argv[idx], 8) != 0) continue; cp = vlog_info.argv[idx] + 8; assert(cp); bp = strchr(cp, ':'); assert(bp); module = malloc(bp-cp+1); strncpy(module, cp, bp-cp); module[bp-cp] = 0; mod = ivl_dlopen(module); if (mod == 0) { vpi_printf("%s link: %s\n", vlog_info.argv[idx], dlerror()); free(module); continue; } bp += 1; boot = ivl_dlsym(mod, bp); if (boot == 0) { vpi_printf("%s: Symbol %s not found.\n", vlog_info.argv[idx], bp); free(module); continue; } free(module); assert(boot); tf = (*((funcvp)boot))(); assert(tf); veriusertfs_register_table(tf); } } void (*vlog_startup_routines[])(void) = { thunker_register, 0 }; iverilog-10_1/cadpli/cadpli.txt000066400000000000000000000032011265551621300165670ustar00rootroot00000000000000 CADENCE PLI1 MODULES Copyright 2003 Stephen Williams With the cadpli module, Icarus Verilog is able to load PLI1 applications that were compiled and linked to be dynamic loaded by Verilog-XL or NC-Verilog. This allows Icarus Verilog users to run third-party modules that were compiled to interface with XL or NC. Obviously, this only works on the operating system that the PLI application was compiled to run on. For example, a Linux module can only be loaded and run under Linux. Icarus Verilog uses an interface module, the "cadpli" module, to connect the worlds. This module is installed with Icarus Verilog, and is invoked by the usual -m flag to iverilog or vvp. This module in turn scans the extended arguments, looking for +cadpli= arguments. The latter specify the share object and bootstrap function for running the module. For example, to run the module product.so, that has the bootstrap function "my_boot": vvp -mcadpli a.out -cadpli=./product.so:my_boot The "-mcadpli" argument causes vvp to load the cadpli.vpl library module. This activates the -cadpli= argument interpreter. The -cadpli=: argument, then, causes vvp, through the cadpli module, to load the loadable PLI application, invoke the my_boot function to get a veriusertfs table, and scan that table to register the system tasks and functions exported by that object. The format of the -cadpli= extended argument is essentially the same as the +loadpli1= argument to Verilog-XL. The integration from this point is seamless. The PLI application hardly knows that it is being invoked by Icarus Verilog instead of Verilog-XL, so operates as it would otherwise. iverilog-10_1/cadpli/ivl_dlfcn.h000066400000000000000000000052321265551621300167110ustar00rootroot00000000000000#ifndef IVL_ivl_dlfcn_H #define IVL_ivl_dlfcn_H /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined(__MINGW32__) # include # include typedef void * ivl_dll_t; #elif defined(HAVE_DLFCN_H) # include typedef void* ivl_dll_t; #elif defined(HAVE_DL_H) # include typedef shl_t ivl_dll_t; #endif #if defined(__MINGW32__) static __inline__ ivl_dll_t ivl_dlopen(const char *name) { return (void *)LoadLibrary(name); } static __inline__ void *ivl_dlsym(ivl_dll_t dll, const char *nm) { return (void *)GetProcAddress((HINSTANCE)dll,nm);} static __inline__ void ivl_dlclose(ivl_dll_t dll) { (void)FreeLibrary((HINSTANCE)dll);} static __inline__ const char *dlerror(void) { static char msg[256]; unsigned long err = GetLastError(); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &msg, sizeof(msg) - 1, NULL ); return msg; } #elif defined(HAVE_DLFCN_H) static __inline__ ivl_dll_t ivl_dlopen(const char*name) { return dlopen(name,RTLD_LAZY); } static __inline__ void* ivl_dlsym(ivl_dll_t dll, const char*nm) { void*sym = dlsym(dll, nm); /* Not found? try without the leading _ */ if (sym == 0 && nm[0] == '_') sym = dlsym(dll, nm+1); return sym; } static __inline__ void ivl_dlclose(ivl_dll_t dll) { dlclose(dll); } #elif defined(HAVE_DL_H) static __inline__ ivl_dll_t ivl_dlopen(const char*name) { return shl_load(name, BIND_IMMEDIATE, 0); } static __inline__ void* ivl_dlsym(ivl_dll_t dll, const char*nm) { void*sym; int rc = shl_findsym(&dll, nm, TYPE_PROCEDURE, &sym); return (rc == 0) ? sym : 0; } static __inline__ void ivl_dlclose(ivl_dll_t dll) { shl_unload(dll); } static __inline__ const char*dlerror(void) { return strerror( errno ); } #endif #endif /* IVL_ivl_dlfcn_H */ iverilog-10_1/check.conf000066400000000000000000000000771265551621300152720ustar00rootroot00000000000000functor:cprop functor:nodangle -t:dll flag:DLL=tgt-vvp/vvp.tgt iverilog-10_1/compiler.h000066400000000000000000000213071265551621300153300ustar00rootroot00000000000000#ifndef IVL_compiler_H #define IVL_compiler_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include "netlist.h" # include "StringHeap.h" /* * This defines constants and defaults for the compiler in general. */ /* * The integer_width is the width of integer variables. This is also * the minimum width of unsized integers when they are found in * self-determined contexts. */ extern unsigned integer_width; /* * The width_cap is the width limit for unsized expressions. */ extern unsigned width_cap; /* * This is the maximum number of recursive module loops allowed within * a generate block. */ extern unsigned recursive_mod_limit; /* The TIME_WIDTH is the width of time variables. */ #ifndef TIME_WIDTH # define TIME_WIDTH 64 #endif /* * When doing dynamic linking, we need a uniform way to identify the * symbol. Some compilers put leading _, some trailing _. The * configure script figures out which is the local convention and * defines NEED_LU and NEED_TU as required. */ #ifdef NEED_LU #define LU "_" #else #define LU "" #endif #ifdef NEED_TU #define TU "_" #else #define TU "" #endif /* * These are flags to enable various sorts of warnings. By default all * the warnings are off, the -W parameter arranges for each to be * enabled. */ /* Implicit definitions of wires. */ extern bool warn_implicit; /* Warn if dimensions of port or var/net are implicitly taken from the input/output/inout declaration. */ extern bool warn_implicit_dimensions; /* inherit timescales across files. */ extern bool warn_timescale; /* Warn about legal but questionable module port bindings. */ extern bool warn_portbinding; /* Warn about constant out of bound selects. */ extern bool warn_ob_select; /* Warn about structures that may have infinite loops. */ extern bool warn_inf_loop; /* Warn about always @* statements where a part or word select causes sensitivity to an entire vector or array. */ extern bool warn_sens_entire_vec; extern bool warn_sens_entire_arr; /* Warn about level-appropriate anachronisms. */ extern bool warn_anachronisms; /* This is true if verbose output is requested. */ extern bool verbose_flag; extern bool debug_scopes; extern bool debug_eval_tree; extern bool debug_elaborate; extern bool debug_emit; extern bool debug_synth2; extern bool debug_optimizer; /* Control evaluation of functions at compile time: * 0 = only for functions in constant expressions * 1 = only for automatic functions * 2 = for all functions * Level 2 should only be used if the user can guarantee that a * function's local variables are never accessed from outside the * function. */ extern unsigned opt_const_func; /* Possibly temporary flag to control virtualization of pin arrays */ extern bool disable_virtual_pins; /* The vlog95 code generator does not want the compiler to generate concat-Z * LPM objects so this flag is used to block them from being generated. */ extern bool disable_concatz_generation; /* Limit to size of devirtualized arrays */ extern unsigned long array_size_limit; /* Path to a directory useful for finding subcomponents. */ extern const char*basedir; /* This is an ordered list of library suffixes to search. */ extern listlibrary_suff; extern int build_library_index(const char*path, bool key_case_sensitive); /* This is the generation of Verilog that the compiler is asked to support. Then there are also more detailed controls for more specific language features. */ enum generation_t { GN_VER1995 = 1, GN_VER2001_NOCONFIG = 2, GN_VER2001 = 3, GN_VER2005 = 4, GN_VER2005_SV = 5, GN_VER2009 = 6, GN_VER2012 = 7, GN_DEFAULT = 4 }; extern generation_t generation_flag; /* If this flag is true, enable extended types support. */ extern bool gn_cadence_types_flag; /* If this flag is true, enable miscellaneous extensions. */ extern bool gn_icarus_misc_flag; /* If this flag is true, then elaborate specify blocks. If this flag is false, then skip elaboration of specify behavior. */ extern bool gn_specify_blocks_flag; /* If this flag is true, then elaborate assertions. If this flag is false, then stub out assertion statements. */ extern bool gn_assertions_flag; /* If this flag is true, then support/elaborate Verilog-AMS. */ extern bool gn_verilog_ams_flag; /* If this flag is false a warning is printed when the port declaration is scalar and the net/register definition is vectored. */ extern bool gn_io_range_error_flag; /* If this flag is true, then force re-evaluation of user functions in a continuous assignment when any part of the expression is re-evaluated. */ extern bool gn_strict_ca_eval_flag; /* If this flag is true, then force strict conformance to the IEEE standard expression width rules. */ extern bool gn_strict_expr_width_flag; /* If variables can be converted to uwires by a continuous assignment (assuming no procedural assign, then return true. This will be true for SystemVerilog */ static inline bool gn_var_can_be_uwire(void) { if (generation_flag == GN_VER2005_SV || generation_flag == GN_VER2009 || generation_flag == GN_VER2012) return true; return false; } static inline bool gn_system_verilog(void) { if (generation_flag == GN_VER2005_SV || generation_flag == GN_VER2009 || generation_flag == GN_VER2012) return true; return false; } static inline bool gn_modules_nest(void) { return gn_system_verilog(); } /* The bits of these GN_KEYWORDS_* constants define non-intersecting sets of keywords. The compiler enables groups of keywords by setting lexor_keyword_mask with the OR of the bits for the keywords to be enabled. */ enum { GN_KEYWORDS_1364_1995 = 0x0001, GN_KEYWORDS_1364_2001 = 0x0002, GN_KEYWORDS_1364_2001_CONFIG = 0x0004, GN_KEYWORDS_1364_2005 = 0x0008, GN_KEYWORDS_VAMS_2_3 = 0x0010, GN_KEYWORDS_1800_2005 = 0x0020, GN_KEYWORDS_1800_2009 = 0x0040, GN_KEYWORDS_1800_2012 = 0x0080, GN_KEYWORDS_ICARUS = 0x8000 }; extern int lexor_keyword_mask; /* This is the string to use to invoke the preprocessor. */ extern char*ivlpp_string; extern map missing_modules; /* Files that are library files are in this map. The lexor compares file names as it processes `line directives, and if the file name matches an entry in this table, it will turn on the library_active_flag so that modules know that they are in a library. */ extern map library_file_map; /* * the lex_strings are perm_strings made up of tokens from the source * file. Identifiers are so likely to be used many times that it makes * much sense to use a StringHeapLex to hold them. */ extern StringHeapLex lex_strings; /* * The ivl_target.h API in a variety of places keeps strings of * bits. Manage these as perm_string in a StringHeap. */ extern StringHeapLex bits_strings; /* * The filename_strings are perm_strings for file names. They are put * into their own StringHeapLex because these paths are used a *lot* * and this makes them more sure to hash together. */ extern StringHeapLex filename_strings; /* * system task/function listings. */ /* * This table describes all the return values of various system * functions. This table is used to elaborate expressions that are * system function calls. */ struct sfunc_return_type { const char* name; ivl_variable_type_t type; unsigned wid; int signed_flag; }; extern const struct sfunc_return_type* lookup_sys_func(const char*name); extern int load_sys_func_table(const char*path); extern void cleanup_sys_func_table(); /* * In system Verilog it is allowed with a warning to call a function * as a task. You can even cast the return value away and have no * warning message. */ extern ivl_sfunc_as_task_t def_sfunc_as_task; #endif /* IVL_compiler_H */ iverilog-10_1/config.guess000077500000000000000000001246371265551621300156770ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2015 Free Software Foundation, Inc. timestamp='2015-03-04' # This file 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 3 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, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || \ echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "${UNAME_MACHINE_ARCH}" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; e2k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: iverilog-10_1/config.h.in000066400000000000000000000035761265551621300154000ustar00rootroot00000000000000#ifndef IVL_config_H /* -*- c++ -*- */ #define IVL_config_H /* * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined(__cplusplus) # if !defined(__GNUC__) using namespace std; # elif (__GNUC__ == 3) using namespace std; # endif #endif # undef NEED_LU # undef NEED_TU # undef WLU # undef WTU # undef HAVE_TIMES # undef HAVE_IOSFWD # undef HAVE_GETOPT_H # undef HAVE_INTTYPES_H # undef HAVE_LIBIBERTY_H # undef HAVE_DLFCN_H # undef HAVE_DL_H # undef HAVE_FCHMOD # undef HAVE_LIBREADLINE # undef HAVE_LIBZ # undef HAVE_LIBBZ2 # undef HAVE_LROUND # undef HAVE_SYS_WAIT_H # undef WORDS_BIGENDIAN #ifdef HAVE_INTTYPES_H # include #endif /* These two are needed by the lxt and lxt2 files (copied from GTKWave). */ # undef HAVE_ALLOCA_H # undef HAVE_FSEEKO /* And this is needed by the fst files (copied from GTKWave). */ # undef HAVE_LIBPTHREAD # undef HAVE_REALPATH /* * Define this if you want to compile vvp with memory freeing and * special valgrind hooks for the memory pools. */ # undef CHECK_WITH_VALGRIND #endif /* IVL_config_H */ iverilog-10_1/config.sub000077500000000000000000001063671265551621300153420ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2015 Free Software Foundation, Inc. timestamp='2015-03-08' # This file 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 3 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, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: iverilog-10_1/configure.in000066400000000000000000000235411265551621300156600ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script. AC_INIT(netlist.h) AC_CONFIG_HEADER(config.h) AC_CONFIG_HEADER(_pli_types.h) AC_CONFIG_HEADER(vhdlpp/vhdlpp_config.h) AC_CONFIG_HEADER(vvp/config.h) AC_CONFIG_HEADER(vpi/vpi_config.h) AC_CONFIG_HEADER(libveriuser/config.h) AC_CONFIG_HEADER(tgt-vvp/vvp_config.h) AC_CONFIG_HEADER(tgt-vhdl/vhdl_config.h) AC_CONFIG_HEADER(tgt-pcb/pcb_config.h) AC_CANONICAL_HOST dnl Checks for programs. AC_PROG_CC # AC_PROG_CC_C99 is only available in autoconf version 2.60 and later. AC_PREREQ([2.60]) AC_PROG_CC_C99 AC_PROG_CXX AC_PROG_RANLIB AC_CHECK_TOOL(LD, ld, false) AC_CHECK_TOOL(AR, ar, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(STRIP, strip, true) AC_CHECK_TOOL(WINDRES,windres,false) AC_CHECK_PROGS(XGPERF,gperf,none) AC_CHECK_PROGS(MAN,man,none) AC_CHECK_PROGS(PS2PDF,ps2pdf,none) AC_CHECK_PROGS(GIT,git,none) if test "$XGPERF" = "none" then echo "" echo "*** Warning: No suitable gperf found. ***" echo " The gperf package is essential for building ivl from" echo " git sources, or modifying the parse engine of ivl itself." echo " You can get away without it when simply building from" echo " snapshots or major releases." echo "" fi AC_CHECK_PROGS(LEX,flex,none) if test "$LEX" = "none" then echo "*** Error: No suitable flex found. ***" echo " Please install the 'flex' package." exit 1 fi AC_CHECK_PROGS(YACC,bison,none) if test "$YACC" = "none" then echo "*** Error: No suitable bison found. ***" echo " Please install the 'bison' package." exit 1 fi AC_EXEEXT AC_SUBST(EXEEXT) # Combined check for Microsoft-related bogosities; sets WIN32 if found AX_WIN32 # Check to see if we are using the Sun compiler. If so then configure # some of the flags to match the Sun compiler syntax. This is also used # in the aclocal.m4 file to configure the flags used to build and link # dynamic libraries AC_CHECK_DECL(__SUNPRO_C, using_sunpro_c=1, using_sunpro_c=0) if test ${using_sunpro_c} = 1 then AC_SUBST(DEPENDENCY_FLAG, [-xMMD]) AC_SUBST(WARNING_FLAGS, [""]) AC_SUBST(WARNING_FLAGS_CXX, [""]) else # Check to see if -Wextra is supported. iverilog_temp_cflags="$CFLAGS" CFLAGS="-Wextra $CFLAGS" AC_MSG_CHECKING(if gcc supports -Wextra) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [[iverilog_wextra_flag="-Wextra";] AC_MSG_RESULT(yes)], [[iverilog_wextra_flag="-W";] AC_MSG_RESULT(no)]) CFLAGS="$iverilog_temp_cflags" AC_SUBST(DEPENDENCY_FLAG, [-MD]) AC_SUBST(WARNING_FLAGS, ["-Wall $iverilog_wextra_flag -Wshadow"]) AC_SUBST(WARNING_FLAGS_CC, ["-Wstrict-prototypes"]) AC_SUBST(WARNING_FLAGS_CXX, [""]) fi AC_LANG(C++) AC_ARG_WITH([m32], [AC_HELP_STRING([--with-m32], [Compile 32-bit on x86_64])], [ with_m32=yes ],[ with_m32=no ]) AS_IF( [test "x$with_m32" = xyes], [ AC_MSG_NOTICE([Compiling for 32-bit environment - needs gcc on x86_64]) LDTARGETFLAGS="-m elf_i386" CTARGETFLAGS="-m32" ], []) CFLAGS="$CTARGETFLAGS $CFLAGS" CXXFLAGS="$CTARGETFLAGS $CXXFLAGS" LDFLAGS="$CTARGETFLAGS $LDFLAGS" # Check that we are using either the GNU compilers or the Sun compilers # but not a mixture of the two (not currently supported). AC_CHECK_DECL(__SUNPRO_CC, using_sunpro_cc=1, using_sunpro_cc=0) if test ${using_sunpro_c} = 1 then if test ${using_sunpro_cc} = 0 then echo "*** Error: No support for mixing GNU and Sun compilers. ***" echo " Using Sun C compiler and GNU C++ compiler.." exit 1 fi else if test ${using_sunpro_cc} = 1 then echo "*** Error: No support for mixing GNU and Sun compilers. ***" echo " Using GNU C compiler and Sun C++ compiler.." exit 1 fi fi iverilog_temp_cxxflags="$CXXFLAGS" CXXFLAGS="-DHAVE_DECL_BASENAME $CXXFLAGS" AC_CHECK_HEADERS(getopt.h inttypes.h libiberty.h iosfwd sys/wait.h) CXXFLAGS="$iverilog_temp_cxxflags" AC_CHECK_SIZEOF(unsigned long long) AC_CHECK_SIZEOF(unsigned long) AC_CHECK_SIZEOF(unsigned) # vvp uses these... AC_CHECK_LIB(termcap, tputs) AC_CHECK_LIB(readline, readline) AC_CHECK_LIB(history, add_history) AC_CHECK_HEADERS(readline/readline.h readline/history.h sys/resource.h) case "${host}" in *linux*) AC_DEFINE([LINUX], [1], [Host operating system is Linux.]) ;; esac # vpi uses these AC_CHECK_LIB(pthread, pthread_create) AC_CHECK_LIB(z, gzwrite) AC_CHECK_LIB(z, gzwrite, HAVE_LIBZ=yes, HAVE_LIBZ=no) AC_SUBST(HAVE_LIBZ) if test "$WIN32" = "yes"; then AC_CHECK_LIB(bz2, main) AC_CHECK_LIB(bz2, main, HAVE_LIBBZ2=yes, HAVE_LIBBZ2=no) else AC_CHECK_LIB(bz2, BZ2_bzdopen) AC_CHECK_LIB(bz2, BZ2_bzdopen, HAVE_LIBBZ2=yes, HAVE_LIBBZ2=no) fi AC_SUBST(HAVE_LIBBZ2) # The lxt/lxt2 files from GTKWave use these... AC_FUNC_ALLOCA AC_FUNC_FSEEKO # valgrind checks AC_ARG_WITH([valgrind], [AC_HELP_STRING([--with-valgrind], [Add valgrind hooks])], [], [check_valgrind=yes]) AS_IF([test "x$check_valgrind" = xyes], [AC_MSG_NOTICE([Not using valgrind hooks])], [AC_CHECK_HEADER([valgrind/memcheck.h], [AC_DEFINE([CHECK_WITH_VALGRIND], [1], [Define to one to use the valgrind hooks])], [AC_MSG_ERROR([Could not find ])])]) AC_MSG_CHECKING(for sys/times) AC_TRY_LINK( #include #include ,{clock_t a = times(0)/sysconf(_SC_CLK_TCK);}, do_times=yes AC_DEFINE([HAVE_TIMES], [1], [The times system call is available in the host operating system.]), do_times=no ) AC_MSG_RESULT($do_times) # -- # Look for a dl library to use. First look for the standard dlopen # functions, and failing that look for the HP specific shl_load function. AC_CHECK_HEADERS(dlfcn.h dl.h, break) DLLIB='' AC_CHECK_LIB(dl,dlopen,[DLLIB=-ldl]) if test -z "$DLLIB" ; then AC_CHECK_LIB(dld,shl_load,[DLLIB=-ldld]) fi AC_SUBST(DLLIB) AC_SUBST(LDRELOCFLAGS) AC_SUBST(CTARGETFLAGS) AC_SUBST(LDTARGETFLAGS) AC_PROG_INSTALL AC_LANG(C) AC_C_BIGENDIAN # $host AX_ENABLE_SUFFIX AX_LD_EXTRALIBS # Compiler option for position independent code, needed when making shared objects. # CFLAGS inherited by cadpli/Makefile? AX_C_PICFLAG # may modify LDFLAGS AX_C99_STRTOD # Processor specific compile flags case "${host}" in alpha*-*-linux*) CPPFLAGS="-mieee $CPPFLAGS" CFLAGS="-mieee $CFLAGS" ;; *-*-mingw*) CXXFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CXXFLAGS" CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CFLAGS" ;; esac # Do some more operating system specific setup. We put the file64_support # define in a substitution instead of simply a define because there # are source files (namely lxt support files) that don't include any # config.h header file. file64_support='' case "${host}" in *-*-linux*) AC_DEFINE([_LARGEFILE_SOURCE], [1], [Indicates LFS (i.e. the ability to create files larger than 2 GiB on 32-bit operating systems).]) file64_support='-D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64' ;; esac AC_SUBST(file64_support) # fstapi.c (from GTKWave) needs this define. AC_CHECK_FUNCS(realpath) # Check that these functions exist. They are mostly C99 # functions that older compilers may not yet support. AC_CHECK_FUNCS(fopen64) # The following math functions may be defined in the math library so look # in the default libraries first and then look in -lm for them. On some # systems we may need to use the compiler in C99 mode to get a definition. # We requested C99 mode earlier with AC_PROG_CC_C99. AC_SEARCH_LIBS([lround], [m], [AC_DEFINE([HAVE_LROUND], [1])]) AC_SEARCH_LIBS([llround], [m], [AC_DEFINE([HAVE_LLROUND], [1])]) AC_SEARCH_LIBS([nan], [m], [AC_DEFINE([HAVE_NAN], [1])]) AC_SEARCH_LIBS([fmin], [m], [AC_DEFINE([HAVE_FMIN], [1])]) AC_SEARCH_LIBS([fmax], [m], [AC_DEFINE([HAVE_FMAX], [1])]) # Check to see if an unsigned long and uint64_t are the same from # a compiler perspective. We can not just check that they are the # same size since unsigned long and unsigned long long are not the # same from an overloading perspective even though they could be # the same size on some 64 bit machines. The result from this test # is only used if inttypes.h is available, so if the test fails for # that reason we don't care. AC_LANG(C++) AC_MSG_CHECKING(if uint64_t and unsigned long are identical) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "inttypes.h" static bool check(unsigned long val) { return val != 0; } static bool check(uint64_t val) { return val != 0; }]], [[unsigned long ulval = 1; bool result = check(ulval); uint64_t uival = 1; result &= check(uival); return !result;]])], [AC_MSG_RESULT(no)], [AC_DEFINE([UINT64_T_AND_ULONG_SAME], [1]) AC_MSG_RESULT(yes)]) # Linker option used when compiling the target AX_LD_RDYNAMIC # linker options when building a shared library AX_LD_SHAREDLIB_OPTS ####################### ## test for underscores. The vpi module loader needs to know this ## in order to know the name of the start symbol for the .vpi module. ####################### AX_C_UNDERSCORES_LEADING AX_C_UNDERSCORES_TRAILING ####################### ## end of test for underscores ####################### ####################### # Sanity check the configured results ####################### AC_MSG_CHECKING(for sanity of prefix) if test `echo "$prefix" | wc -w` != 1 then AC_MSG_ERROR(cannot configure white space in prefix: $prefix) fi AC_MSG_RESULT(ok) AC_MSG_CHECKING(for sanity of exec_prefix) if test `echo "$exec_prefix" | wc -w` != 1 then AC_MSG_ERROR(cannot configure white space in exec_prefix: $exec_prefix) fi AC_MSG_RESULT(ok) AC_MSG_CHECKING(for sanity of libdir) if test `echo "$libdir" | wc -w` != 1 then AC_MSG_ERROR(cannot configure white space in libdir: $libdir) fi AC_MSG_RESULT(ok) AC_OUTPUT(Makefile ivlpp/Makefile vhdlpp/Makefile vvp/Makefile vpi/Makefile driver/Makefile driver-vpi/Makefile cadpli/Makefile libveriuser/Makefile tgt-null/Makefile tgt-stub/Makefile tgt-vvp/Makefile tgt-vhdl/Makefile tgt-fpga/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vlog95/Makefile tgt-pcb/Makefile tgt-blif/Makefile tgt-sizer/Makefile) iverilog-10_1/constants.vams000066400000000000000000000025411265551621300162500ustar00rootroot00000000000000// Mathematical and physical constants `ifdef CONSTANTS_VAMS `else `define CONSTANTS_VAMS 1 // M_ is a mathematical constant `define M_E 2.7182818284590452354 `define M_LOG2E 1.4426950408889634074 `define M_LOG10E 0.43429448190325182765 `define M_LN2 0.69314718055994530942 `define M_LN10 2.30258509299404568402 `define M_PI 3.14159265358979323846 `define M_TWO_PI 6.28318530717958647693 `define M_PI_2 1.57079632679489661923 `define M_PI_4 0.78539816339744830962 `define M_1_PI 0.31830988618379067154 `define M_2_PI 0.63661977236758134308 `define M_2_SQRTPI 1.12837916709551257390 `define M_SQRT2 1.41421356237309504880 `define M_SQRT1_2 0.70710678118654752440 /* * Do we need these? For now they are not available. * // The following constants have been taken from http://physics.nist.gov // P_ is a physical constant // charge of electron in coulombs `define P_Q 1.602176462e-19 // speed of light in vacuum in meters/sec `define P_C 2.99792458e8 // Boltzmann's constant in joules/kelvin `define P_K 1.3806503e-23 // Planck's constant in joules*sec `define P_H 6.62606876e-34 // permittivity of vacuum in farads/meter `define P_EPS0 8.854187817e-12 // permeability of vacuum in henrys/meter `define P_U0 (4.0e-7 * `M_PI) // zero celsius in kelvin `define P_CELSIUS0 273.15 */ `endif iverilog-10_1/cppcheck.sup000066400000000000000000000001751265551621300156560ustar00rootroot00000000000000// These are correct and are used to find the base (zero) pin. thisSubtraction:netlist.h:4976 thisSubtraction:netlist.h:4985 iverilog-10_1/cprop.cc000066400000000000000000000323061265551621300150000ustar00rootroot00000000000000/* * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include "netlist.h" # include "netmisc.h" # include "functor.h" # include "compiler.h" # include "ivl_assert.h" /* * The cprop function below invokes constant propagation where * possible. The elaboration generates NetConst objects. I can remove * these and replace the gates connected to it with simpler ones. I * may even be able to replace nets with a new constant. */ struct cprop_functor : public functor_t { unsigned count; virtual void signal(Design*des, NetNet*obj); virtual void lpm_add_sub(Design*des, NetAddSub*obj); virtual void lpm_compare(Design*des, NetCompare*obj); virtual void lpm_concat(Design*des, NetConcat*obj); virtual void lpm_ff(Design*des, NetFF*obj); virtual void lpm_logic(Design*des, NetLogic*obj); virtual void lpm_mux(Design*des, NetMux*obj); virtual void lpm_part_select(Design*des, NetPartSelect*obj); void lpm_compare_eq_(Design*des, NetCompare*obj); }; void cprop_functor::signal(Design*, NetNet*) { } void cprop_functor::lpm_add_sub(Design*, NetAddSub*) { } void cprop_functor::lpm_compare(Design*des, NetCompare*obj) { if (obj->pin_AEB().is_linked()) { assert( ! obj->pin_AGB().is_linked() ); assert( ! obj->pin_AGEB().is_linked() ); assert( ! obj->pin_ALB().is_linked() ); assert( ! obj->pin_ALEB().is_linked() ); assert( ! obj->pin_AGB().is_linked() ); assert( ! obj->pin_ANEB().is_linked() ); lpm_compare_eq_(des, obj); return; } } void cprop_functor::lpm_compare_eq_(Design*, NetCompare*) { } void cprop_functor::lpm_concat(Design*des, NetConcat*obj) { // Sorry, I don't know how to constant-propagate through // transparent concatenations. if (obj->transparent()) return; verinum result (verinum::Vz, obj->width()); unsigned off = 0; for (unsigned idx = 1 ; idx < obj->pin_count() ; idx += 1) { Nexus*nex = obj->pin(idx).nexus(); // If there are non-constant drivers, then give up. if (! nex->drivers_constant()) return; verinum tmp = nex->driven_vector(); result.set(off, tmp); off += tmp.len(); } if (debug_optimizer) cerr << obj->get_fileline() << ": cprop_functor::lpm_concat: " << "Replace NetConcat with " << result << "." << endl; NetScope*scope = obj->scope(); // Create a NetConst object to carry the result. Give it the // same name as the Concat object that we are replacing, and // link the NetConst to the NetConcat object. Then delete the // concat that is now replaced. NetConst*result_obj = new NetConst(scope, obj->name(), result); result_obj->set_line(*obj); des->add_node(result_obj); connect(obj->pin(0), result_obj->pin(0)); // Note that this will leave the const inputs to dangle. They // will be reaped by other passes of cprop_functor. delete obj; count += 1; } void cprop_functor::lpm_ff(Design*, NetFF*obj) { // Look for and count unlinked FF outputs. Note that if the // Data and Q pins are connected together, they can be removed // from the circuit, since it doesn't do anything. if (connected(obj->pin_Data(), obj->pin_Q()) && (! obj->pin_Sclr().is_linked()) && (! obj->pin_Sset().is_linked()) && (! obj->pin_Aclr().is_linked()) && (! obj->pin_Aset().is_linked())) { obj->pin_Data().unlink(); obj->pin_Q().unlink(); delete obj; } } void cprop_functor::lpm_logic(Design*, NetLogic*) { } /* * This detects the case where the mux selects between a value and * Vz. In this case, replace the device with a mos with the sel * input used to enable the output. */ void cprop_functor::lpm_mux(Design*des, NetMux*obj) { if (obj->size() != 2) return; if (obj->sel_width() != 1) return; Nexus*sel_nex = obj->pin_Sel().nexus(); /* If the select input is constant, then replace with a BUFZ */ // If the select is not constant, there is nothing we can do. if (! sel_nex->drivers_constant()) return; // If the constant select is 'bz or 'bx, then give up. verinum::V sel_val = sel_nex->driven_value(); if (sel_val == verinum::Vz || sel_val == verinum::Vx) return; // The Select input must be a defined constant value, so we // can replace the device with a BUFZ. NetBUFZ*tmp = new NetBUFZ(obj->scope(), obj->name(), obj->width(), true); tmp->set_line(*obj); if (debug_optimizer) cerr << obj->get_fileline() << ": debug: " << "Replace binary MUX with constant select=" << sel_val << " with a BUFZ to the selected input." << endl; tmp->rise_time(obj->rise_time()); tmp->fall_time(obj->fall_time()); tmp->decay_time(obj->decay_time()); connect(tmp->pin(0), obj->pin_Result()); if (sel_val == verinum::V1) connect(tmp->pin(1), obj->pin_Data(1)); else connect(tmp->pin(1), obj->pin_Data(0)); delete obj; des->add_node(tmp); count += 1; } static bool compare_base(NetPartSelect*a, NetPartSelect*b) { return a->base() < b->base(); } /* * This optimization searches for Nexa that are driven only by * NetPartSelect(PV) outputs. These might turn from Verilog input that * looks like this: * wire [7:0] foo * assign foo[7:4] = a; * assign foo[3:0] = b; * The idea is to convert the part selects of the above to a single * concatenation that looks like this: * assign foo = {a, b}; */ void cprop_functor::lpm_part_select(Design*des, NetPartSelect*obj) { if (obj->dir() != NetPartSelect::PV) return; NetScope*scope = obj->scope(); Nexus*nex = obj->pin(1).nexus(); vector obj_set; for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { // If this is an input (or passive) then ignore it. if (cur->get_dir() != Link::OUTPUT) continue; // Check to see if this is the output of a // NetPartSelect::PV. If not, then give up on the blend. NetPins*tmp_obj = cur->get_obj(); unsigned tmp_pin = cur->get_pin(); NetPartSelect*cur_obj = dynamic_cast (tmp_obj); if (cur_obj == 0) return; if (cur_obj->dir() != NetPartSelect::PV) return; if (tmp_pin != 1) return; obj_set.push_back(cur_obj); } if (obj_set.size() < 2) return; if (debug_optimizer) cerr << obj->get_fileline() << ": cprop::lpm_part_select: " << "Found " << obj_set.size() << " NetPartSelect(PV) objects." << endl; // Sort by increasing base offset. sort(obj_set.begin(), obj_set.end(), compare_base); // Check and make sure there are no overlaps. If there are, // then give up on this optimization. for (size_t idx = 1 ; idx < obj_set.size() ; idx += 1) { unsigned top = obj_set[idx-1]->base() + obj_set[idx-1]->width(); if (top > obj_set[idx]->base()) { if (debug_optimizer) cerr << obj->get_fileline() << ": cprop::lpm_part_select: " << "Range [" << obj_set[idx-1]->base() << " " << top << ") overlaps PV starting at " << obj_set[idx]->base() << ". Give up." << endl; return; } } // Check if the tail runs off the end of the target. If so it // should be possible to replace it with a bit select to // shorten the object for the target, but for now just give up. unsigned sig_width = nex->vector_width(); if (obj_set.back()->base() + obj_set.back()->width() > sig_width) { if (debug_optimizer) cerr << obj->get_fileline() << ": cprop::lpm_part_select: " << "Range [" << obj_set.back()->base() << ":" << (obj_set.back()->base() + obj_set.back()->width() - 1) << "] runs off the end of target." << endl; return; } // Figure out how many components we are going to need. unsigned part_count = 0; unsigned off = 0; for (size_t idx = 0 ; idx < obj_set.size() ; idx += 1) { if (obj_set[idx]->base() > off) { off = obj_set[idx]->base(); part_count += 1; } off += obj_set[idx]->width(); part_count += 1; } if (off < sig_width) part_count += 1; NetConcat*concat = new NetConcat(scope, scope->local_symbol(), sig_width, part_count); concat->set_line(*obj); des->add_node(concat); connect(concat->pin(0), obj->pin(1)); off = 0; size_t concat_pin = 1; for (size_t idx = 0 ; idx < obj_set.size() ; idx += 1) { NetPartSelect*cobj = obj_set[idx]; if (cobj->base() > off) { NetNet*zzz = make_const_z(des, scope, cobj->base()-off); connect(concat->pin(concat_pin), zzz->pin(0)); concat_pin += 1; off = cobj->base(); } connect(concat->pin(concat_pin), cobj->pin(0)); concat_pin += 1; off += cobj->width(); } if (off < sig_width) { NetNet*zzz = make_const_z(des, scope, sig_width-off); connect(concat->pin(concat_pin), zzz->pin(0)); concat_pin += 1; } ivl_assert(*obj, concat_pin == concat->pin_count()); for (size_t idx = 0 ; idx < obj_set.size() ; idx += 1) { delete obj_set[idx]; } count += 1; } /* * This functor looks to see if the constant is connected to nothing * but signals. If that is the case, delete the dangling constant and * the now useless signals. This functor is applied after the regular * functor to clean up dangling constants that might be left behind. */ struct cprop_dc_functor : public functor_t { virtual void lpm_const(Design*des, NetConst*obj); }; struct nexus_info_s { Nexus*nex; unsigned inp; unsigned out; }; void cprop_dc_functor::lpm_const(Design*, NetConst*obj) { // 'bz constant values drive high impedance to whatever is // connected to it. In other words, it is a noop. But that is // only true if *all* the bits of the vectors. { unsigned tmp = 0; ivl_assert(*obj, obj->pin_count()==1); for (unsigned idx = 0 ; idx < obj->width() ; idx += 1) { if (obj->value(idx) == verinum::Vz) { tmp += 1; } } if (tmp == obj->width()) { delete obj; return; } } std::vector nexus_info (obj->pin_count()); for (unsigned idx = 0 ; idx < obj->pin_count() ; idx += 1) { nexus_info[idx].nex = obj->pin(idx).nexus(); unsigned inputs = 0, outputs = 0; nexus_info[idx].nex -> count_io(inputs, outputs); nexus_info[idx].inp = inputs; nexus_info[idx].out = outputs; } // If there are any links that take input, the constant is // used structurally somewhere. for (unsigned idx = 0 ; idx < obj->pin_count() ; idx += 1) if (nexus_info[idx].inp > 0) return; // Look for signals that have NetESignal nodes attached to // them. If I find any, then this constant is used by a // behavioral expression somewhere. for (unsigned idx = 0 ; idx < obj->pin_count() ; idx += 1) { for (Link*clnk = nexus_info[idx].nex->first_nlink() ; clnk ; clnk = clnk->next_nlink()) { NetPins*cur; unsigned pin; clnk->cur_link(cur, pin); NetNet*tmp = dynamic_cast(cur); if (tmp == 0) continue; assert(tmp->scope()); // If the net is a signal name from the source, // then users will probably want to see it in the // waveform dump, so unhooking the constant will // make it look wrong. if (! tmp->local_flag()) return; // If the net has an eref, then there is an // expression somewhere that reads this signal. So // the constant does get read. if (tmp->peek_eref() > 0) return; // If the net is a port of the root module, then // the constant may be driving something outside // the design, so do not eliminate it. if ((tmp->port_type() != NetNet::NOT_A_PORT) && (tmp->scope()->parent() == 0)) return; } } // Done. Found no reason to keep this object, so delete it. delete obj; } void cprop(Design*des) { // Continually propagate constants until a scan finds nothing // to do. cprop_functor prop; do { prop.count = 0; des->functor(&prop); if (verbose_flag) { cout << " ... Iteration detected " << prop.count << " optimizations." << endl << flush; } } while (prop.count > 0); if (verbose_flag) { cout << " ... Look for dangling constants" << endl << flush; } cprop_dc_functor dc; des->functor(&dc); if (verbose_flag) { cout << " ... done" << endl << flush; } } iverilog-10_1/cygwin.txt000066400000000000000000000015441265551621300154070ustar00rootroot00000000000000 This file describes the build procedure under cygwin32 (Windows 95/98/NT/2K) ---------------------------------------------------------------------------- Note: Icarus Verilog also compiles to native Windows binaries if you use the instructions in the mingw.txt file. Some people prefer cygwin binaries, and these instructions apply. To build using cygwin: Prerequisites: o Latest net release (1.1.4) of cygwin (sources.redhat.com/cygwin) Procedure: o Get the source code - see the main Icarus Verilog page for how to do this o cd to the verilog directory o autoconf.sh o ./configure o make o make install That's all that's needed. To build your own extensions - just include vpi_user.h and link with a command like this: $(CC) -shared -o -Wl,--enable-auto-image-base -L../vvm -lvvm -lvpip - Venkat Iyer iverilog-10_1/design_dump.cc000066400000000000000000001272701265551621300161600ustar00rootroot00000000000000/* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * This file contains all the dump methods of the netlist classes. */ # include # include # include # include "netlist.h" # include "compiler.h" # include "discipline.h" # include "netclass.h" # include "netdarray.h" # include "netqueue.h" # include "netvector.h" # include "ivl_assert.h" # include "PExpr.h" static ostream& operator<< (ostream&o, NetBlock::Type t) { switch (t) { case NetBlock::SEQU: o << "begin"; break; case NetBlock::PARA: o << "fork"; break; case NetBlock::PARA_JOIN_NONE: o << "fork-join_none"; break; case NetBlock::PARA_JOIN_ANY: o << "fork-join_any"; break; } return o; } ostream& operator << (ostream&o, ivl_drive_t str) { switch (str) { case IVL_DR_HiZ: o << "highz"; break; case IVL_DR_SMALL: o << "small"; break; case IVL_DR_MEDIUM: o << "medium"; break; case IVL_DR_WEAK: o << "weak"; break; case IVL_DR_LARGE: o << "large"; break; case IVL_DR_PULL: o << "pull"; break; case IVL_DR_STRONG: o << "strong"; break; case IVL_DR_SUPPLY: o << "supply"; break; default: assert(0); } return o; } ostream& operator << (ostream&o, ivl_variable_type_t val) { switch (val) { case IVL_VT_VOID: o << "void"; break; case IVL_VT_NO_TYPE: o << ""; break; case IVL_VT_REAL: o << "real"; break; case IVL_VT_BOOL: o << "bool"; break; case IVL_VT_LOGIC: o << "logic"; break; case IVL_VT_STRING: o << "string"; break; case IVL_VT_DARRAY: o << "darray"; break; case IVL_VT_CLASS: o << "class"; break; case IVL_VT_QUEUE: o << "queue"; break; } return o; } ostream& operator << (ostream&o, ivl_switch_type_t val) { switch (val) { case IVL_SW_TRAN: o << "tran"; break; case IVL_SW_TRANIF0: o << "tranif0"; break; case IVL_SW_TRANIF1: o << "tranif1"; break; case IVL_SW_RTRAN: o << "rtran"; break; case IVL_SW_RTRANIF0: o << "rtranif0"; break; case IVL_SW_RTRANIF1: o << "rtranif1"; break; case IVL_SW_TRAN_VP: o << "tran(VP)"; break; } return o; } ostream& operator << (ostream&fd, PortType::Enum val) { switch (val) { case PortType::NOT_A_PORT: fd << "NOT_A_PORT"; break; case PortType::PIMPLICIT: fd << "PIMPLICIT"; break; case PortType::PINPUT: fd << "PINPUT"; break; case PortType::POUTPUT: fd << "POUTPUT"; break; case PortType::PINOUT: fd << "PINOUT"; break; case PortType::PREF: fd << "PREF"; break; default: fd << "PortType::Enum::?"; break; } return fd; } ostream& operator << (ostream&fd, NetCaseCmp::kind_t that) { switch (that) { case NetCaseCmp::EEQ: fd << "==="; break; case NetCaseCmp::NEQ: fd << "!=="; break; case NetCaseCmp::XEQ: fd << "==?"; break; case NetCaseCmp::ZEQ: fd << "==z?"; break; } return fd; } ostream& ivl_type_s::debug_dump(ostream&o) const { o << typeid(*this).name(); return o; } ostream& netclass_t::debug_dump(ostream&fd) const { fd << "class " << name_ << "{"; for (size_t idx = 0 ; idx < property_table_.size() ; idx += 1) { if (idx != 0) fd << "; "; if (property_table_[idx].type) property_table_[idx].type->debug_dump(fd); else fd << "NO_TYPE"; fd << " " << property_table_[idx].name; } fd << "}"; return fd; } ostream& netdarray_t::debug_dump(ostream&o) const { o << "dynamic array of " << *element_type(); return o; } ostream& netqueue_t::debug_dump(ostream&fd) const { fd << "queue of " << *element_type(); return fd; } ostream& netvector_t::debug_dump(ostream&o) const { o << type_ << (signed_? " signed" : " unsigned") << packed_dims_; return o; } static inline void dump_scope_path(ostream&o, const NetScope*scope) { const NetScope*parent = scope->parent(); if (parent) { dump_scope_path(o, parent); o << "."; } o << scope->fullname(); } ostream& operator <<(ostream&o, struct __ScopePathManip marg) { if (marg.scope != 0) dump_scope_path(o, marg.scope); return o; } ostream& operator <<(ostream&o, struct __ObjectPathManip marg) { if (marg.obj != 0) { dump_scope_path(o, marg.obj->scope()); o << "." << marg.obj->name(); } return o; } ostream& operator <<(ostream&fd, Link::DIR dir) { switch (dir) { case Link::PASSIVE: fd << "PASSIVE"; break; case Link::INPUT: fd << "INPUT"; break; case Link::OUTPUT: fd << "OUTPUT"; break; default: fd << "<" << (int)dir << ">"; break; } return fd; } void NetPins::show_type(ostream&fd) const { fd << typeid(*this).name(); } void NetObj::show_type(ostream&fd) const { fd << typeid(*this).name() << "[" << scope_path(scope_) << "." << name_ << "]"; } struct __ShowTypeManip { const NetPins*pins; }; inline __ShowTypeManip show_type(const NetPins*pins) { __ShowTypeManip tmp; tmp.pins = pins; return tmp; } inline ostream& operator << (ostream&fd, __ShowTypeManip man) { if (man.pins == 0) fd << "NexusSet"; else man.pins->show_type(fd); return fd; } void Link::dump_link(ostream&fd, unsigned ind) const { const Link*cur; const Nexus*nex = nexus(); if (nex == 0) { fd << setw(ind) << "" << "" << endl; return; } for (cur = nex->first_nlink() ; cur; cur = cur->next_nlink()) { const NetPins*obj = cur->get_obj(); unsigned pin = cur->get_pin(); fd << setw(ind) << "" << "Pin " << pin << " of " << show_type(obj) << ", dir=" << cur->dir_ << endl; } } void NetBranch::dump(ostream&o, unsigned ind) const { static const char*pin_names[2] = { "terminal0", "terminal1" }; o << setw(ind) << "" << "branch island=" << get_island(); o << " // " << get_fileline() << endl; dump_node_pins(o, ind+4, pin_names); } void NetDelaySrc::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "specify delay"; if (posedge_) o << " posedge"; if (negedge_) o << " negedge"; if (is_condit()) { if (has_condit()) o << " if"; else o << " ifnone"; } o << " src " << "(" << transition_delays_[IVL_PE_01] << "," << transition_delays_[IVL_PE_10] << "," << transition_delays_[IVL_PE_0z] << "/" << transition_delays_[IVL_PE_z1] << "," << transition_delays_[IVL_PE_1z] << "," << transition_delays_[IVL_PE_z0] << "/" << transition_delays_[IVL_PE_0x] << "," << transition_delays_[IVL_PE_x1] << "," << transition_delays_[IVL_PE_1x] << "/" << transition_delays_[IVL_PE_x0] << "," << transition_delays_[IVL_PE_xz] << "," << transition_delays_[IVL_PE_zx] << ") scope=" << scope_path(scope()) << endl; dump_node_pins(o, ind+4); } static inline ostream&operator<<(ostream&out, const netrange_t&that) { if (that.defined()) out << "[" << that.get_msb() << ":" << that.get_lsb() << "]"; else out << "[]"; return out; } ostream&operator<<(ostream&out, const list&rlist) { for (list::const_iterator cur = rlist.begin() ; cur != rlist.end() ; ++cur) { out << *cur; } return out; } ostream&operator<<(ostream&out, const vector&rlist) { for (vector::const_iterator cur = rlist.begin() ; cur != rlist.end() ; ++cur) { out << *cur; } return out; } /* Dump a net. This can be a wire or register. */ void NetNet::dump_net(ostream&o, unsigned ind) const { o << setw(ind) << "" << type() << ": " << name() << unpacked_dims_ << " unpacked dims=" << unpacked_dimensions(); o << " pin_count=" << pin_count(); if (local_flag_) o << " (local)"; switch (port_type_) { case NetNet::NOT_A_PORT: break; case NetNet::PIMPLICIT: o << " implicit-port?"; break; case NetNet::PINPUT: o << " input"; break; case NetNet::POUTPUT: o << " output"; break; case NetNet::PINOUT: o << " inout"; break; case NetNet::PREF: o <<" ref"; break; } if (ivl_discipline_t dis = get_discipline()) o << " discipline=" << dis->name(); if (net_type_) o << " " << *net_type_; o << " (eref=" << peek_eref() << ", lref=" << peek_lref() << ")"; if (scope()) o << " scope=" << scope_path(scope()); o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") vector_width=" << vector_width() << " pin_count=" << pin_count(); if (pins_are_virtual()) { o << " pins_are_virtual" << endl; return; } o << endl; for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { if (! pin(idx).is_linked()) continue; const Nexus*nex = pin(idx).nexus(); o << setw(ind+4) << "" << "[" << idx << "]: " << nex << " " << nex->name() << endl; } for (unsigned idx = 0 ; idx < delay_paths_.size() ; idx += 1) { const NetDelaySrc*cur = delay_paths_[idx]; cur->dump(o, ind+4); } dump_obj_attr(o, ind+4); } /* Dump a NetNode and its pins. Dump what I know about the netnode on the first line, then list all the pins, with the name of the connected signal. */ void NetNode::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "node: "; o << typeid(*this).name() << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } /* This is the generic dumping of all the signals connected to each pin of the object. The "this" object is not printed, only the signals connected to this. */ void NetPins::dump_node_pins(ostream&o, unsigned ind, const char**pin_names) const { for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { o << setw(ind) << "" << idx; if (pin_names && pin_names[idx]) o << " " << pin_names[idx]; else o << " pin" << idx; switch (pin(idx).get_dir()) { case Link::PASSIVE: o << " p"; break; case Link::INPUT: o << " I"; break; case Link::OUTPUT: o << " O"; break; } o << " (" << pin(idx).drive0() << "0 " << pin(idx).drive1() << "1): "; if (pin(idx).is_linked()) { const Nexus*nex = pin(idx).nexus(); o << nex << " " << nex->name(); } o << endl; } } void NetObj::dump_obj_attr(ostream&o, unsigned ind) const { unsigned idx; for (idx = 0 ; idx < attr_cnt() ; idx += 1) { o << setw(ind) << "" << attr_key(idx) << " = \"" << attr_value(idx) << "\"" << endl; } } void NetAbs::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Absolute value (NetAbs): " << name() << " width=" << width() << " pin_count=" << pin_count() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetAddSub::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Adder (NetAddSub): " << name() << " width=" << width() << " pin_count=" << pin_count() << endl; static const char* pin_names[] = { "Cout ", "DataA ", "DataB ", "Result" }; dump_node_pins(o, ind+4, pin_names); dump_obj_attr(o, ind+4); } void NetArrayDq::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "NetArrayDq: " << name() << " array=" << mem_->name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetCastInt2::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Cast to int2. (NetCastInt2): " << name() << " width=" << width() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetCastInt4::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Cast to int4. (NetCastInt4): " << name() << " width=" << width() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetCastReal::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Cast to real (NetCastReal): " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetCLShift::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetCompare::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "LPM_COMPARE (NetCompare " << (get_signed()? "signed" : "unsigned") << "): " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetConcat::dump_node(ostream&o, unsigned ind) const { if (transparent_) o << setw(ind) << "" << "NetConcat8: "; else o << setw(ind) << "" << "NetConcat: "; o << name(); if (rise_time()) o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time() << ")"; else o << " #(0,0,0)"; o << " scope=" << scope_path(scope()) << " width=" << width_ << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetDivide::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "NET_DIVIDE (NetDivide): " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetMult::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "LPM_MULT (NetMult): " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetPow::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "LPM_POW (NetPow): " << name() << " scope=" << scope_path(scope()) << " delay=("; if (rise_time()) o << *rise_time() << "," << *fall_time() << "," << *decay_time(); else o << "0,0,0"; o << ")" << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetMux::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Multiplexer (NetMux): " << name() << " width=" << width_ << " swidth=" << swidth_ << " size=" << size_ << " scope=" << scope_path(scope()) << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetBUFZ::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "NetBUFZ: " << name() << " scope=" << scope_path(scope()) << " delay=(" << rise_time() << "," << fall_time() << "," << decay_time() << ") width=" << width() << (transparent()? " " : " non-") << "transparent" << endl; dump_node_pins(o, ind+4); } void NetCaseCmp::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "case compare " << kind_ << ": " << name() << endl; dump_node_pins(o, ind+4); } void NetConst::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "constant " << value_; o << ": " << name(); if (rise_time()) o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time() << ")"; else o << " #(.,.,.)"; o << endl; dump_node_pins(o, ind+4); } void NetFF::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "LPM_FF: " << name() << " scope=" << scope_path(scope()); if (negedge_) o << " negedge"; else o << " posedge"; o << " aset_value=" << aset_value_ << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetLiteral::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "constant real " << real_ << ": " << name(); if (rise_time()) o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time() << ")"; else o << " #(.,.,.)"; o << endl; dump_node_pins(o, ind+4); } void NetLogic::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "logic: "; switch (type_) { case AND: o << "and"; break; case BUF: o << "buf"; break; case BUFIF0: o << "bufif0"; break; case BUFIF1: o << "bufif1"; break; case CMOS: o << "cmos"; break; case NAND: o << "nand"; break; case NMOS: o << "nmos"; break; case NOR: o << "nor"; break; case NOT: o << "not"; break; case NOTIF0: o << "notif0"; break; case NOTIF1: o << "notif1"; break; case OR: o << "or"; break; case PULLDOWN: o << "pulldown"; break; case PULLUP: o << "pullup"; break; case RCMOS: o << "rcmos"; break; case RNMOS: o << "rnmos"; break; case RPMOS: o << "rpmos"; break; case PMOS: o << "pmos"; break; case XNOR: o << "xnor"; break; case XOR: o << "xor"; break; } o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") " << name() << " scope=" << scope_path(scope()) << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetModulo::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "NET_MODULO (NetModulo): " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetPartSelect::dump_node(ostream&o, unsigned ind) const { const char*pt = ""; switch (dir_) { case VP: pt = "VP"; break; case PV: pt = "PV"; break; } o << setw(ind) << "" << "NetPartSelect(" << pt << "): " << name(); if (rise_time()) o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time() << ")"; else o << " #(.,.,.)"; o << " off=" << off_ << " wid=" << wid_ <name << "(...) -->" << data_type() << " width=" << vector_width() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetUserFunc::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "USER FUNC: " << scope_path(def_); if (rise_time()) o << " #(" <<*rise_time() <<","<<*fall_time() << "," <<*decay_time() << ")"; o << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetTaskDef::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "task " << scope_path(scope()) << ";" << endl; for (unsigned idx = 0 ; idx < port_count() ; idx += 1) { const NetNet*pnet = port(idx); o << setw(ind+4) << ""; assert(pnet); switch (pnet->port_type()) { case NetNet::PINPUT: o << "input "; break; case NetNet::POUTPUT: o << "output "; break; case NetNet::PINOUT: o << "input "; break; default: o << "NOT_A_PORT "; break; } o << pnet->name() << ";" << endl; } if (proc_) proc_->dump(o, ind+4); else o << setw(ind+4) << "" << "MISSING PROCEDURAL CODE" << endl; o << setw(ind) << "" << "endtask" << endl; } void NetTran::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << type_ << " " << name() << " island " << get_island(); if (type_ == IVL_SW_TRAN_VP) { o << " width=" << vector_width() << " part=" << part_width() << " offset=" << part_offset(); } o << " delay=("; if (rise_time()) o << *rise_time() << "," << *fall_time() << "," << *decay_time(); else o << "0,0,0"; o << ")" << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetUDP::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "UDP (" << udp_name() << "): "; o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") " << name() << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetProcTop::dump(ostream&o, unsigned ind) const { switch (type_) { case IVL_PR_INITIAL: o << "initial /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; case IVL_PR_ALWAYS: o << "always /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; case IVL_PR_FINAL: o << "final /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; } for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) { o << setw(ind+2) << "" << "(* " << attr_key(idx) << " = " << attr_value(idx) << " *)" << endl; } statement_->dump(o, ind+2); } void NetAnalogTop::dump(ostream&o, unsigned ind) const { switch (type_) { case IVL_PR_INITIAL: o << "analog initial /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; case IVL_PR_ALWAYS: o << "analog /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; case IVL_PR_FINAL: o << "analog final /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; } statement_->dump(o, ind+2); } void NetAlloc::dump(ostream&o, unsigned ind) const { o << setw(ind) << "// allocate storage : " << scope_path(scope_) << endl; } void NetAssign_::dump_lval(ostream&o) const { if (sig_) o << sig_->name(); else if (nest_) nest_->dump_lval(o); else o << ""; if (! member_.nil()) { o << "." << member_; } if (word_) { o << "[word=" << *word_ << "]"; } if (base_) { o << "[" << *base_ << " +: " << lwid_ << "]"; } } void NetAssignBase::dump_lval(ostream&o) const { o << "{"; lval_->dump_lval(o); for (NetAssign_*cur = lval_->more ; cur ; cur = cur->more) { o << ", "; cur->dump_lval(o); } o << "}"; } /* Dump an assignment statement */ void NetAssign::dump(ostream&o, unsigned ind) const { o << setw(ind) << ""; dump_lval(o); if (op_) o << " " << op_ << "= "; else o << " = "; if (const NetExpr*de = get_delay()) o << "#(" << *de << ") "; o << *rval() << ";" << endl; } void NetAssignNB::dump(ostream&o, unsigned ind) const { o << setw(ind) << ""; dump_lval(o); o << " <= "; if (const NetExpr*de = get_delay()) o << "#(" << *de << ") "; if (count_) o << "repeat(" << *count_ << ") "; if (event_) { o << *event_; } o << *rval() << ";" << endl; } void NetAssignBase::dump(ostream&o, unsigned ind) const { if (const NetAssignNB *n1 = dynamic_cast(this)) { n1->dump(o,ind); } else if (const NetAssign *n2 = dynamic_cast(this)) { n2->dump(o,ind); } } /* Dump a block statement */ void NetBlock::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << type_; if (subscope_) o << " : " << scope_path(subscope_); o << endl; if (last_) { const NetProc*cur = last_; do { cur = cur->next_; cur->dump(o, ind+4); } while (cur != last_); } o << setw(ind) << "" << "end" << endl; } void NetCase::dump(ostream&o, unsigned ind) const { switch (type_) { case EQ: o << setw(ind) << "" << "case (" << *expr_ << ")" << endl; break; case EQX: o << setw(ind) << "" << "casex (" << *expr_ << ")" << endl; break; case EQZ: o << setw(ind) << "" << "casez (" << *expr_ << ")" << endl; break; } for (unsigned idx = 0 ; idx < items_.size() ; idx += 1) { o << setw(ind+2) << ""; if (items_[idx].guard) o << *items_[idx].guard << ":"; else o << "default:"; if (items_[idx].statement) { o << endl; items_[idx].statement->dump(o, ind+6); } else { o << " ;" << endl; } } o << setw(ind) << "" << "endcase" << endl; } void NetCAssign::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "cassign "; dump_lval(o); o << " = " << *rval() << "; /* " << get_fileline() << " */" << endl; } void NetCondit::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "if ("; expr_->dump(o); o << ")" << endl; if (if_) if_->dump(o, ind+4); else o << setw(ind+4) << "" << "/* empty */ ;" << endl; if (else_) { o << setw(ind) << "" << "else" << endl; else_->dump(o, ind+4); } } void NetContribution::dump(ostream&o, unsigned ind) const { o << setw(ind) << ""; lval_->dump(o); o << " <+ "; rval_->dump(o); o << ";" << endl; } void NetDeassign::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "deassign "; dump_lval(o); o << "; /* " << get_fileline() << " */" << endl; } void NetDisable::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "disable "; if (target_) o << scope_path(target_); else o << "fork"; o << "; " << "/* " << get_fileline() << " */" << endl; } void NetDoWhile::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "do" << endl; proc_->dump(o, ind+3); o << setw(ind) << "" << "while (" << *cond_ << ");" << endl; } void NetEvProbe::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << ""; switch (edge_) { case ANYEDGE: o << "anyedge "; break; case POSEDGE: o << "posedge "; break; case NEGEDGE: o << "negedge "; break; } o << setw(ind) << "" << "-> " << event_->name() << "; " << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } void NetEvTrig::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "-> " << event_->name() << "; " << "// " << get_fileline() << endl; } void NetEvWait::dump(ostream&o, unsigned ind) const { o << setw(ind) << ""; /* Check for a wait fork. */ if ((nevents() == 1) && (event(0) == 0)) { o << "wait fork;"; return; } o << "@("; if (nevents() > 0) o << event(0)->name(); for (unsigned idx = 1 ; idx < nevents() ; idx += 1) o << " or " << event(idx)->name(); o << ") // " << get_fileline() << endl; if (statement_) statement_->dump(o, ind+2); else o << setw(ind+2) << "" << "/* noop */ ;" << endl; } ostream& operator << (ostream&out, const NetEvWait&obj) { obj.dump_inline(out); return out; } void NetEvWait::dump_inline(ostream&o) const { /* Check for a wait fork. */ if ((nevents() == 1) && (event(0) == 0)) { o << "wait fork;"; return; } o << "@("; if (nevents() > 0) o << event(0)->name(); for (unsigned idx = 1 ; idx < nevents() ; idx += 1) o << " or " << event(idx)->name(); o << ") "; } void NetForce::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "force "; dump_lval(o); o << " = " << *rval() << "; /* " << get_fileline() << " */" << endl; } void NetForever::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "forever" << endl; statement_->dump(o, ind+2); } void NetForLoop::dump(ostream&fd, unsigned ind) const { fd << setw(ind) << "" << "FOR LOOP index=" << index_->name() << endl; statement_->dump(fd, ind+4); step_statement_->dump(fd, ind+4); } void NetFree::dump(ostream&o, unsigned ind) const { o << setw(ind) << "// free storage : " << scope_path(scope_) << endl; } void NetFuncDef::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "function definition for " << scope_path(scope()) << endl; if (result_sig_) { o << setw(ind+2) << "" << "Return signal: "; if (result_sig_->get_signed()) o << "+"; o << result_sig_->name() << endl; } o << setw(ind+2) << "" << "Arguments: "; if (port_count() == 0) o << ""; o << endl; for (unsigned idx = 0; idx < port_count(); idx += 1) { o << setw(ind+4) << "" << "Arg[" << idx+1 << "] = "; switch (port(idx)->port_type()) { default: o << "implicit-port? "; break; case NetNet::PINPUT: o << "input "; break; case NetNet::POUTPUT: o << "output "; break; case NetNet::PINOUT: o << "inout "; break; } if (port(idx)->get_signed()) o << "+"; o << port(idx)->name() << endl; } if (proc_) proc_->dump(o, ind+2); else o << setw(ind+2) << "" << "MISSING PROCEDURAL CODE" << endl; } void NetPDelay::dump(ostream&o, unsigned ind) const { if (expr_) { o << setw(ind) << "" << "#" << *expr_; } else { o << setw(ind) << "" << "#" << delay_; } if (statement_) { o << endl; statement_->dump(o, ind+2); } else { o << " /* noop */;" << endl; } } void NetRelease::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "release "; dump_lval(o); o << "; /* " << get_fileline() << " */" << endl; } void NetRepeat::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "repeat (" << *expr_ << ")" << endl; statement_->dump(o, ind+2); } void netclass_t::dump_scope(ostream&fd) const { class_scope_->dump(fd); } void NetScope::dump(ostream&o) const { /* This is a constructed hierarchical name. */ o << scope_path(this) << " "; print_type(o); if (is_auto()) o << " (automatic)"; if (is_cell()) o << " (cell)"; if (nested_module()) o << " (nested)"; if (program_block()) o << " (program)"; if (is_interface()) o << " (interface)"; o << " " << children_.size() << " children, " << classes_.size() << " classes" << endl; for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) o << " (* " << attr_key(idx) << " = " << attr_value(idx) << " *)" << endl; o << " timescale = 10e" << time_unit() << " / 10e" << time_precision() << endl; /* Dump the parameters for this scope. */ { map::const_iterator pp; for (pp = parameters.begin() ; pp != parameters.end() ; ++ pp ) { if ((*pp).second.is_annotatable) o << " specparam "; else o << " parameter "; o << pp->second.type << " "; if ((*pp).second.signed_flag) o << "signed "; if ((*pp).second.msb) o << "[" << *(*pp).second.msb << ":" << *(*pp).second.lsb << "] "; o << (*pp).first << " = "; if (pp->second.val) o << *(*pp).second.val; else o << ""; for (range_t*ran = (*pp).second.range ; ran ; ran = ran->next) { if (ran->exclude_flag) o << " exclude "; else o << " from "; if (ran->low_open_flag) o << "("; else o << "["; if (ran->low_expr) o << *ran->low_expr; else if (ran->low_open_flag==false) o << "-inf"; else o << ""; if (ran->high_expr) o << ":" << *ran->high_expr; else if (ran->high_open_flag==false) o << ":inf"; else o << ":"; if (ran->high_open_flag) o << ")"; else o << "]"; } o << ";" << endl; } } /* Dump the saved defparam assignments here. */ { list >::const_iterator pp; for (pp = defparams.begin() ; pp != defparams.end() ; ++ pp ) { o << " defparam " << (*pp).first << " = " << *(*pp).second << ";" << endl; } } { list,PExpr*> >::const_iterator pp; for (pp = defparams_later.begin() ; pp != defparams_later.end() ; ++ pp ) { o << " defparam(later) " << pp->first << " = " << *(pp->second) << ";" << endl; } } o << " enum sets {" << endl; /* Dump the enumerations and enum names in this scope. */ for (map::const_iterator cur = enum_sets_.begin() ; cur != enum_sets_.end() ; ++ cur) { o << " " << cur->second << endl; } o << " }" << endl; o << " enum names {" << endl; for (map::const_iterator cur = enum_names_.begin() ; cur != enum_names_.end() ; ++ cur) { o << " " << cur->first << " = " << cur->second->value() << " from " << cur->second->enumeration() << endl; } o << " }" << endl; /* Dump the events in this scope. */ for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) { o << " event " << cur->name() << "; nprobe=" << cur->nprobe() << " scope=" << scope_path(cur->scope()) << " // " << cur->get_fileline() << endl; } // Dump the signals, for (signals_map_iter_t cur = signals_map_.begin() ; cur != signals_map_.end() ; ++ cur) { cur->second->dump_net(o, 4); } switch (type_) { case FUNC: if (func_def()) func_def()->dump(o, 4); else o << " MISSING FUNCTION DEFINITION" << endl; break; case TASK: if (task_def()) task_def()->dump(o, 4); else o << " MISSING TASK DEFINITION" << endl; break; default: break; } /* Dump any sub-scopes. */ for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) cur->second->dump(o); for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++ cur ) { cur->second->dump_scope(o); } } void NetSTask::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << name_; if (! parms_.empty()) { o << "("; if (parms_[0]) parms_[0]->dump(o); for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { o << ", "; if (parms_[idx]) parms_[idx]->dump(o); } o << ")"; } o << ";" << endl; } void NetUTask::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << scope_path(task_) << ";" << endl; } void NetWhile::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "while (" << *cond_ << ")" << endl; proc_->dump(o, ind+3); } /* Dump a statement type that someone didn't write a dump for. */ void NetProc::dump(ostream&o, unsigned ind) const { o << setw(ind) << "" << "// " << typeid(*this).name() << endl; } /* Dump an expression that no one wrote a dump method for. */ void NetExpr::dump(ostream&o) const { o << "(?" << typeid(*this).name() << "?)"; } void NetEAccess::dump(ostream&o) const { o << nature_->name() << "." << nature_->access() << "("; assert(branch_); if (branch_->pin(0).is_linked()) o << branch_->pin(0).nexus()->name(); o << ", "; if (branch_->pin(1).is_linked()) o << branch_->pin(1).nexus()->name(); o << ")"; } void NetEArrayPattern::dump(ostream&fd) const { fd << "'{"; if (items_.size() >= 1) { if (items_[0]) fd << *items_[0]; } for (size_t idx = 1 ; idx < items_.size() ; idx += 1) { fd << ", "; if (items_[idx]) fd << *items_[idx]; } fd << "}"; } void NetEBinary::dump(ostream&o) const { if (op_ == 'm' || op_ == 'M') { if (op_ == 'm') o << "min"; else o << "max"; o << "("; left_->dump(o); o << ", "; right_->dump(o); o << ")"; return; } o << "("; left_->dump(o); o << ")"; switch (op_) { default: o << op_; break; case 'a': o << "&&"; break; case 'A': o << "~&"; break; case 'E': o << "==="; break; case 'e': o << "=="; break; case 'G': o << ">="; break; case 'l': o << "<<"; break; case 'L': o << "<="; break; case 'n': o << "!="; break; case 'N': o << "!=="; break; case 'o': o << "||"; break; case 'O': o << "~|"; break; case 'p': o << "**"; break; case 'r': o << ">>"; break; case 'R': o << ">>>"; break; case 'X': o << "~^"; break; } o << "("; right_->dump(o); o << ")"; } void NetECast::dump(ostream&fd) const { if (op_=='2') fd << "bool<" << expr_width() << ">(" << *expr_ << ")"; else if (op_=='4') fd << "logic<" << expr_width() << ">(" << *expr_ << ")"; else NetEUnary::dump(fd); } void NetEConcat::dump(ostream&o) const { if (repeat_ != 1) o << repeat_; if (parms_[0]) o << "{" << *parms_[0]; else o << "{"; for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) o << ", " << *parms_[idx]; else o << ", "; } o << "}"; } void NetEConst::dump(ostream&o) const { if (value_.is_string()) o << "\"" << value_.as_string() << "\""; else o << value_; } void NetEConstEnum::dump(ostream&o) const { o << "<" << name_ << "="; NetEConst::dump(o); o << ", wid=" << expr_width() << ">"; } void NetEConstParam::dump(ostream&o) const { o << "<" << name_ << "="; NetEConst::dump(o); o << ", wid=" << expr_width() << ">"; } void NetECReal::dump(ostream&o) const { o << value_; } void NetECRealParam::dump(ostream&o) const { o << "<" << name_ << "="; NetECReal::dump(o); o << ">"; } void NetEEvent::dump(ostream&o) const { o << "name() << ">"; } void NetELast::dump(ostream&fd) const { fd << "name() << ">"; } void NetENetenum::dump(ostream&o) const { o << ""; } void NetENew::dump(ostream&o) const { o << "new ["; if (size_) size_->dump(o); o << "]"; if (init_val_) { o << "("; init_val_->dump(o); o << ")"; } } void NetENull::dump(ostream&o) const { o << ""; } void NetEProperty::dump(ostream&o) const { o << net_->name() << ".<" << pidx_ << ">"; if (index_) o << "[" << *index_ << "]"; } void NetEScope::dump(ostream&o) const { o << ""; } void NetESelect::dump(ostream&o) const { o << "dump(o); o << "["; if (base_) base_->dump(o); else o << "(0)"; o << "+:" << expr_width() << "]"; if (ivl_type_t nt = net_type()) { o << " net_type=(" << *nt << ")"; } else { o << " expr_type=" << expr_type(); } o << ">"; } void NetESFunc::dump(ostream&o) const { o << name_ << "("; if (nparms() > 0) o << *parm(0); for (unsigned idx = 1 ; idx < nparms() ; idx += 1) o << ", " << *parm(idx); o << ")"; } void NetEShallowCopy::dump(ostream&o) const { o << ""; } void NetESignal::dump(ostream&o) const { if (has_sign()) o << "+"; o << name(); if (word_) o << "[word=" << *word_ << "]"; vectortmp = net_->net_type()->slice_dimensions(); o << tmp; } void NetETernary::dump(ostream&o) const { o << "(" << *cond_ << ") ? (" << *true_val_ << ") : (" << *false_val_ << ")"; } void NetEUFunc::dump(ostream&o) const { o << scope_path(func_) << "("; if (! parms_.empty()) { parms_[0]->dump(o); for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { o << ", "; parms_[idx]->dump(o); } } o << ")"; } void NetEUnary::dump(ostream&o) const { switch (op_) { case 'A': o << "~&"; break; case 'm': o << "abs"; break; case 'N': o << "~|"; break; case 'X': o << "~^"; break; case 'I': o << "++"; break; case 'D': o << "--"; break; case 'i': case 'd': break; default: o << op_; break; } o << "("; expr_->dump(o); o << ")"; switch (op_) { case 'i': o << "++"; break; case 'd': o << "--"; break; } } void Design::dump(ostream&o) const { o << "DESIGN TIME PRECISION: 10e" << get_precision() << endl; o << "PACKAGES:" << endl; for (map::const_iterator cur = packages_.begin() ; cur != packages_.end() ; ++cur) { cur->second->dump(o); } o << "$ROOT CLASSES:" << endl; for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++cur) { cur->second->dump_scope(o); } o << "$ROOT TASKS/FUNCTIONS:" << endl; for (map::const_iterator cur = root_tasks_.begin() ; cur != root_tasks_.end() ; ++ cur) { cur->first->dump(o); } o << "SCOPES:" << endl; for (list::const_iterator scope = root_scopes_.begin(); scope != root_scopes_.end(); ++ scope ) { (*scope)->dump(o); } o << "ELABORATED NODES:" << endl; // dump the nodes, if (nodes_) { NetNode*cur = nodes_->node_next_; do { assert(cur); cur->dump_node(o, 0); cur = cur->node_next_; } while (cur != nodes_->node_next_); } o << "ELABORATED BRANCHES:" << endl; if (branches_) { for (NetBranch*cur = branches_ ; cur ; cur = cur->next_) cur->dump(o, 0); } o << "ELABORATED PROCESSES:" << endl; // Dump the processes. for (const NetProcTop*idx = procs_ ; idx ; idx = idx->next_) idx->dump(o, 0); for (const NetAnalogTop*idx = aprocs_ ; idx ; idx = idx->next_) idx->dump(o, 0); } iverilog-10_1/developer-quick-start.txt000066400000000000000000000141071265551621300203400ustar00rootroot00000000000000 Developer Quick Start for Icarus Verilog The documentation for getting, building and installing Icarus Verilog is kept and maintained at the iverilog documentation wiki at . See the Installation Guide for getting the current source from the git repository (and how to use the git repository) and see the Developer Guide for instructions on participating in the Icarus Verilog development process. That information will not be repeated here. What this documentation *will* cover is the gross structure of the Icarus Verilog compiler source. This will help orient you to the source code itself, so that you can find the global parts where you can look for even better detail. * Compiler Components - The compiler driver (driver/) This is the binary that is installed as "iverilog". This program takes the command line arguments and assembles invocations of all the other subcommands to perform the steps of compilation. - The preprocessor (ivlpp/) This implements the Verilog pre-processor. In Icarus Verilog, the compiler directives `define, `include, `ifdef and etc. are implemented in an external program. The ivlpp/ directory contains the source for this program. - The core compiler (this directory) The "ivl" program is the core that does all the Verilog compiler processing that is not handled elsewhere. This is the main core of the Icarus Verilog compiler, not the runtime. See below for more details on the core itself. - The loadable code generators (tgt-*/) This core compiler, after it is finished with parsing and semantic analysis, uses loadable code generators to emit code for supported targets. The tgt-*/ directories contains the source for the target code generators that are bundled with Icarus Verilog. The tgt-vvp/ directory in particular contains the code generator for the vvp runtime. * Runtime Components - The vvp runtime (vvp/) This program implements the runtime environment for Icarus Verilog. It implements the "vvp" command described in the user documentation. See the vvp/ subdirectory for further developer documentation. - The system tasks implementations (vpi/) The standard Verilog system tasks are implemented using VPI (PLI-2) and the source is in this subdirectory. - The PLI-1 compatibility library (libveriuser/) The Icarus Verilog support for the deprecated PLI-1 is in this subdirectory. The vvp runtime does not directly support the PLI-1. Instead, the libveriuser library emulates it using the builtin PLI-2 support. - The Cadence PLI module compatibility module (cadpli/) It is possible in some specialized situations to load and execute PLI-1 code written for Verilog-XL. This directory contains the source for the module that provides the Cadence PLI interface. * The Core Compiler The "ivl" binary is the core compiler that does the heavy lifting of compiling the Verilog source (including libraries) and generating the output. This is the most complex component of the Icarus Verilog compilation system. The process in the abstract starts with the Verilog lexical analysis and parsing to generate an internal "pform". The pform is then translated by elaboration into the "netlist" form. The netlist is processed by some functors (which include some optimizations and optional synthesis) then is translated into the ivl_target internal form. And finally, the ivl_target form is passed via the ivl_target.h API to the code generators. - Lexical Analysis Lexical analysis and parsing use the tools "flex", "gperf", and "bison". The "flex" input file "lexor.lex" recognizes the tokens in the input stream. This is called "lexical analysis". The lexical analyzer also does some processing of compiler directives that are not otherwise taken care of by the external preprocessor. The lexical analyzer uses a table of keywords that is generated using the "gperf" program and the input file "lexor_keywords.gperf". This table allows the lexical analyzer to efficiently check input words with the rather large set of potential keywords. - Parsing The parser input file "parse.y" is passed to the "bison" program to generate the parser. The parser uses the functions in parse*.h, parse*.cc, pform.h, and pform*.cc to generate the pform from the stream of input tokens. The pform is what compiler writers call a "decorated parse tree". The pform itself is described by the classes in the header files "PScope.h", "Module.h", "PGenerate.h", "Statement.h", and "PExpr.h". The implementations of the classes in those header files are in the similarly named C++ files. - Elaboration Elaboration transforms the pform to the netlist form. Elaboration is conceptually divided into several major steps: Scope elaboration, parameter overrides and defparam propagation, signal elaboration, and statement and expression elaboration. The elaboration of scopes and parameter overrides and defparam propagation are conceptually separate, but are in practice intermingled. The elaboration of scopes scans the pform to find and instantiate all the scopes of the design. New scopes are created by instantiation of modules (starting with the root instances) by user defined tasks and functions, named blocks, and generate schemes. The elaborate_scope methods implement scope elaboration, and the elab_scope.cc source file has the implementations of those methods. The elaborate.cc source file contains the initial calls to the elaborate_scope for the root scopes to get the process started. In particular, see the "elaborate" function near the bottom of the elaborate.cc source file. The calls to Design::make_root_scope create the initial root scopes, and the creation and enqueue of the elaborate_root_scope_t work items primes the scope elaboration work list. Intermingled in the work list are defparms work items that call the Design::run_defparams and Design::evaluate_parameters methods that override and evaluate parameters. The override and evaluation of parameters must be intermingled with the elaboration of scopes because the exact values of parameters may impact the scopes created (imagine generate schemes and instance arrays) and the created scopes in turn create new parameters that need override and evaluation. iverilog-10_1/discipline.cc000066400000000000000000000023571265551621300160030ustar00rootroot00000000000000/* * Copyright (c) 2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "discipline.h" ivl_nature_s::ivl_nature_s(perm_string name__, perm_string access__) : name_(name__), access_(access__) { } ivl_nature_s::~ivl_nature_s() { } ivl_discipline_s::ivl_discipline_s(perm_string name__, ivl_dis_domain_t domain__, ivl_nature_t pot, ivl_nature_t flow__) : name_(name__), domain_(domain__), potential_(pot), flow_(flow__) { } ivl_discipline_s::~ivl_discipline_s() { } iverilog-10_1/discipline.h000066400000000000000000000050271265551621300156420ustar00rootroot00000000000000#ifndef IVL_discipline_H #define IVL_discipline_H /* * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The discipline types include the discipline, nature and other * related types for managing declared and used disciplines in a * Verilog-AMS program. */ # include "StringHeap.h" # include # include # include "ivl_target.h" # include "LineInfo.h" extern std::ostream& operator << (std::ostream&, ivl_dis_domain_t); class ivl_nature_s : public LineInfo { public: explicit ivl_nature_s(perm_string name, perm_string access); ~ivl_nature_s(); perm_string name() const { return name_; } // Identifier for the access function for this nature perm_string access() const { return access_; } private: perm_string name_; perm_string access_; }; class ivl_discipline_s : public LineInfo { public: explicit ivl_discipline_s (perm_string name, ivl_dis_domain_t dom, ivl_nature_t pot, ivl_nature_t flow); ~ivl_discipline_s(); perm_string name() const { return name_; } ivl_dis_domain_t domain() const { return domain_; } ivl_nature_t potential() const { return potential_; } ivl_nature_t flow() const { return flow_; } private: perm_string name_; ivl_dis_domain_t domain_; ivl_nature_t potential_; ivl_nature_t flow_; private: // not implemented ivl_discipline_s(const ivl_discipline_s&); ivl_discipline_s& operator = (const ivl_discipline_s&); }; extern map natures; extern map disciplines; // Map access function name to the nature that it accesses. extern map access_function_nature; #endif /* IVL_discipline_H */ iverilog-10_1/disciplines.vams000066400000000000000000000021451265551621300165420ustar00rootroot00000000000000 // Standard definitions for Verilog-AMS `ifdef DISCIPLINES_VAMS `else `define DISCIPLINES_VAMS 1 discipline \logic ; domain discrete; enddiscipline discipline ddiscrete; domain discrete; enddiscipline nature Current; units = "A"; access = I; idt_nature = Charge; `ifdef CURRENT_ABSTOL abstol = `CURRENT_ABSTOL `else abstol = 1e-12; `endif endnature nature Charge; units = "coul"; access = Q; ddt_nature = Current; `ifdef CHARGE_ABSTOL abstol = `CHARGE_ABSTOL; `else abstol = 1e-14; `endif endnature nature Voltage; units = "V"; access = V; idt_nature = Flux; `ifdef VOLTAGE_ABSTOL abstol = `VOLTAGE_ABSTOL; `else abstol = 1e-6; `endif endnature nature Flux; units = "Wb"; access = Phi; ddt_nature = Voltage; `ifdef FLUX_ABSTOL abstol = `flux_ABSTOL; `else abstol = 1e-9; `endif endnature discipline electrical; potential Voltage; flow Current; enddiscipline discipline voltage; potential Voltage; enddiscipline discipline current; flow Current; enddiscipline `endif // !`ifdef DISCIPLINES_VAMS iverilog-10_1/dosify.c000066400000000000000000000036151265551621300150100ustar00rootroot00000000000000/* * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This is a simple program to make a dosified copy of the * original. That is, it converts unix style line ends to DOS * style. This is useful for installing text files. * * The exact substitution is to replace \n with \r\n. If the line * already ends with \r\n then it is not changed to \r\r\n. */ # include int main(int argc, char*argv[]) { FILE*ifile; FILE*ofile; int ch, pr; if (argc != 3) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } ifile = fopen(argv[1], "rb"); if (ifile == 0) { fprintf(stderr, "Unable to open %s for input.\n", argv[1]); return 2; } ofile = fopen(argv[2], "wb"); if (ofile == 0) { fprintf(stderr, "Unable to open %s for output.\n", argv[2]); fclose(ifile); return 2; } pr = 0; while ((ch = fgetc(ifile)) != EOF) { if ((ch == '\n') && (pr != '\r')) fputc('\r', ofile); fputc(ch, ofile); pr = ch; } fclose(ifile); fclose(ofile); return 0; } iverilog-10_1/driver-vpi/000077500000000000000000000000001265551621300154315ustar00rootroot00000000000000iverilog-10_1/driver-vpi/Makefile.in000066400000000000000000000053461265551621300175060ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ datarootdir = @datarootdir@ VPATH = $(srcdir) suffix = @install_suffix@ bindir = $(exec_prefix)/bin libdir = $(exec_prefix)/lib includedir = $(prefix)/include mandir = @mandir@ dllib=@DLLIB@ CC = @CC@ WINDRES = @WINDRES@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ O = main.o res.o all: iverilog-vpi@EXEEXT@ check: all clean: rm -f *.o config.h iverilog-vpi@EXEEXT@ res.rc distclean: clean rm -f Makefile config.log cppcheck: main.c cppcheck --enable=all -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=driver-vpi/$@ iverilog-vpi@EXEEXT@: $O $(CC) $(LDFLAGS) $O -o iverilog-vpi@EXEEXT@ @EXTRALIBS@ main.o: $(srcdir)/main.c config.h $(CC) $(CPPFLAGS) $(CFLAGS) -c $(srcdir)/main.c config.h: $(srcdir)/config.h.in Makefile sed -e 's;@IVLCC@;@CC@;' -e 's;@IVLCXX@;@CXX@;' \ -e 's;@SUFFIX@;$(suffix);g' \ -e 's;@IVLCFLAGS@;$(CFLAGS);' \ -e 's;@IVLCXXFLAGS@;$(CXXFLAGS);' \ -e 's;@SHARED@;@shared@;' $< > $@ # Windows specific... res.rc: $(srcdir)/res.rc.in ../version.exe sed -e 's;@PRODUCTVERSION@;'`../version.exe '%M,%n,0,0'`';' \ $(srcdir)/res.rc.in > $@ res.o: res.rc $(WINDRES) -i res.rc -o res.o # install: all installdirs $(bindir)/iverilog-vpi$(suffix)@EXEEXT@ $(bindir)/iverilog-vpi$(suffix)@EXEEXT@: ./iverilog-vpi@EXEEXT@ $(INSTALL_PROGRAM) ./iverilog-vpi@EXEEXT@ "$(bindir)/iverilog-vpi$(suffix)@EXEEXT@" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(bindir)" uninstall: rm -f $(bindir)/iverilog-vpi$(suffix)@EXEEXT@ iverilog-10_1/driver-vpi/config.h.in000066400000000000000000000010351265551621300174530ustar00rootroot00000000000000/* For now do not put the Icarus Verilog include or library paths here. * They are generated from a registry entry the user must set (-ivl=). * This may change in the future once I have thought about it more. */ #define IVERILOG_VPI_CC "@IVLCC@" #define IVERILOG_VPI_CXX "@IVLCXX@" #define IVERILOG_VPI_CFLAGS " @IVLCFLAGS@" #define IVERILOG_VPI_CXXFLAGS " @IVLCXXFLAGS@" #define IVERILOG_VPI_LDFLAGS "@SHARED@" #define IVERILOG_VPI_LDLIBS "-lveriuser@SUFFIX@ -lvpi@SUFFIX@" #define IVERILOG_SUFFIX "@SUFFIX@" iverilog-10_1/driver-vpi/main.c000066400000000000000000000431601265551621300165250ustar00rootroot00000000000000/* * Copyright (c) 2002 Gus Baldauf (gus@picturel.com) * Copyright (c) 2015 Martin Whitaker * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * iverilog-vpi.c * * this program provides the functionality of iverilog-vpi.sh under Windows */ #include #include #include #include #include #include static void setup_ivl_environment(void); static void assign(char **ptr, char *str); /* The compile options: compiler, flags, etc. are in here */ #include "config.h" /* pointers to global strings */ static struct global_strings { char *pCCSRC; /* list of C source files (*.c) */ char *pCXSRC; /* list of C++ source files (*.cc, *.cpp) */ char *pOBJ; /* list of object files */ char *pLIB; /* list of library files */ char *pINCS; /* list of include directories */ char *pDEFS; /* list of definitions */ char *pOUT; /* output file name (.vpi extension), if 0 length then no source files specified */ char *pMINGW; /* path to MinGW directory */ char *pIVL; /* path to IVL directory */ char *pCCFLAGS; /* compiler flags for compiling C source files */ char *pCXFLAGS; /* compiler flags for compiling C++ source files */ char *pLDLIBS; /* linker flags for final linking stage */ char *pCCNAME; /* base name of compiler */ char *pLD; /* what to use for a linker */ } gstr; static void deInitDynString(char *str) { free(str); } /* when finished, free allocated memory and return error code */ static void myExit(int exitVal) { deInitDynString(gstr.pCCSRC); deInitDynString(gstr.pCXSRC); deInitDynString(gstr.pOBJ); deInitDynString(gstr.pLIB); deInitDynString(gstr.pINCS); deInitDynString(gstr.pDEFS); deInitDynString(gstr.pOUT); deInitDynString(gstr.pMINGW); deInitDynString(gstr.pIVL); deInitDynString(gstr.pCCFLAGS); deInitDynString(gstr.pCXFLAGS); deInitDynString(gstr.pLDLIBS); deInitDynString(gstr.pCCNAME); deInitDynString(gstr.pLD); exit(exitVal); } /* display usage summary and exit */ static void usage(void) { fprintf(stderr, "usage: iverilog-vpi" IVERILOG_SUFFIX " [options] [src and obj files]...\n"); fprintf(stderr, " or iverilog-vpi" IVERILOG_SUFFIX " -mingw=dir\n"); myExit(1); } static void initDynString(char **str) { *str = (char *) malloc(1); if (!*str) { fprintf(stderr, "error: out of memory\n"); myExit(4); } *str[0] = 0; } /* initialize dynamic memory buffers */ static void init(void) { char *ptr; initDynString(&gstr.pCCSRC); initDynString(&gstr.pCXSRC); initDynString(&gstr.pOBJ); initDynString(&gstr.pLIB); initDynString(&gstr.pINCS); initDynString(&gstr.pDEFS); initDynString(&gstr.pOUT); initDynString(&gstr.pMINGW); initDynString(&gstr.pIVL); initDynString(&gstr.pCCFLAGS); initDynString(&gstr.pCXFLAGS); initDynString(&gstr.pLDLIBS); initDynString(&gstr.pCCNAME); initDynString(&gstr.pLD); /* Get the base name of the C compiler. */ assign(&gstr.pCCNAME, IVERILOG_VPI_CC); ptr = strchr(gstr.pCCNAME, ' '); if (ptr != NULL) *ptr = '\0'; /* By default use the C compiler to link the programs. */ assign(&gstr.pLD, IVERILOG_VPI_CC); } /* return true if "str" is terminated with with "end", case insensitive */ static int endsIn (char *end, char *str) { char *ext; if (strlen(end) >= strlen(str)) return 0; ext = str + (strlen(str) - strlen(end)); return stricmp(end, ext) ? 0 : 1; } /* return true if "str" begins with "prefix", case insensitive */ static int startsWith (char *prefix, char *str) { if (strlen(prefix) >= strlen(str)) return 0; return strnicmp(prefix, str, strlen(prefix)) ? 0 : 1; } /* append "app" to "ptr", allocating memory as needed */ /* if count is zero, then copy all characters of "app" */ static void appendn (char **ptr, char *app, size_t count) { char *nptr = (char *) realloc(*ptr, strlen(*ptr) + (count ? count : strlen(app)) + 1); if (nptr == NULL) { fprintf(stderr, "error: out of memory\n"); free(*ptr); myExit(4); } *ptr = nptr; if (count) strncat(*ptr, app, count); else strcat(*ptr, app); } /* append "app" to "ptr", allocating memory as needed */ static void append (char **ptr, char *app) { appendn(ptr, app, 0); } /* if the string does not end with a backslash, add one */ static void appendBackSlash(char **str) { if ((*str)[strlen(*str)-1] != '\\') append(str, "\\"); } /* copy count characters of "str" to "ptr", allocating memory as needed */ /* if count is zero, then copy all characters of "str" */ static void assignn (char **ptr, char *str, size_t count) { char *nptr = (char *) realloc(*ptr, (count ? count : strlen(str)) + 1); if (nptr == NULL) { fprintf(stderr, "error: out of memory\n"); free(*ptr); myExit(4); } *ptr = nptr; if (count) { strncpy(*ptr, str, count); (*ptr)[count] = 0; } else strcpy(*ptr, str); } /* copy count characters of "str" to "ptr", allocating memory as needed */ static void assign (char **ptr, char *str) { assignn(ptr, str, 0); } /* get a copy of a Icarus Verilog registry string key */ static int GetRegistryKey(char *key, char **value) { long lrv; HKEY hkKey; char *regKeyBuffer; DWORD regKeyType, regKeySize; lrv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Icarus Verilog", 0, KEY_QUERY_VALUE, &hkKey); if (lrv != ERROR_SUCCESS) return 0; lrv = RegQueryValueEx(hkKey, key, NULL, ®KeyType, NULL, ®KeySize); if ((lrv != ERROR_SUCCESS) || (regKeyType != REG_SZ) || (!regKeySize)) { lrv = RegCloseKey(hkKey); return 0; } regKeyBuffer = (char *) malloc(regKeySize+1); if (!regKeyBuffer) { lrv = RegCloseKey(hkKey); fprintf(stderr, "error: out of memory\n"); myExit(4); } regKeyBuffer[regKeySize] = 0; /* makes sure there is a trailing NULL */ /* This needs an unsigned char *, but for MinGW the char is signed. */ lrv = RegQueryValueEx(hkKey, key, NULL, ®KeyType, (unsigned char *) regKeyBuffer, ®KeySize); if ((lrv != ERROR_SUCCESS) || (regKeyType != REG_SZ) || (!regKeySize)) { lrv = RegCloseKey(hkKey); free(regKeyBuffer); return 0; } RegCloseKey(hkKey); assign(value, regKeyBuffer); free(regKeyBuffer); return 1; } /* store a copy of a Icarus Verilog registry string key */ static void SetRegistryKey(char *key, char *value) { long lrv; HKEY hkKey; DWORD res; lrv = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\Icarus Verilog", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkKey, &res); if (lrv != ERROR_SUCCESS) { char message[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, lrv, LANG_USER_DEFAULT, message, sizeof(message), NULL); fprintf(stderr, "error: couldn't write to registry - %s\n", message); if (lrv == ERROR_ACCESS_DENIED) { fprintf(stderr, " try running as administrator\n"); } return; } /* This needs an unsigned char *, but for MinGW the char is signed. */ RegSetValueEx(hkKey, key, 0, REG_SZ, (unsigned char *) value, strlen(value)+1); RegCloseKey(hkKey); printf("info: storing %s in Windows' registry entry\n", value); printf(" HKEY_LOCAL_MACHINE\\Software\\Icarus Verilog\\%s\n", key); } /* parse the command line, assign results to global variable strings */ static int parse(int argc, char *argv[]) { int idx, srcFileCnt=0; char dot_c_ext[] = ".c"; char dot_cc_ext[] = ".cc"; char dot_cpp_ext[] = ".cpp"; char dot_o_ext[] = ".o"; char name_option[] = "--name="; char lib_option[] = "-l"; char inc_option[] = "-I"; char mingw_option[] = "-mingw="; char def_option[] = "-D"; if (argc == 1) return 0; for (idx=1; idx $@ # Build this in two steps to avoid parallel build issues (see pr3462585) cfparse.c: $(srcdir)/cfparse.y $(YACC) --verbose -t -p cf -d -o $@ $< cfparse.h: cfparse.c %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep main.o: main.c globals.h $(srcdir)/../version_base.h ../version_tag.h Makefile $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c -DIVL_ROOT='"@libdir@/ivl$(suffix)"' -DIVL_SUFFIX='"$(suffix)"' -DIVL_INC='"@includedir@"' -DIVL_LIB='"@libdir@"' -DDLLIB='"@DLLIB@"' $(srcdir)/main.c mv $*.d dep cflexor.o: cflexor.c cfparse.h iverilog.man: $(srcdir)/iverilog.man.in ../version.exe ../version.exe `head -1 $(srcdir)/iverilog.man.in`'\n' > $@ tail -n +2 $(srcdir)/iverilog.man.in >> $@ iverilog.ps: iverilog.man $(MAN) -t ./iverilog.man > iverilog.ps iverilog.pdf: iverilog.ps $(PS2PDF) iverilog.ps iverilog.pdf ifeq (@MINGW32@,yes) ifeq ($(MAN),none) INSTALL_DOC = $(mandir)/man1/iverilog$(suffix).1 else ifeq ($(PS2PDF),none) INSTALL_DOC = $(mandir)/man1/iverilog$(suffix).1 else INSTALL_DOC = $(prefix)/iverilog$(suffix).pdf $(mandir)/man1/iverilog$(suffix).1 all: iverilog.pdf endif endif INSTALL_DOCDIR = $(mandir)/man1 else INSTALL_DOC = $(mandir)/man1/iverilog$(suffix).1 INSTALL_DOCDIR = $(mandir)/man1 endif install: all installdirs $(bindir)/iverilog$(suffix)@EXEEXT@ $(INSTALL_DOC) $(bindir)/iverilog$(suffix)@EXEEXT@: ./iverilog@EXEEXT@ $(INSTALL_PROGRAM) ./iverilog@EXEEXT@ "$(DESTDIR)$(bindir)/iverilog$(suffix)@EXEEXT@" $(mandir)/man1/iverilog$(suffix).1: iverilog.man $(INSTALL_DATA) iverilog.man "$(DESTDIR)$(mandir)/man1/iverilog$(suffix).1" $(prefix)/iverilog$(suffix).pdf: iverilog.pdf $(INSTALL_DATA) iverilog.pdf "$(DESTDIR)$(prefix)/iverilog$(suffix).pdf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(INSTALL_DOCDIR)" uninstall: rm -f "$(DESTDIR)$(bindir)/iverilog$(suffix)@EXEEXT@" rm -f "$(DESTDIR)$(mandir)/man1/iverilog$(suffix).1" "$(DESTDIR)$(prefix)/iverilog$(suffix).pdf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/driver/cflexor.lex000066400000000000000000000155051265551621300170170ustar00rootroot00000000000000%option prefix="cf" %option nounput %option noinput %{ /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "cfparse.h" # include "cfparse_misc.h" # include "globals.h" # include static int comment_enter; static char* trim_trailing_white(char*txt, int trim); /* * Mostly copied from the flex manual. Do not make this arbitrary * depth without checking for looping files. */ #define MAX_CMDFILE_DEPTH 15 typedef struct t_cmdfile { char *cmdfile; YY_BUFFER_STATE buffer; } s_cmdfile; s_cmdfile cmdfile_stack[MAX_CMDFILE_DEPTH]; int cmdfile_stack_ptr = 0; %} %x CCOMMENT %x LCOMMENT %x PLUS_ARGS %x FILE_NAME %% /* Accept C++ style comments. */ "//".* { comment_enter = YY_START; BEGIN(LCOMMENT); } . { yymore(); } \n { cflloc.first_line += 1; BEGIN(comment_enter); } /* Accept C style comments. */ "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); } . { yymore(); } \n { cflloc.first_line += 1; yymore(); } "*/" { BEGIN(comment_enter); } /* Accept shell type comments. */ ^"#".* { ; } /* Skip white space. */ [ \t\f\r] { ; } /* Skip line ends, but also count the line. */ \n { cflloc.first_line += 1; } "+define+" { BEGIN(PLUS_ARGS); return TOK_DEFINE; } "+incdir+" { BEGIN(PLUS_ARGS); return TOK_INCDIR; } "+integer-width+" { BEGIN(PLUS_ARGS); return TOK_INTEGER_WIDTH; } "+libdir+" { BEGIN(PLUS_ARGS); return TOK_LIBDIR; } "+libdir-nocase+" { BEGIN(PLUS_ARGS); return TOK_LIBDIR_NOCASE; } "+libext+" { BEGIN(PLUS_ARGS); return TOK_LIBEXT; } "+parameter+" { BEGIN(PLUS_ARGS); return TOK_PARAMETER; } "+timescale+" { BEGIN(PLUS_ARGS); return TOK_TIMESCALE; } "+vhdl-work+" { BEGIN(PLUS_ARGS); return TOK_VHDL_WORK; } "+vhdl-libdir+" { BEGIN(PLUS_ARGS); return TOK_VHDL_LIBDIR; } "+width-cap+" { BEGIN(PLUS_ARGS); return TOK_WIDTH_CAP; } /* If it is not any known plus-flag, return the generic form. */ "+"[^\n \t\b\f\r+]* { cflval.text = strdup(yytext); BEGIN(PLUS_ARGS); return TOK_PLUSWORD; } /* Once in PLUS_ARGS mode, words are delimited by + characters. White space and line end terminate PLUS_ARGS mode, but + terminates only the word. */ [^\n \t\b\f\r+]* { cflval.text = strdup(yytext); return TOK_PLUSARG; } /* Within plusargs, this is a delimiter. */ "+" { } /* White space end plus_args mode. */ [ \t\b\f\r] { BEGIN(0); } \n { cflloc.first_line += 1; BEGIN(0); } /* Notice the -a flag. */ "-a" { return TOK_Da; } /* Notice the -c or -f flag. */ "-c" { return TOK_Dc; } "-f" { return TOK_Dc; } /* Notice the -v flag. */ "-v" { return TOK_Dv; } /* Notice the -y flag. */ "-y" { return TOK_Dy; } /* This rule matches paths and strings that may be file names. This is a little bit tricky, as we don't want to mistake a comment for a string word. */ "/"[\r\n] { /* Special case of file name "/" */ cflval.text = trim_trailing_white(yytext, 0); return TOK_STRING; } "/"[^\*\/] { /* A file name that starts with "/". */ yymore(); BEGIN(FILE_NAME); } [^/\n \t\b\r+-][^/\n\r]* { /* A file name that starts with other than "/" */ yymore(); BEGIN(FILE_NAME); } "//" { /* Found a trailing comment. Returning the terminated name. */ cflval.text = trim_trailing_white(yytext, 2); BEGIN(LCOMMENT); return TOK_STRING; } "/"?[^/\n\r]* { yymore(); /* not a comment... continuing */; } [\n\r] { /* No trailing comment. Return the file name. */ cflval.text = trim_trailing_white(yytext, 0); BEGIN(0); return TOK_STRING; } /* Fallback match. */ . { return yytext[0]; } /* At the end of file switch back to the previous buffer. */ <> { if (--cmdfile_stack_ptr < 0) { free(current_file); yyterminate(); } else { yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(cmdfile_stack[cmdfile_stack_ptr].buffer); free(current_file); current_file = cmdfile_stack[cmdfile_stack_ptr].cmdfile; } } %% static char* trim_trailing_white(char*text, int trim) { char*cp = text + strlen(text); while (cp > text && trim > 0) { trim -= 1; cp -= 1; *cp = 0; } while (cp > text && strchr("\n\r\t\b", cp[-1])) cp -= 1; cp[0] = 0; return strdup(text); } int yywrap() { return 1; } void switch_to_command_file(const char *file) { char path[4096]; if (cmdfile_stack_ptr >= MAX_CMDFILE_DEPTH) { fprintf(stderr, "Error: command files nested too deeply (%d) " "at %s:%d.\n", MAX_CMDFILE_DEPTH, current_file, cflloc.first_line); exit(1); } cmdfile_stack[cmdfile_stack_ptr].buffer = YY_CURRENT_BUFFER; cmdfile_stack[cmdfile_stack_ptr].cmdfile = current_file; cmdfile_stack_ptr += 1; /* * If this is a relative file then add the current path to the * file name. */ if (file[0] != '/') { char *cp; strcpy(path, current_file); cp = strrchr(path, '/'); if (cp == 0) strcpy(path, file); /* A base file. */ else { *(cp+1) = '\0'; strcat(path, file); } } else strcpy(path, file); /* An absolute path. */ yyin = fopen(path, "r"); if (yyin == NULL) { fprintf(stderr, "Error: unable to read nested command file (%s) " "at %s:%d.\n", path, current_file, cflloc.first_line); exit(1); } current_file = strdup(path); yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); cflloc.first_line = 1; } void cfreset(FILE*fd, const char*path) { yyin = fd; yyrestart(fd); current_file = strdup(path); cflloc.first_line = 1; } /* * Modern version of flex (>=2.5.9) can clean up the scanner data. */ void destroy_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif } iverilog-10_1/driver/cfparse.y000066400000000000000000000140421265551621300164530ustar00rootroot00000000000000%{ /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "globals.h" # include "cfparse_misc.h" # include # include # include # include # include "ivl_alloc.h" /* * This flag is set to 0, 1 or 2 if file names are to be translated to * uppercase(1) or lowercase(2). */ static int setcase_filename_flag = 0; static void translate_file_name(char*text) { switch (setcase_filename_flag) { case 0: break; case 1: while (*text) { *text = toupper((int)*text); text += 1; } break; case 2: while (*text) { *text = tolower((int)*text); text += 1; } break; } } %} %union { char*text; }; %token TOK_Da TOK_Dc TOK_Dv TOK_Dy %token TOK_DEFINE TOK_INCDIR TOK_INTEGER_WIDTH TOK_LIBDIR TOK_LIBDIR_NOCASE %token TOK_LIBEXT TOK_PARAMETER TOK_TIMESCALE TOK_VHDL_WORK TOK_VHDL_LIBDIR %token TOK_WIDTH_CAP %token TOK_PLUSARG TOK_PLUSWORD TOK_STRING %% start : | item_list ; item_list : item_list item | item ; item /* Absent any other matching, a token string is taken to be the name of a source file. Add the file to the file list. */ : TOK_STRING { char*tmp = substitutions($1); translate_file_name(tmp); process_file_name(tmp, 0); free($1); free(tmp); } /* The -a flag is completely ignored. */ | TOK_Da { } /* Match a -c or -f */ | TOK_Dc TOK_STRING { char*tmp = substitutions($2); translate_file_name(tmp); switch_to_command_file(tmp); free($2); free(tmp); } /* The -v flag is ignored, and the is processed as an ordinary source file. */ | TOK_Dv TOK_STRING { char*tmp = substitutions($2); translate_file_name(tmp); process_file_name(tmp, 1); free($2); free(tmp); } /* This rule matches "-y " sequences. This does the same thing as -y on the command line, so add the path to the library directory list. */ | TOK_Dy TOK_STRING { char*tmp = substitutions($2); process_library_switch(tmp); free($2); free(tmp); } | TOK_LIBDIR TOK_PLUSARG { char*tmp = substitutions($2); process_library_switch(tmp); free($2); free(tmp); } | TOK_LIBDIR_NOCASE TOK_PLUSARG { char*tmp = substitutions($2); process_library_nocase_switch(tmp); free($2); free(tmp); } | TOK_PARAMETER TOK_PLUSARG { char*tmp = substitutions($2); process_parameter(tmp); free($2); free(tmp); } /* The +timescale token is used to set the default timescale for the simulator. */ | TOK_TIMESCALE TOK_PLUSARG { char*tmp = substitutions($2); process_timescale(tmp); free($2); free(tmp); } | TOK_DEFINE TOK_PLUSARG { process_define($2); free($2); } | TOK_VHDL_WORK TOK_PLUSARG { char*tmp = substitutions($2); vhdlpp_work = tmp; free($2); } | TOK_VHDL_LIBDIR TOK_PLUSARG { char*tmp = substitutions($2); vhdlpp_libdir = realloc(vhdlpp_libdir, (vhdlpp_libdir_cnt+1)*sizeof(char*)); vhdlpp_libdir[vhdlpp_libdir_cnt] = tmp; vhdlpp_libdir_cnt += 1; free($2); } /* The +incdir token introduces a list of + arguments that are the include directories to search. */ | TOK_INCDIR inc_args /* The +libext token introduces a list of + arguments that become individual -Y flags to ivl. */ | TOK_LIBEXT libext_args /* These are various misc flags that are supported. */ | TOK_INTEGER_WIDTH TOK_PLUSARG { char*tmp = substitutions($2); free($2); integer_width = strtoul(tmp,0,10); free(tmp); } | TOK_WIDTH_CAP TOK_PLUSARG { char*tmp = substitutions($2); free($2); width_cap = strtoul(tmp,0,10); free(tmp); } /* The + tokens that are not otherwise matched, are ignored. The skip_args rule arranges for all the argument words to be consumed. */ | TOK_PLUSWORD skip_args { fprintf(stderr, "%s:%u: Ignoring %s\n", @1.text, @1.first_line, $1); free($1); } | TOK_PLUSWORD { if (strcmp($1, "+toupper-filenames") == 0) { setcase_filename_flag = 1; } else if (strcmp($1, "+tolower-filenames") == 0) { setcase_filename_flag = 2; } else { fprintf(stderr, "%s:%u: Ignoring %s\n", @1.text, @1.first_line, $1); } free($1); } | error { fprintf(stderr, "Error: unable to parse line %u in " "%s.\n", cflloc.first_line, current_file); return 1; } ; /* inc_args are +incdir+ arguments in order. */ inc_args : inc_args inc_arg | inc_arg ; inc_arg : TOK_PLUSARG { char*tmp = substitutions($1); process_include_dir(tmp); free($1); free(tmp); } ; /* inc_args are +incdir+ arguments in order. */ libext_args : libext_args libext_arg | libext_arg ; libext_arg : TOK_PLUSARG { process_library2_switch($1); free($1); } ; /* skip_args are arguments to a +word flag that is not otherwise parsed. This rule matches them and releases the strings, so that they can be safely ignored. */ skip_args : skip_args skip_arg | skip_arg ; skip_arg : TOK_PLUSARG { free($1); } ; %% int yyerror(const char*msg) { (void)msg; /* Parameter is not used. */ return 0; } iverilog-10_1/driver/cfparse_misc.h000066400000000000000000000027671265551621300174600ustar00rootroot00000000000000#ifndef IVL_cfparse_misc_H #define IVL_cfparse_misc_H /* * Copyright (c) 2001-2014 Picture Elements, Inc. * Stephen Williams (steve@picturel.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The vlltype supports the passing of detailed source file location * information between the lexical analyzer and the parser. Defining * YYLTYPE compels the lexor to use this type and not something other. */ struct cfltype { unsigned first_line; unsigned first_column; unsigned last_line; unsigned last_column; const char*text; }; # define YYLTYPE struct cfltype int cflex(void); int cferror(const char *); int cfparse(void); void switch_to_command_file(const char *); void destroy_lexor(void); char *current_file; #endif /* IVL_cfparse_misc_H */ iverilog-10_1/driver/globals.h000066400000000000000000000040061265551621300164310ustar00rootroot00000000000000#ifndef IVL_globals_H #define IVL_globals_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include /* This is the integer-width argument that will be passed to ivl. */ extern unsigned integer_width; /* This is the width-cap argument that will be passed to ivl. */ extern unsigned width_cap; extern const char*vhdlpp_work; extern const char**vhdlpp_libdir; extern unsigned vhdlpp_libdir_cnt; /* Perform variable substitutions on the string. */ extern char* substitutions(const char*str); /* Add the name to the list of source files. */ extern void process_file_name(const char*name, int lib_flag); /* Add the name to the list of library directories. */ extern void process_library_switch(const char*name); extern void process_library_nocase_switch(const char*name); extern void process_library2_switch(const char*name); /* Add a new include file search directory */ extern void process_include_dir(const char*name); /* Add a new -D define. */ extern void process_define(const char*name); /* Add a new parameter definition */ extern void process_parameter(const char*name); /* Set the default timescale for the simulator. */ extern void process_timescale(const char*ts_string); #endif /* IVL_globals_H */ iverilog-10_1/driver/iverilog.man.in000066400000000000000000000530351265551621300175650ustar00rootroot00000000000000.TH iverilog 1 "Aug 7th, 2015" "" "Version %M.%n%E" .SH NAME iverilog - Icarus Verilog compiler .SH SYNOPSIS .B iverilog [\-ESVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] [\-Pparameter=value] [\-pflag=value] [\-dname] [\-g1995|\-g2001|\-g2005|\-g2005-sv|\-g2009|\-g2012|\-g] [\-Iincludedir] [\-mmodule] [\-M[mode=]file] [\-Nfile] [\-ooutputfilename] [\-stopmodule] [\-ttype] [\-Tmin/typ/max] [\-Wclass] [\-ypath] sourcefile .SH DESCRIPTION .PP \fIiverilog\fP is a compiler that translates Verilog source code into executable programs for simulation, or other netlist formats for further processing. The currently supported targets are \fIvvp\fP for simulation, and \fIfpga\fP for synthesis. Other target types are added as code generators are implemented. .SH OPTIONS \fIiverilog\fP accepts the following options: .TP 8 .B -B\fIbase\fP The \fIiverilog\fP program uses external programs and configuration files to preprocess and compile the Verilog source. Normally, the path used to locate these tools is built into the \fIiverilog\fP program. However, the \fB\-B\fP switch allows the user to select a different set of programs. The path given is used to locate \fIivlpp\fP, \fIivl\fP, code generators and the VPI modules. .TP 8 .B -c\fIfile\fP -f\fIfile\fP These flags specify an input file that contains a list of Verilog source files. This is similar to the \fIcommand file\fP of other Verilog simulators, in that it is a file that contains the file names instead of taking them on the command line. See \fBCommand Files\fP below. .TP 8 .B -D\fImacro\fP Defines macro \fImacro\fP with the string `1' as its definition. This form is normally only used to trigger ifdef conditionals in the Verilog source. .TP 8 .B -D\fImacro=defn\fP Defines macro \fImacro\fP as \fIdefn\fP. .TP 8 .B -P\fIparameter=value\fP Override (i.e. defparam) a parameter in a root module. This allows the user to override at compile time (defparam) a parameter in a root module instance. For example, \fB\-Pmain.foo=2\fP overrides the parameter foo in the root instance main with the value 2. .TP 8 .B -d\fIname\fP Activate a class of compiler debugging messages. The \fB\-d\fP switch may be used as often as necessary to activate all the desired messages. Supported names are scopes, eval_tree, elaborate, and synth2; any other names are ignored. .TP 8 .B -E Preprocess the Verilog source, but do not compile it. The output file is the Verilog input, but with file inclusions and macro references expanded and removed. This is useful, for example, to preprocess Verilog source for use by other compilers. .TP 8 .B -g1995\fI|\fP-g2001\fI|\fP-g2001-noconfig\fI|\fP-g2005\fI|\fP-g2005-sv\fI|\fP-g2009\fI|\fP-g2012 Select the Verilog language \fIgeneration\fP to support in the compiler. This selects between \fIIEEE1364\-1995\fP, \fIIEEE1364\-2001\fP, \fIIEEE1364\-2005\fP, \fIIEEE1800\-2005\fP, \fIIEEE1800\-2009\fP, or \fIIEEE1800\-2012\fP. Icarus Verilog currently defaults to the \fIIEEE1364\-2005\fP generation of the language. This flag is used to restrict the language to a set of keywords/features, this allows simulation of older Verilog code that may use newer keywords and for compatibility with other tools. Much of the \fIIEEE1800\fP generations functionality is not currently supported. The \fIIEEE1800\fP generations do parse all the keywords, so they can be used to verify that \fIIEEE1364\fP compliant Verilog code does not use any of the new \fIIEEE1800\fP keywords. .TP 8 .B -gverilog-ams\fI|\fP-gno-verilog-ams Enable or disable (default) support for Verilog\-AMS. Very little Verilog\-AMS specific functionality is currently supported. .TP 8 .B -gspecify\fI|\fP-gno-specify Enable or disable (default) specify block support. When enabled, specify block code is elaborated. When disabled, specify blocks are parsed but ignored. Specify blocks are commonly not needed for RTL simulation, and in fact can hurt performance of the simulation. However, disabling specify blocks reduces accuracy of full-timing simulations. .TP 8 .B -gstd-include\fI|\fP-gno-std-include Enable (default) or disable the search of a standard installation include directory after all other explicit include directories. This standard include directory is a convenient place to install standard header files that a Verilog program may include. .TP 8 .B -grelative-include\fI|\fP-gno-relative-include Enable or disable (default) adding the local files directory to the beginning of the include file search path. This allows files to be included relative to the current file not the more common files are only found in the working directory or in the specified include file search path. .TP 8 .B -gxtypes\fI|\fP-gno-xtypes Enable (default) or disable support for extended types. Enabling extended types allows for new types that are supported by Icarus Verilog as extensions beyond the baseline Verilog. It may be necessary to disable extended types if compiling code that clashes with the few new keywords used to implement the type system. .TP 8 .B -gio-range-error\fI|\fP-gno-io-range-error The standards requires that a vectored port have matching ranges for its port declaration as well as any net/register declaration. It was common practice in the past to only specify the range for the net/register declaration and some tools still allow this. By default any mismatch is reported as a error. Using \fB\-gno\-io\-range\-error\fP will produce a warning instead of a fatal error for the case of a vectored net/register and a scalar port declaration. .TP 8 .B -gstrict-ca-eval\fI|\fP-gno-strict-ca-eval The standard requires that if any input to a continuous assignment expression changes value, the entire expression is re-evaluated. By default, parts of the expression that do not depend on the changed input value(s) are not re-evaluated. If an expression contains a call to a function that doesn't depend solely on its input values or that has side effects, the resulting behavior will differ from that required by the standard. Using \fI\-gstrict\-ca\-eval\fP will force standard compliant behavior (with some loss in performance). .TP 8 .B -gstrict-expr-width\fI|\fP-gno-strict-expr-width Enable or disable (default) strict compliance with the standard rules for determining expression bit lengths. When disabled, the RHS of a parameter assignment is evaluated as a lossless expression, as is any expression containing an unsized constant number, and unsized constant numbers are not truncated to integer width. .TP 8 .B -I\fIincludedir\fP Append directory \fIincludedir\fP to list of directories searched for Verilog include files. The \fB\-I\fP switch may be used many times to specify several directories to search, the directories are searched in the order they appear on the command line. .TP 8 .B -M\fIpath\fP This is equivalent to \fB\-Mall=path\fP. Preserved for backwards compatibility. .TP 8 .B -M\fImode=path\fP Write into the file specified by path a list of files that contribute to the compilation of the design. If \fBmode\fP is \fBall\fP or \fBprefix\fP, this includes files that are included by include directives and files that are automatically loaded by library support as well as the files explicitly specified by the user. If \fBmode\fP is \fBinclude\fP, only files that are included by include directives are listed. If \fBmode\fP is \fBmodule\fP, only files that are specified by the user or that are automatically loaded by library support are listed. The output is one file name per line, with no leading or trailing space. If \fBmode\fP is \fBprefix\fP, files that are included by include directives are prefixed by "I " and other files are prefixed by "M ". .TP 8 .B -m\fImodule\fP Add this module to the list of VPI modules to be loaded by the simulation. Many modules can be specified, and all will be loaded, in the order specified. The system module is implicit and always included. If a System Function Table file (.sft) exists for the module it will be loaded automatically. .TP 8 .B -N\fIpath\fP This is used for debugging the compiler proper. Dump the final netlist form of the design to the specified file. It otherwise does not affect operation of the compiler. The dump happens after the design is elaborated and optimized. .TP 8 .B -o \fIfilename\fP Place output in the file \fIfilename\fP. If no output file name is specified, \fIiverilog\fP uses the default name \fBa.out\fP. .TP 8 .B -p\fIflag=value\fP Assign a value to a target specific flag. The \fB\-p\fP switch may be used as often as necessary to specify all the desired flags. The flags that are used depend on the target that is selected, and are described in target specific documentation. Flags that are not used are ignored. .TP 8 .B -S Synthesize. Normally, if the target can accept behavioral descriptions the compiler will leave processes in behavioral form. The \fB\-S\fP switch causes the compiler to perform synthesis even if it is not necessary for the target. If the target type is a netlist format, the \fB\-S\fP switch is unnecessary and has no effect. .TP 8 .B -s \fItopmodule\fP Specify the top level module to elaborate. Icarus Verilog will by default choose modules that are not instantiated in any other modules, but sometimes that is not sufficient, or instantiates too many modules. If the user specifies one or more root modules with \fB\-s\fP flags, then they will be used as root modules instead. .TP 8 .B -T\fImin|typ|max\fP Use this switch to select min, typ or max times from min:typ:max expressions. Normally, the compiler will simply use the typ value from these expressions (printing a warning for the first ten it finds) but this switch will tell the compiler explicitly which value to use. This will suppress the warning that the compiler is making a choice. .TP 8 .B -t\fItarget\fP Use this switch to specify the target output format. See the \fBTARGETS\fP section below for a list of valid output formats. .TP 8 .B -v Turn on verbose messages. This will print the command lines that are executed to perform the actual compilation, along with version information from the various components, as well as the version of the product as a whole. You will notice that the command lines include a reference to a key temporary file that passes information to the compiler proper. To keep that file from being deleted at the end of the process, provide a file name of your own in the environment variable \fBIVERILOG_ICONFIG\fP. If the selected target is \fIvvp\fP, the \fB\-v\fP switch is appended to the shebang line in the compiler output file, so directly executing the compiler output file will turn on verbose messages in \fIvvp\fP. This extra verbosity can be avoided by using the \fIvvp\fP command to indirectly execute the compiler output file. .TP 8 .B -V Print the version of the compiler, and exit. .TP 8 .B -W\fIclass\fP Turn on different classes of warnings. See the \fBWARNING TYPES\fP section below for descriptions of the different warning groups. If multiple \fB\-W\fP switches are used, the warning set is the union of all the requested classes. .TP 8 .B -y\fIlibdir\fP Append the directory to the library module search path. When the compiler finds an undefined module, it looks in these directories for files with the right name. .TP 8 .B -Y\fIsuffix\fP Add suffix to the list of accepted file name suffixes used when searching a library for cells. The list defaults to the single entry \fI.v\fP. .SH MODULE LIBRARIES The Icarus Verilog compiler supports module libraries as directories that contain Verilog source files. During elaboration, the compiler notices the instantiation of undefined module types. If the user specifies library search directories, the compiler will search the directory for files with the name of the missing module type. If it finds such a file, it loads it as a Verilog source file, they tries again to elaborate the module. Library module files should contain only a single module, but this is not a requirement. Library modules may reference other modules in the library or in the main design. .SH TARGETS The Icarus Verilog compiler supports a variety of targets, for different purposes, and the \fB\-t\fP switch is used to select the desired target. .TP 8 .B null The null target causes no code to be generated. It is useful for checking the syntax of the Verilog source. .TP 8 .B vvp This is the default. The vvp target generates code for the vvp runtime. The output is a complete program that simulates the design but must be run by the \fBvvp\fP command. The -pfileline=1 option can be used to add procedural statement debugging opcodes to the generated code. .TP 8 .B fpga This is a synthesis target that supports a variety of fpga devices, mostly by EDIF format output. The Icarus Verilog fpga code generator can generate complete designs or EDIF macros that can in turn be imported into larger designs by other tools. The \fBfpga\fP target implies the synthesis \fB\-S\fP flag. .TP 8 .B vhdl This target produces a VHDL translation of the Verilog netlist. The output is a single file containing VHDL entities corresponding to the modules in the Verilog source code. Note that only a subset of the Verilog language is supported. See the wiki for more information. .SH "WARNING TYPES" These are the types of warnings that can be selected by the \fB\-W\fP switch. All the warning types (other than \fBall\fP) can also be prefixed with \fBno\-\fP to turn off that warning. This is most useful after a \fB\-Wall\fP argument to suppress isolated warning types. .TP 8 .B all This enables the anachronisms, implicit, portbind, select\-range, timescale, and sensitivity\-entire\-array warning categories. .TP 8 .B anachronisms This enables warnings for use of features that have been deprecated or removed in the selected generation of the Verilog language. .TP 8 .B implicit This enables warnings for creation of implicit declarations. For example, if a scalar wire X is used but not declared in the Verilog source, this will print a warning at its first use. .TP 8 .B portbind This enables warnings for ports of module instantiations that are not connected but probably should be. Dangling input ports, for example, will generate a warning. .TP 8 .B select-range This enables warnings for constant out of bound selects. This includes partial or fully out of bound selects as well as a select containing a 'bx or 'bz in the index. .TP 8 .B timescale This enables warnings for inconsistent use of the timescale directive. It detects if some modules have no timescale, or if modules inherit timescale from another file. Both probably mean that timescales are inconsistent, and simulation timing can be confusing and dependent on compilation order. .TP 8 .B infloop This enables warnings for \fRalways\fP statements that may have runtime infinite loops (has paths with no or zero delay). This class of warnings is not included in \fB\-Wall\fP and hence does not have a \fBno\-\fP variant. A fatal error message will always be printed when the compiler can determine that there will definitely be an infinite loop (all paths have no or zero delay). When you suspect an always statement is producing a runtime infinite loop use this flag to find the always statements that need to have their logic verified. It is expected that many of the warnings will be false positives, since the code treats the value of all variables and signals as indeterminate. .TP 8 .B sensitivity-entire-vector This enables warnings for when a part select within an "always @*" statement results in the entire vector being added to the implicit sensitivity list. Although this behaviour is prescribed by the IEEE standard, it is not what might be expected and can have performance implications if the vector is large. .TP 8 .B sensitivity-entire-array This enables warnings for when a word select within an "always @*" statement results in the entire array being added to the implicit sensitivity list. Although this behaviour is prescribed by the IEEE standard, it is not what might be expected and can have performance implications if the array is large. .SH "SYSTEM FUNCTION TABLE FILES" If the source file name as a \fB.sft\fP suffix, then it is taken to be a system function table file. A System function table file is used to describe to the compiler the return types for system functions. This is necessary because the compiler needs this information to elaborate expressions that contain these system functions, but cannot run the sizetf functions since it has no run-time. The format of the table is ASCII, one function per line. Empty lines are ignored, and lines that start with the '\fI#\fP' character are comment lines. Each non-comment line starts with the function name, then the vpi type (i.e. vpiSysFuncReal). The following types are supported: .TP 8 .B vpiSysFuncReal The function returns a real/realtime value. .TP 8 .B vpiSysFuncInt The function returns an integer. .TP 8 .B vpiSysFuncSized The function returns a vector with the given width, and is signed or unsigned according to the flag. .SH "COMMAND FILES" The command file allows the user to place source file names and certain command line switches into a text file instead of on a long command line. Command files can include C or C++ style comments, as well as # comments, if the # starts the line. .TP 8 .I "file name" A simple file name or file path is taken to be the name of a Verilog source file. The path starts with the first non-white-space character. Variables are substituted in file names. .TP 8 .B -c\ \fIcmdfile\fP -f\ \fIcmdfile\fP A \fB\-c\fP or \fB\-f\fP token prefixes a command file, exactly like it does on the command line. The cmdfile may be on the same line or the next non-comment line. .TP 8 .B -y\ \fIlibdir\fP A \fB\-y\fP token prefixes a library directory in the command file, exactly like it does on the command line. The parameter to the \fB\-y\fP flag may be on the same line or the next non-comment line. Variables in the \fIlibdir\fP are substituted. .TP 8 .B +incdir+\fIincludedir\fP The \fB+incdir+\fP token in command files gives directories to search for include files in much the same way that \fB\-I\fP flags work on the command line. The difference is that multiple \fI+includedir\fP directories are valid parameters to a single \fB+incdir+\fP token, although you may also have multiple \fB+incdir+\fP lines. Variables in the \fIincludedir\fP are substituted. .TP 8 .B +libext+\fIext\fP The \fB+libext\fP token in command files fives file extensions to try when looking for a library file. This is useful in conjunction with \fB\-y\fP flags to list suffixes to try in each directory before moving on to the next library directory. .TP 8 .B +libdir+\fIdir\fP This is another way to specify library directories. See the \-y flag. .TP 8 .B +libdir-nocase+\fIdir\fP This is like the \fB+libdir\fP statement, but file names inside the directories declared here are case insensitive. The missing module name in a lookup need not match the file name case, as long as the letters are correct. For example, "foo" matches "Foo.v" but not "bar.v". .TP 8 .B +define+\fINAME\fP=\fIvalue\fP The \fB+define+\fP token is the same as the \fB\-D\fP option on the command line. The value part of the token is optional. .TP 8 .B +parameter+\fINAME\fP=\fIvalue\fP The \fB+parameter+\fP token is the same as the \fB\-P\fP option on the command line. .TP 8 .B +timescale+\fIvalue\fP The \fB+timescale+\fP token is used to set the default timescale for the simulation. This is the time units and precision before any `timescale directive or after a `resetall directive. The default is 1s/1s. .TP 8 .B +toupper-filename This token causes file names after this in the command file to be translated to uppercase. This helps with situations where a directory has passed through a DOS machine, and in the process the file names become munged. .TP 8 .B +tolower-filename This is similar to the \fB+toupper\-filename\fP hack described above. .TP 8 .B +integer-width+\fIvalue\fP This allows the programmer to select the width for integer variables in the Verilog source. The default is 32, the value can be any desired integer value. .TP 8 .B +width-cap+\fIvalue\fP This allows the programmer to select the width cap for unsized expressions. If the calculated width for an unsized expression exceeds this value, the compiler will issue a warning and limit the expression width to this value. .SH "VARIABLES IN COMMAND FILES" In certain cases, iverilog supports variables in command files. These are strings of the form "$(\fIvarname\fP)" or "${\fIvarname\fP}", where \fIvarname\fP is the name of the environment variable to read. The entire string is replaced with the contents of that variable. Variables are only substituted in contexts that explicitly support them, including file and directory strings. Variable values come from the operating system environment, and not from preprocessor defines elsewhere in the file or the command line. .SH PREDEFINED MACROS The following macros are predefined by the compiler: .TP 8 .B __ICARUS__ = 1 This is always defined when compiling with Icarus Verilog. .TP 8 .B __VAMS_ENABLE__ = 1 This is defined if Verilog\-AMS is enabled. .SH EXAMPLES These examples assume that you have a Verilog source file called hello.v in the current directory To compile hello.v to an executable file called a.out: iverilog hello.v To compile hello.v to an executable file called hello: iverilog \-o hello hello.v To compile and run explicitly using the vvp runtime: iverilog \-ohello.vvp \-tvvp hello.v .SH "AUTHOR" .nf Steve Williams (steve@icarus.com) .SH SEE ALSO vvp(1), .BR "" Tips on using, debugging, and developing the compiler can be found at .BR "" .SH COPYRIGHT .nf Copyright \(co 2002\-2015 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 iverilog-10_1/driver/main.c000066400000000000000000001030471265551621300157320ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "version_base.h" # include "version_tag.h" const char NOTICE[] = " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; const char HELP[] = "Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]\n" " [-g1995|-g2001|-g2005|-g2005-sv|-g2009|-g2012] [-g]\n" " [-D macro[=defn]] [-I includedir]\n" " [-M [mode=]depfile] [-m module]\n" " [-N file] [-o filename] [-p flag=value]\n" " [-s topmodule] [-t target] [-T min|typ|max]\n" " [-W class] [-y dir] [-Y suf] source_file(s)\n" "\n" "See the man page for details."; #define MAXSIZE 4096 #include #include #include #include #include #include #include #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef __MINGW32__ # include # include #ifdef HAVE_LIBIBERTY_H # include #endif #endif #include #ifdef HAVE_GETOPT_H #include #endif #if defined(__MINGW32__) && !defined(HAVE_GETOPT_H) extern int getopt(int argc, char*argv[], const char*fmt); extern int optind; extern const char*optarg; #endif #if !defined(WIFEXITED) # define WIFEXITED(rc) ((rc&0x7f) == 0) #endif #if !defined(WEXITSTATUS) # define WEXITSTATUS(rc) (rc>>8) #endif #ifndef IVL_ROOT # define IVL_ROOT "." #endif # include "globals.h" #include "cfparse_misc.h" /* cfparse() */ #include "ivl_alloc.h" #ifdef __MINGW32__ const char sep = '\\'; #else const char sep = '/'; #endif extern void cfreset(FILE*fd, const char*path); const char*base = 0; const char*ivlpp_dir = 0; const char*vhdlpp_dir= 0; const char*vhdlpp_work = 0; const char*mtm = 0; const char*opath = "a.out"; const char*npath = 0; const char*targ = "vvp"; const char*depfile = 0; const char**vhdlpp_libdir = 0; unsigned vhdlpp_libdir_cnt = 0; char depmode = 'a'; const char*generation = "2005"; const char*gen_specify = "no-specify"; const char*gen_assertions = "assertions"; const char*gen_xtypes = "xtypes"; const char*gen_icarus = "icarus-misc"; const char*gen_io_range_error = "io-range-error"; const char*gen_strict_ca_eval = "no-strict-ca-eval"; const char*gen_strict_expr_width = "no-strict-expr-width"; const char*gen_verilog_ams = "no-verilog-ams"; /* Boolean: true means use a default include dir, false means don't */ int gen_std_include = 1; /* Boolean: true means add the local file directory to the start of the include list. */ int gen_relative_include = 0; char warning_flags[16] = "n"; unsigned integer_width = 32; unsigned width_cap = 65536; char*mod_list = 0; char*command_filename = 0; /* These are used to collect the list of file names that will be passed to ivlpp. Keep the list in a file because it can be a long list. */ char*source_path = 0; FILE*source_file = 0; unsigned source_count = 0; char*defines_path = 0; FILE*defines_file = 0; char*iconfig_path = 0; FILE*iconfig_file = 0; char*compiled_defines_path = 0; static char iconfig_common_path[4096] = ""; int synth_flag = 0; int verbose_flag = 0; FILE *fp; char line[MAXSIZE]; char tmp[MAXSIZE]; static char ivl_root[MAXSIZE]; /* Structure to keep a FIFO list of the command files */ typedef struct t_command_file { char *filename; struct t_command_file *next; } s_command_file, *p_command_file; p_command_file cmd_file_head = NULL; /* The FIFO head */ p_command_file cmd_file_tail = NULL; /* The FIFO tail */ /* Temporarily store parameter definition from command line and * parse it after we have dealt with command file */ static const char** defparm_base = 0; static int defparm_size = 0; /* Function to add a command file name to the FIFO. */ static void add_cmd_file(const char* filename) { p_command_file new; new = (p_command_file) malloc(sizeof(s_command_file)); new->filename = strdup(filename); new->next = NULL; if (cmd_file_head == NULL) { cmd_file_head = new; cmd_file_tail = new; } else { cmd_file_tail->next = new; cmd_file_tail = new; } } /* Function to return the top command file name from the FIFO. */ static char *get_cmd_file(void) { char *filename; if (cmd_file_head == NULL) filename = NULL; else { p_command_file head; filename = cmd_file_head->filename; head = cmd_file_head; cmd_file_head = cmd_file_head->next; free(head); } return filename; } #ifdef __MINGW32__ static FILE*fopen_safe(const char*path) { FILE*file = 0; int fd; fd = _open(path, _O_WRONLY|_O_CREAT|_O_EXCL, 0700); if (fd != -1) file = _fdopen(fd, "w"); return file; } #else static FILE*fopen_safe(const char*path) { FILE*file = 0; int fd; fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0700); if (fd != -1) file = fdopen(fd, "w"); return file; } #endif #ifdef __MINGW32__ /* * The MinGW version of getenv() returns the path with a forward * slash. This should be converted to a back slash to keep every * thing in the code using a back slash. This function wraps the * code for this in one place. The conversion can not be done * directly on the getenv() result since it is const char*. */ static void convert_to_MS_path(char *path) { char *t; for (t = path; *t; t++) { if (*t == '/') *t = '\\'; } } #endif static const char*my_tempfile(const char*str, FILE**fout) { FILE*file; int retry; static char pathbuf[8192]; const char*tmpdir = getenv("TMP"); if (tmpdir == 0) tmpdir = getenv("TMPDIR"); if (tmpdir == 0) tmpdir = getenv("TEMP"); #ifdef __MINGW32__ if (tmpdir == 0) tmpdir = "C:\\TEMP"; #else if (tmpdir == 0) tmpdir = "/tmp"; #endif assert(tmpdir); assert((strlen(tmpdir) + strlen(str)) < sizeof pathbuf - 10); srand(getpid()); retry = 100; file = NULL; while ((retry > 0) && (file == NULL)) { unsigned code = rand(); snprintf(pathbuf, sizeof pathbuf, "%s%c%s%04x", tmpdir, sep, str, code); #ifdef __MINGW32__ convert_to_MS_path(pathbuf); #endif file = fopen_safe(pathbuf); retry -= 1; } *fout = file; return pathbuf; } static int t_version_only(void) { int rc; remove(source_path); free(source_path); fflush(0); snprintf(tmp, sizeof tmp, "%s%civlpp -V", ivlpp_dir, sep); rc = system(tmp); if (rc != 0) { fprintf(stderr, "Unable to get version from \"%s\"\n", tmp); } fflush(0); snprintf(tmp, sizeof tmp, "%s%civl -V -C\"%s\" -C\"%s\"", base, sep, iconfig_path, iconfig_common_path); rc = system(tmp); if (rc != 0) { fprintf(stderr, "Unable to get version from \"%s\"\n", tmp); } if ( ! getenv("IVERILOG_ICONFIG")) { remove(iconfig_path); free(iconfig_path); remove(defines_path); free(defines_path); remove(compiled_defines_path); free(compiled_defines_path); } return 0; } static void build_preprocess_command(int e_flag) { snprintf(tmp, sizeof tmp, "%s%civlpp %s%s -F\"%s\" -f\"%s\" -p\"%s\" ", ivlpp_dir, sep, verbose_flag?" -v":"", e_flag?"":" -L", defines_path, source_path, compiled_defines_path); } static int t_preprocess_only(void) { int rc; char*cmd; unsigned ncmd; build_preprocess_command(1); ncmd = strlen(tmp); cmd = malloc(ncmd+1); strcpy(cmd, tmp); if (strcmp(opath,"-") != 0) { snprintf(tmp, sizeof tmp, " > \"%s\"", opath); cmd = realloc(cmd, ncmd+strlen(tmp)+1); strcpy(cmd+ncmd, tmp); ncmd += strlen(tmp); } if (verbose_flag) printf("preprocess: %s\n", cmd); rc = system(cmd); remove(source_path); free(source_path); if ( ! getenv("IVERILOG_ICONFIG")) { remove(iconfig_path); free(iconfig_path); remove(defines_path); free(defines_path); remove(compiled_defines_path); free(compiled_defines_path); } if (rc != 0) { if (WIFEXITED(rc)) { fprintf(stderr, "errors preprocessing Verilog program.\n"); free(cmd); return WEXITSTATUS(rc); } fprintf(stderr, "Command signaled: %s\n", cmd); free(cmd); return -1; } free(cmd); return 0; } /* * This is the default target type. It looks up the bits that are * needed to run the command from the configuration file (which is * already parsed for us) so we can handle must of the generic cases. */ static int t_compile(void) { unsigned rc; /* Start by building the preprocess command line. */ build_preprocess_command(0); size_t ncmd = strlen(tmp); char*cmd = malloc(ncmd + 1); strcpy(cmd, tmp); #ifndef __MINGW32__ int rtn; #endif /* Build the ivl command and pipe it to the preprocessor. */ snprintf(tmp, sizeof tmp, " | %s%civl", base, sep); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); ncmd += rc; if (verbose_flag) { const char*vv = " -v"; rc = strlen(vv); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, vv); ncmd += rc; } if (npath != 0) { snprintf(tmp, sizeof tmp, " -N\"%s\"", npath); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); ncmd += rc; } snprintf(tmp, sizeof tmp, " -C\"%s\"", iconfig_path); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); ncmd += rc; snprintf(tmp, sizeof tmp, " -C\"%s\" -- -", iconfig_common_path); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); ncmd += rc; if (verbose_flag) printf("translate: %s\n", cmd); rc = system(cmd); if ( ! getenv("IVERILOG_ICONFIG")) { remove(source_path); free(source_path); remove(iconfig_path); free(iconfig_path); remove(defines_path); free(defines_path); remove(compiled_defines_path); free(compiled_defines_path); } #ifdef __MINGW32__ /* MinGW just returns the exit status, so return it! */ free(cmd); return rc; #else rtn = 0; if (rc != 0) { if (rc == 127) { fprintf(stderr, "Failed to execute: %s\n", cmd); rtn = 1; } else if (WIFEXITED(rc)) { rtn = WEXITSTATUS(rc); } else { fprintf(stderr, "Command signaled: %s\n", cmd); rtn = -1; } } free(cmd); return rtn; #endif } static void process_warning_switch(const char*name) { if (strcmp(name,"all") == 0) { process_warning_switch("anachronisms"); process_warning_switch("implicit"); process_warning_switch("implicit-dimensions"); process_warning_switch("portbind"); process_warning_switch("select-range"); process_warning_switch("timescale"); process_warning_switch("sensitivity-entire-array"); } else if (strcmp(name,"anachronisms") == 0) { if (! strchr(warning_flags, 'n')) strcat(warning_flags, "n"); } else if (strcmp(name,"implicit") == 0) { if (! strchr(warning_flags, 'i')) strcat(warning_flags, "i"); } else if (strcmp(name,"implicit-dimensions") == 0) { if (! strchr(warning_flags, 'd')) strcat(warning_flags, "d"); } else if (strcmp(name,"portbind") == 0) { if (! strchr(warning_flags, 'p')) strcat(warning_flags, "p"); } else if (strcmp(name,"select-range") == 0) { if (! strchr(warning_flags, 's')) strcat(warning_flags, "s"); } else if (strcmp(name,"timescale") == 0) { if (! strchr(warning_flags, 't')) strcat(warning_flags, "t"); /* Since the infinite loop check is not part of 'all' it * does not have a no- version. */ } else if (strcmp(name,"infloop") == 0) { if (! strchr(warning_flags, 'l')) strcat(warning_flags, "l"); } else if (strcmp(name,"sensitivity-entire-vector") == 0) { if (! strchr(warning_flags, 'v')) strcat(warning_flags, "v"); } else if (strcmp(name,"sensitivity-entire-array") == 0) { if (! strchr(warning_flags, 'a')) strcat(warning_flags, "a"); } else if (strcmp(name,"no-anachronisms") == 0) { char*cp = strchr(warning_flags, 'n'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-implicit") == 0) { char*cp = strchr(warning_flags, 'i'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-implicit-dimensions") == 0) { char*cp = strchr(warning_flags, 'd'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-portbind") == 0) { char*cp = strchr(warning_flags, 'p'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-select-range") == 0) { char*cp = strchr(warning_flags, 's'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-timescale") == 0) { char*cp = strchr(warning_flags, 't'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-sensitivity-entire-vector") == 0) { char*cp = strchr(warning_flags, 'v'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else if (strcmp(name,"no-sensitivity-entire-array") == 0) { char*cp = strchr(warning_flags, 'a'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } } else { fprintf(stderr, "Ignoring unknown warning class " "%s\n", name); } } void process_library_switch(const char *name) { fprintf(iconfig_file, "-y:%s\n", name); } void process_library_nocase_switch(const char *name) { fprintf(iconfig_file, "-yl:%s\n", name); } void process_library2_switch(const char *name) { fprintf(iconfig_file, "-Y:%s\n", name); } void process_include_dir(const char *name) { fprintf(defines_file, "I:%s\n", name); } void process_define(const char*name) { fprintf(defines_file,"D:%s\n", name); } void process_parameter(const char*name) { fprintf(iconfig_file,"defparam:%s\n", name); } void process_timescale(const char*ts_string) { fprintf(iconfig_file, "timescale:%s\n", ts_string); } /* * This function is called while processing a file name in a command * file, or a file name on the command line. Look to see if there is a * .sft suffix, and if so pass that as a sys_func file. Otherwise, it * is a Verilog source file to be written into the file list. */ void process_file_name(const char*name, int lib_flag) { if (strlen(name) > 4 && strcasecmp(".sft", name+strlen(name)-4) == 0) { fprintf(iconfig_file,"sys_func:%s\n", name); } else { fprintf(source_file, "%s\n", name); source_count += 1; if (lib_flag) fprintf(iconfig_file,"library_file:%s\n", name); } } static int process_generation(const char*name) { if (strcmp(name,"1995") == 0) generation = "1995"; else if (strcmp(name,"2001") == 0) generation = "2001"; else if (strcmp(name,"2001-noconfig") == 0) generation = "2001-noconfig"; else if (strcmp(name,"2005") == 0) generation = "2005"; else if (strcmp(name,"2005-sv") == 0) generation = "2005-sv"; else if (strcmp(name,"2009") == 0) generation = "2009"; else if (strcmp(name,"2012") == 0) generation = "2012"; else if (strcmp(name,"1") == 0) { /* Deprecated: use 1995 */ generation = "1995"; gen_xtypes = "no-xtypes"; gen_icarus = "no-icarus-misc"; } else if (strcmp(name,"2") == 0) { /* Deprecated: use 2001 */ generation = "2001"; gen_xtypes = "no-xtypes"; gen_icarus = "no-icarus-misc"; } else if (strcmp(name,"2x") == 0) { /* Deprecated: use 2005/xtypes */ generation = "2005"; gen_xtypes = "xtypes"; gen_icarus = "icarus-misc"; } else if (strcmp(name,"xtypes") == 0) gen_xtypes = "xtypes"; else if (strcmp(name,"no-xtypes") == 0) gen_xtypes = "no-xtypes"; else if (strcmp(name,"icarus-misc") == 0) gen_icarus = "icarus-misc"; else if (strcmp(name,"no-icarus-misc") == 0) gen_icarus = "no-icarus-misc"; else if (strcmp(name,"specify") == 0) gen_specify = "specify"; else if (strcmp(name,"no-specify") == 0) gen_specify = "no-specify"; else if (strcmp(name,"assertions") == 0) gen_assertions = "assertions"; else if (strcmp(name,"no-assertions") == 0) gen_assertions = "no-assertions"; else if (strcmp(name,"std-include") == 0) gen_std_include = 1; else if (strcmp(name,"no-std-include") == 0) gen_std_include = 0; else if (strcmp(name,"relative-include") == 0) gen_relative_include = 1; else if (strcmp(name,"no-relative-include") == 0) gen_relative_include = 0; else if (strcmp(name,"io-range-error") == 0) gen_io_range_error = "io-range-error"; else if (strcmp(name,"no-io-range-error") == 0) gen_io_range_error = "no-io-range-error"; else if (strcmp(name,"strict-ca-eval") == 0) gen_strict_ca_eval = "strict-ca-eval"; else if (strcmp(name,"no-strict-ca-eval") == 0) gen_strict_ca_eval = "no-strict-ca-eval"; else if (strcmp(name,"strict-expr-width") == 0) gen_strict_expr_width = "strict-expr-width"; else if (strcmp(name,"no-strict-expr-width") == 0) gen_strict_expr_width = "no-strict-expr-width"; else if (strcmp(name,"verilog-ams") == 0) gen_verilog_ams = "verilog-ams"; else if (strcmp(name,"no-verilog-ams") == 0) gen_verilog_ams = "no-verilog-ams"; else { fprintf(stderr, "Unknown/Unsupported Language generation " "%s\n\n", name); fprintf(stderr, "Supported generations are:\n"); fprintf(stderr, " 1995 -- IEEE1364-1995\n" " 2001 -- IEEE1364-2001\n" " 2005 -- IEEE1364-2005\n" " 2005-sv -- IEEE1800-2005\n" " 2009 -- IEEE1800-2009\n" " 2012 -- IEEE1800-2012\n" "Other generation flags:\n" " specify | no-specify\n" " verilog-ams | no-verilog-ams\n" " std-include | no-std-include\n" " relative-include | no-relative-include\n" " xtypes | no-xtypes\n" " icarus-misc | no-icarus-misc\n" " io-range-error | no-io-range-error\n" " strict-ca-eval | no-strict-ca-eval\n" " strict-expr-width | no-strict-expr-width\n"); return 1; } return 0; } static int process_depfile(const char*name) { const char*cp = strchr(name, '='); if (cp) { int match_length = (int)(cp - name) + 1; if (strncmp(name, "all=", match_length) == 0) { depmode = 'a'; } else if (strncmp(name, "include=", match_length) == 0) { depmode = 'i'; } else if (strncmp(name, "module=", match_length) == 0) { depmode = 'm'; } else if (strncmp(name, "prefix=", match_length) == 0) { depmode = 'p'; } else { fprintf(stderr, "Unknown dependency file mode '%.*s'\n\n", match_length - 1, name); fprintf(stderr, "Supported modes are:\n"); fprintf(stderr, " all\n"); fprintf(stderr, " include\n"); fprintf(stderr, " module\n"); fprintf(stderr, " prefix\n"); return -1; } depfile = cp + 1; } else { depmode = 'a'; depfile = name; } return 0; } /* * If it exists add the SFT file for the given module. */ static void add_sft_file(const char *module) { char *file; file = (char *) malloc(strlen(base)+1+strlen(module)+4+1); sprintf(file, "%s%c%s.sft", base, sep, module); if (access(file, R_OK) == 0) fprintf(iconfig_file, "sys_func:%s\n", file); free(file); } int main(int argc, char **argv) { int e_flag = 0; int version_flag = 0; int opt; #ifdef __MINGW32__ /* Calculate the ivl_root from the path to the command. This is necessary because of the installation process on Windows. Mostly, it is those darn drive letters, but oh well. We know the command path is formed like this: D:\iverilog\bin\iverilog.exe The module path in a Windows installation is the path: D:\iverilog\lib\ivl$(suffix) so we chop the file name and the last directory by turning the last two \ characters to null. Then we append the lib\ivl$(suffix) to finish. */ char *s; char tmppath[MAXSIZE]; GetModuleFileName(NULL, tmppath, sizeof tmppath); /* Convert to a short name to remove any embedded spaces. */ GetShortPathName(tmppath, ivl_root, sizeof ivl_root); s = strrchr(ivl_root, sep); if (s) *s = 0; else { fprintf(stderr, "%s: Missing first %c in exe path!\n", argv[0], sep); exit(1); } s = strrchr(ivl_root, sep); if (s) *s = 0; else { fprintf(stderr, "%s: Missing second %c in exe path!\n", argv[0], sep); exit(1); } strcat(ivl_root, "\\lib\\ivl" IVL_SUFFIX); base = ivl_root; #else /* In a UNIX environment, the IVL_ROOT from the Makefile is dependable. It points to the $prefix/lib/ivl directory, where the sub-parts are installed. */ strcpy(ivl_root, IVL_ROOT); base = ivl_root; #endif /* Create a temporary file for communicating input parameters to the preprocessor. */ source_path = strdup(my_tempfile("ivrlg", &source_file)); if (NULL == source_file) { fprintf(stderr, "%s: Error opening temporary file %s\n", argv[0], source_path); fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]); return 1; } defines_path = strdup(my_tempfile("ivrlg2", &defines_file)); if (NULL == defines_file) { fprintf(stderr, "%s: Error opening temporary file %s\n", argv[0], defines_path); fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]); fclose(source_file); remove(source_path); return 1; } fprintf(defines_file, "D:__ICARUS__=1\n"); if (strcmp(gen_verilog_ams,"verilog-ams") == 0) fprintf(defines_file, "D:__VAMS_ENABLE__=1\n"); /* Create another temporary file for passing configuration information to ivl. */ if ( (iconfig_path = getenv("IVERILOG_ICONFIG")) ) { fprintf(stderr, "%s: IVERILOG_ICONFIG=%s\n", argv[0], iconfig_path); iconfig_file = fopen(iconfig_path, "w"); } else { iconfig_path = strdup(my_tempfile("ivrlh", &iconfig_file)); } if (NULL == iconfig_file) { fprintf(stderr, "%s: Error opening temporary file %s\n", argv[0], iconfig_path); fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]); fclose(source_file); remove(source_path); fclose(defines_file); remove(defines_path); return 1; } /* Create a temporary file (I only really need the path) that can carry preprocessor precompiled defines to the library. */ { FILE*tmp_file = 0; compiled_defines_path = strdup(my_tempfile("ivrli", &tmp_file)); if (tmp_file) { fclose(tmp_file); } } while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hI:M:m:N:o:P:p:Ss:T:t:vVW:y:Y:")) != EOF) { switch (opt) { case 'B': /* The individual components can be located by a single base, or by individual bases. The first character of the path indicates which path the user is specifying. */ switch (optarg[0]) { case 'P': /* Path for the ivlpp preprocessor */ ivlpp_dir = optarg+1; break; case 'V': /* Path for the vhdlpp VHDL processor */ vhdlpp_dir = optarg+1; break; default: /* Otherwise, this is a default base. */ base=optarg; break; } break; case 'c': case 'f': add_cmd_file(optarg); break; case 'D': process_define(optarg); break; case 'E': e_flag = 1; break; case 'P': defparm_size += 1; defparm_base = (const char**)realloc(defparm_base, defparm_size*sizeof(char*)); defparm_base[defparm_size-1] = optarg; break; case 'p': fprintf(iconfig_file, "flag:%s\n", optarg); break; case 'd': fprintf(iconfig_file, "debug:%s\n", optarg); break; case 'g': if (process_generation(optarg) != 0) return -1; break; case 'h': fprintf(stderr, "%s\n", HELP); return 1; case 'I': process_include_dir(optarg); break; case 'M': if (process_depfile(optarg) != 0) return -1; break; case 'm': fprintf(iconfig_file, "module:%s\n", optarg); add_sft_file(optarg); break; case 'N': npath = optarg; break; case 'o': opath = optarg; break; case 'S': synth_flag = 1; break; case 's': fprintf(iconfig_file, "root:%s\n", optarg); break; case 'T': if (strcmp(optarg,"min") == 0) { mtm = "min"; } else if (strcmp(optarg,"typ") == 0) { mtm = "typ"; } else if (strcmp(optarg,"max") == 0) { mtm = "max"; } else { fprintf(stderr, "%s: invalid -T%s argument\n", argv[0], optarg); return 1; } break; case 't': targ = optarg; break; case 'v': verbose_flag = 1; break; case 'V': version_flag = 1; break; case 'W': process_warning_switch(optarg); break; case 'y': process_library_switch(optarg); break; case 'Y': process_library2_switch(optarg); break; case '?': default: fclose(source_file); remove(source_path); free(source_path); fclose(defines_file); remove(defines_path); free(defines_path); fclose(iconfig_file); remove(iconfig_path); free(iconfig_path); remove(compiled_defines_path); free(compiled_defines_path); while( (command_filename = get_cmd_file()) ) { free(command_filename); } return 1; } } if (ivlpp_dir == 0) ivlpp_dir = base; if (vhdlpp_dir == 0) vhdlpp_dir = base; if (version_flag || verbose_flag) { printf("Icarus Verilog version " VERSION " (" VERSION_TAG ")\n\n"); printf("Copyright 1998-2015 Stephen Williams\n\n"); puts(NOTICE); } /* Make a common conf file path to reflect the target. */ snprintf(iconfig_common_path, sizeof iconfig_common_path, "%s%c%s%s.conf", base, sep, targ, synth_flag? "-s" : ""); /* Write values to the iconfig file. */ fprintf(iconfig_file, "basedir:%s\n", base); /* Tell the core where to find the system.sft. This file describes the system functions so that elaboration knows how to handle them. */ fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep); fprintf(iconfig_file, "sys_func:%s%cvhdl_sys.sft\n", base, sep); /* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams, * then include the v2005_math library. */ if (strcmp(generation, "2005") == 0 || strcmp(generation, "2009") == 0 || strcmp(generation, "2012") == 0 || strcmp(gen_icarus, "icarus-misc") == 0 || strcmp(gen_verilog_ams, "verilog-ams") == 0) { fprintf(iconfig_file, "sys_func:%s%cv2005_math.sft\n", base, sep); fprintf(iconfig_file, "module:v2005_math\n"); } /* If verilog-ams or icarus_misc is enabled, then include the * va_math module as well. */ if (strcmp(gen_verilog_ams,"verilog-ams") == 0 || strcmp(gen_icarus, "icarus-misc") == 0) { fprintf(iconfig_file, "sys_func:%s%cva_math.sft\n", base, sep); fprintf(iconfig_file, "module:va_math\n"); } /* If verilog-2009 (SystemVerilog) is enabled, then include the v2009 module. */ if (strcmp(generation, "2005-sv") == 0 || strcmp(generation, "2009") == 0 || strcmp(generation, "2012") == 0) { fprintf(iconfig_file, "sys_func:%s%cv2009.sft\n", base, sep); fprintf(iconfig_file, "module:v2009\n"); } if (mtm != 0) fprintf(iconfig_file, "-T:%s\n", mtm); fprintf(iconfig_file, "generation:%s\n", generation); fprintf(iconfig_file, "generation:%s\n", gen_specify); fprintf(iconfig_file, "generation:%s\n", gen_assertions); fprintf(iconfig_file, "generation:%s\n", gen_xtypes); fprintf(iconfig_file, "generation:%s\n", gen_io_range_error); fprintf(iconfig_file, "generation:%s\n", gen_strict_ca_eval); fprintf(iconfig_file, "generation:%s\n", gen_strict_expr_width); fprintf(iconfig_file, "generation:%s\n", gen_verilog_ams); fprintf(iconfig_file, "generation:%s\n", gen_icarus); fprintf(iconfig_file, "warnings:%s\n", warning_flags); fprintf(iconfig_file, "out:%s\n", opath); if (depfile) { fprintf(iconfig_file, "depfile:%s\n", depfile); fprintf(iconfig_file, "depmode:%c\n", depmode); } while ( (command_filename = get_cmd_file()) ) { int rc; if (( fp = fopen(command_filename, "r")) == NULL ) { fprintf(stderr, "%s: cannot open command file %s " "for reading.\n", argv[0], command_filename); return 1; } cfreset(fp, command_filename); rc = cfparse(); if (rc != 0) { fprintf(stderr, "%s: parsing failed in base command " "file %s.\n", argv[0], command_filename); return 1; } free(command_filename); fclose(fp); } destroy_lexor(); if (depfile) { fprintf(defines_file, "M%c:%s\n", depmode, depfile); } if (vhdlpp_work == 0) vhdlpp_work = "ivl_vhdl_work"; fprintf(defines_file, "vhdlpp:%s%cvhdlpp\n", vhdlpp_dir, sep); fprintf(defines_file, "vhdlpp-work:%s\n", vhdlpp_work); for (unsigned idx = 0 ; idx < vhdlpp_libdir_cnt ; idx += 1) fprintf(defines_file, "vhdlpp-libdir:%s\n", vhdlpp_libdir[idx]); /* Process parameter definition from command line. The last defined would override previous ones. */ int pitr; for (pitr = 0; pitr < defparm_size; pitr++) process_parameter(defparm_base[pitr]); free(defparm_base); defparm_base = 0; defparm_size = 0; /* Finally, process all the remaining words on the command line as file names. */ for (int idx = optind ; idx < argc ; idx += 1) process_file_name(argv[idx], 0); /* If the use of a default include directory is not specifically disabled, then write that directory as the very last include directory to use... always. */ if (gen_std_include) { fprintf(defines_file, "I:%s%cinclude\n", base, sep); } if (gen_relative_include) { fprintf(defines_file, "relative include:true\n"); } else { fprintf(defines_file, "relative include:false\n"); } fclose(source_file); source_file = 0; fclose(defines_file); defines_file = 0; /* If we are planning on opening a dependencies file, then open and truncate it here. The other phases of compilation will append to the file, so this is necessary to make sure it starts out empty. */ if (depfile) { FILE*fd = fopen(depfile, "w"); fclose(fd); } if (source_count == 0 && !version_flag) { fprintf(stderr, "%s: no source files.\n\n%s\n", argv[0], HELP); return 1; } fprintf(iconfig_file, "iwidth:%u\n", integer_width); fprintf(iconfig_file, "widthcap:%u\n", width_cap); /* Write the preprocessor command needed to preprocess a single file. This may be used to preprocess library files. */ fprintf(iconfig_file, "ivlpp:%s%civlpp -L -F\"%s\" -P\"%s\"\n", ivlpp_dir, sep, defines_path, compiled_defines_path); /* Done writing to the iconfig file. Close it now. */ fclose(iconfig_file); /* If we're only here for the version output, then we're done. */ if (version_flag) return t_version_only(); /* If the -E flag was given on the command line, then all we do is run the preprocessor and put the output where the user wants it. */ if (e_flag) return t_preprocess_only(); /* Otherwise, this is a full compile. */ return t_compile(); } iverilog-10_1/driver/substit.c000066400000000000000000000047741265551621300165120ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "ivl_alloc.h" # include "globals.h" char* substitutions(const char*str) { size_t nbuf = strlen(str) + 1; char*buf = malloc(nbuf); char*cp = buf; while (*str) { if ((str[0] == '$') && ((str[1] == '(') || str[1] == '{')) { /* If I find a $(x) or ${x} string in the source, replace it in the destination with the contents of the environment variable x. */ char*name; char*value; const char*ep = strchr(str, (str[1]=='(') ? ')' : '}'); str += 2; name = malloc(ep-str+1); strncpy(name, str, ep-str); name[ep-str] = 0; str = ep + 1; value = getenv(name); if (value == 0) { fprintf(stderr, "Warning: environment variable " "\"%s\" not found during command file " "processing.\n", name); free(name); continue; } free(name); if (strlen(value) >= (nbuf - (cp-buf))) { size_t old_size = cp - buf; nbuf = (cp - buf) + strlen(value) + 1; buf = realloc(buf, nbuf); cp = buf + old_size; } strcpy(cp, value); cp += strlen(cp); } else { if ( cp == (buf + nbuf) ) { size_t old_size = nbuf; nbuf = old_size + 32; buf = realloc(buf, nbuf); cp = buf + old_size; } *cp++ = *str++; } } /* Add the trailing nul to the string, and reallocate the buffer to be a tight fit. */ if ( cp == (buf + nbuf) ) { size_t old_size = nbuf; nbuf = old_size + 1; buf = realloc(buf, nbuf); buf[old_size] = 0; } else { *cp++ = 0; nbuf = cp - buf; buf = realloc(buf, nbuf); } return buf; } iverilog-10_1/dup_expr.cc000066400000000000000000000200771265551621300155050ustar00rootroot00000000000000/* * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "netlist.h" # include # include # include "ivl_assert.h" NetEAccess* NetEAccess::dup_expr() const { NetEAccess*tmp = new NetEAccess(branch_, nature_); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEArrayPattern*NetEArrayPattern::dup_expr() const { vectortmp (items_.size()); for (size_t idx = 0 ; idx < tmp.size() ; idx += 1) tmp[idx] = items_[idx]->dup_expr(); NetEArrayPattern*res = new NetEArrayPattern(net_type(), tmp); res->set_line(*this); return res; } NetEBinary* NetEBinary::dup_expr() const { ivl_assert(*this, 0); return 0; } NetEBAdd* NetEBAdd::dup_expr() const { NetEBAdd*tmp = new NetEBAdd(op_, left_->dup_expr(), right_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBBits* NetEBBits::dup_expr() const { NetEBBits*tmp = new NetEBBits(op_, left_->dup_expr(), right_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBComp* NetEBComp::dup_expr() const { NetEBComp*tmp = new NetEBComp(op_, left_->dup_expr(), right_->dup_expr()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBDiv* NetEBDiv::dup_expr() const { NetEBDiv*tmp = new NetEBDiv(op_, left_->dup_expr(), right_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBLogic* NetEBLogic::dup_expr() const { NetEBLogic*tmp = new NetEBLogic(op_, left_->dup_expr(), right_->dup_expr()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBMult* NetEBMult::dup_expr() const { NetEBMult*tmp = new NetEBMult(op_, left_->dup_expr(), right_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBPow* NetEBPow::dup_expr() const { NetEBPow*tmp = new NetEBPow(op_, left_->dup_expr(), right_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEBShift* NetEBShift::dup_expr() const { NetEBShift*tmp = new NetEBShift(op_, left_->dup_expr(), right_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEConcat* NetEConcat::dup_expr() const { NetEConcat*dup = new NetEConcat(parms_.size(), repeat_, expr_type_); ivl_assert(*this, dup); dup->set_line(*this); for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) if (parms_[idx]) { NetExpr*tmp = parms_[idx]->dup_expr(); ivl_assert(*this, tmp); dup->parms_[idx] = tmp; } dup->expr_width(expr_width()); return dup; } NetEConst* NetEConst::dup_expr() const { NetEConst*tmp = new NetEConst(value_); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEConstEnum* NetEConstEnum::dup_expr() const { NetEConstEnum*tmp = new NetEConstEnum(scope_, name_, enum_set_, value()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEConstParam* NetEConstParam::dup_expr() const { NetEConstParam*tmp = new NetEConstParam(scope_, name_, value()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetECReal* NetECReal::dup_expr() const { NetECReal*tmp = new NetECReal(value_); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetECRealParam* NetECRealParam::dup_expr() const { NetECRealParam*tmp = new NetECRealParam(scope_, name_, value()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEEvent* NetEEvent::dup_expr() const { ivl_assert(*this, 0); return 0; } NetELast* NetELast::dup_expr() const { NetELast*tmp = new NetELast(sig_); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetENetenum* NetENetenum::dup_expr() const { ivl_assert(*this, 0); return 0; } NetENew* NetENew::dup_expr() const { ivl_assert(*this, 0); return 0; } NetENull* NetENull::dup_expr() const { ivl_assert(*this, 0); return 0; } NetEProperty* NetEProperty::dup_expr() const { ivl_assert(*this, 0); return 0; } NetEScope* NetEScope::dup_expr() const { ivl_assert(*this, 0); return 0; } NetESelect* NetESelect::dup_expr() const { NetESelect*tmp = new NetESelect(expr_->dup_expr(), base_? base_->dup_expr() : 0, expr_width(), sel_type_); ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); tmp->set_line(*this); return tmp; } NetESFunc* NetESFunc::dup_expr() const { NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms()); ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { ivl_assert(*this, parm(idx)); tmp->parm(idx, parm(idx)->dup_expr()); } tmp->set_line(*this); return tmp; } NetEShallowCopy* NetEShallowCopy::dup_expr() const { ivl_assert(*this, 0); return 0; } NetESignal* NetESignal::dup_expr() const { NetESignal*tmp = new NetESignal(net_, word_); ivl_assert(*this, tmp); tmp->expr_width(expr_width()); tmp->cast_signed(has_sign()); tmp->set_line(*this); return tmp; } NetETernary* NetETernary::dup_expr() const { NetETernary*tmp = new NetETernary(cond_->dup_expr(), true_val_->dup_expr(), false_val_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUFunc* NetEUFunc::dup_expr() const { NetEUFunc*tmp; vector tmp_parms (parms_.size()); for (unsigned idx = 0 ; idx < tmp_parms.size() ; idx += 1) { ivl_assert(*this, parms_[idx]); tmp_parms[idx] = parms_[idx]->dup_expr(); } tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms, need_const_); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUBits* NetEUBits::dup_expr() const { NetEUBits*tmp = new NetEUBits(op_, expr_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUnary* NetEUnary::dup_expr() const { NetEUnary*tmp = new NetEUnary(op_, expr_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUReduce* NetEUReduce::dup_expr() const { NetEUReduce*tmp = new NetEUReduce(op_, expr_->dup_expr()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetECast* NetECast::dup_expr() const { NetECast*tmp = new NetECast(op_, expr_->dup_expr(), expr_width(), has_sign()); ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } iverilog-10_1/elab_anet.cc000066400000000000000000000110351265551621300155630ustar00rootroot00000000000000/* * Copyright (c) 2000-2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * The elaborate_anet methods elaborate expressions that are intended * to be the left side of procedural continuous assignments. */ # include "PExpr.h" # include "netlist.h" # include "netmisc.h" # include NetNet* PExpr::elaborate_anet(Design*des, NetScope*scope) const { cerr << get_line() << ": error: Invalid expression on left side " << "of procedural continuous assignment." << endl; return 0; } NetNet* PEConcat::elaborate_anet(Design*des, NetScope*scope) const { if (repeat_) { cerr << get_line() << ": error: Repeat concatenations make " "no sense in l-value expressions. I refuse." << endl; des->errors += 1; return 0; } svectornets (parms_.count()); unsigned pins = 0; unsigned errors = 0; for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { if (parms_[idx] == 0) { cerr << get_line() << ": error: Empty expressions " << "not allowed in concatenations." << endl; errors += 1; continue; } nets[idx] = parms_[idx]->elaborate_anet(des, scope); if (nets[idx] == 0) errors += 1; else pins += nets[idx]->pin_count(); } /* If any of the sub expressions failed to elaborate, then delete all those that did and abort myself. */ if (errors) { for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { if (nets[idx]) delete nets[idx]; } des->errors += 1; return 0; } /* Make the temporary signal that connects to all the operands, and connect it up. Scan the operands of the concat operator from least significant to most significant, which is opposite from how they are given in the list. Allow for a repeat count other than 1 by repeating the connect loop as many times as necessary. */ NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT_REG, pins); /* Assume that all the data types are the same. */ osig->data_type(nets[0]->data_type()); pins = 0; for (unsigned idx = nets.count() ; idx > 0 ; idx -= 1) { NetNet*cur = nets[idx-1]; assert(cur->data_type() == osig->data_type()); for (unsigned pin = 0; pin < cur->pin_count(); pin += 1) { connect(osig->pin(pins), cur->pin(pin)); pins += 1; } } osig->local_flag(true); return osig; } NetNet* PEIdent::elaborate_anet(Design*des, NetScope*scope) const { assert(scope); NetNet* sig = 0; NetMemory* mem = 0; const NetExpr*par = 0; NetEvent* eve = 0; symbol_search(this, des, scope, path_, sig, mem, par, eve); if (mem != 0) { cerr << get_line() << ": error: memories not allowed " << "on left side of procedural continuous " << "assignment." << endl; des->errors += 1; return 0; } if (eve != 0) { cerr << get_line() << ": error: named events not allowed " << "on left side of procedural continuous " << "assignment." << endl; des->errors += 1; return 0; } if (sig == 0) { cerr << get_line() << ": error: reg ``" << path_ << "'' " << "is undefined in this scope." << endl; des->errors += 1; return 0; } switch (sig->type()) { case NetNet::REG: case NetNet::IMPLICIT_REG: break; default: cerr << get_line() << ": error: " << path_ << " is not " << "a reg in this context." << endl; des->errors += 1; return 0; } assert(sig); if (msb_ || lsb_) { cerr << get_line() << ": error: bit/part selects not allowed " << "on left side of procedural continuous assignment." << endl; des->errors += 1; return 0; } return sig; } iverilog-10_1/elab_expr.cc000066400000000000000000006030361265551621300156220ustar00rootroot00000000000000/* * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include # include "compiler.h" # include "PPackage.h" # include "pform.h" # include "netlist.h" # include "netclass.h" # include "netenum.h" # include "netparray.h" # include "netvector.h" # include "discipline.h" # include "netmisc.h" # include "netdarray.h" # include "netstruct.h" # include "netscalar.h" # include "util.h" # include "ivl_assert.h" bool type_is_vectorable(ivl_variable_type_t type) { switch (type) { case IVL_VT_BOOL: case IVL_VT_LOGIC: return true; default: return false; } } static ivl_nature_t find_access_function(const pform_name_t&path) { if (path.size() != 1) return 0; else return access_function_nature[peek_tail_name(path)]; } /* * Look at the signal to see if there is already a branch that * connects the sig to the gnd. If there is, then return it. If not, * return 0. */ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) { Nexus*nex = sig->pin(0).nexus(); for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { if (cur->is_equal(sig->pin(0))) continue; if (cur->get_pin() != 0) continue; NetBranch*tmp = dynamic_cast (cur->get_obj()); if (tmp == 0) continue; if (tmp->name()) continue; if (tmp->pin(1).is_linked(gnd->pin(0))) return tmp; } return 0; } NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, unsigned lv_width, PExpr*expr, bool need_const) { if (debug_elaborate) { cerr << expr->get_fileline() << ": elaborate_rval_expr: " << "expr=" << *expr; if (lv_net_type) cerr << ", lv_net_type=" << *lv_net_type; else cerr << ", lv_net_type="; cerr << ", lv_type=" << lv_type << ", lv_width=" << lv_width << endl; } int context_wid = -1; switch (lv_type) { case IVL_VT_DARRAY: case IVL_VT_QUEUE: // For these types, use a different elab_and_eval that // uses the lv_net_type. We should eventually transition // all the types to this new form. if (lv_net_type) return elab_and_eval(des, scope, expr, lv_net_type, need_const); break; case IVL_VT_REAL: case IVL_VT_STRING: break; case IVL_VT_BOOL: case IVL_VT_LOGIC: context_wid = lv_width; break; case IVL_VT_VOID: case IVL_VT_NO_TYPE: ivl_assert(*expr, 0); break; case IVL_VT_CLASS: cerr << expr->get_fileline() << ": sorry: " << "I do not know how to elaborate r-value as IVL_VT_CLASS." << endl; des->errors += 1; return 0; break; } return elab_and_eval(des, scope, expr, context_wid, need_const, false, lv_type); } /* * If the mode is UPSIZE, make sure the final expression width is at * least integer_width, but return the calculated lossless width to * the caller. */ unsigned PExpr::fix_width_(width_mode_t mode) { unsigned width = expr_width_; if ((mode == UPSIZE) && type_is_vectorable(expr_type_) && (width < integer_width)) expr_width_ = integer_width; return width; } unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) { cerr << get_fileline() << ": internal error: I do not know how to" << " test the width of this expression. " << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; return 1; } NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, ivl_type_t, unsigned) const { cerr << get_fileline() << ": internal error: I do not know how to" << " elaborate (ivl_type_t) this expression. " << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; cerr << get_fileline() << ": : Expression type: " << typeid(*this).name() << endl; des->errors += 1; return 0; } NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { cerr << get_fileline() << ": internal error: I do not know how to" << " elaborate this expression. " << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; return 0; } /* * For now, assume that assignment patterns are for dynamic * objects. This is not really true as this expression type, fully * supported, can assign to packed arrays and structs, unpacked arrays * and dynamic arrays. */ unsigned PEAssignPattern::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_DARRAY; expr_width_ = 1; min_width_ = 1; signed_flag_= false; return 1; } NetExpr*PEAssignPattern::elaborate_expr(Design*des, NetScope*scope, ivl_type_t ntype, unsigned flags) const { // Special case: If this is an empty pattern (i.e. '{}) and // the expected type is a DARRAY, then convert this to a null // handle. Internally, Icarus Verilog uses this to represent // nil dynamic arrays. if (parms_.size() == 0 && ntype->base_type()==IVL_VT_DARRAY) { NetENull*tmp = new NetENull; tmp->set_line(*this); return tmp; } if (ntype->base_type()==IVL_VT_DARRAY) return elaborate_expr_darray_(des, scope, ntype, flags); cerr << get_fileline() << ": sorry: I don't know how to elaborate " << "assignment_pattern expressions yet." << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; return 0; } NetExpr*PEAssignPattern::elaborate_expr_darray_(Design*des, NetScope*scope, ivl_type_t ntype, unsigned flags) const { const netdarray_t*array_type = dynamic_cast (ntype); ivl_assert(*this, array_type); // This is an array pattern, so run through the elements of // the expression and elaborate each as if they are // element_type expressions. ivl_type_t elem_type = array_type->element_type(); vector elem_exprs (parms_.size()); for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { NetExpr*tmp = parms_[idx]->elaborate_expr(des, scope, elem_type, flags); elem_exprs[idx] = tmp; } NetEArrayPattern*res = new NetEArrayPattern(array_type, elem_exprs); res->set_line(*this); return res; } NetExpr* PEAssignPattern::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { cerr << get_fileline() << ": sorry: I do not know how to" << " elaborate assignment patterns using old method." << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; ivl_assert(*this, 0); return 0; } unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { ivl_assert(*this, left_); ivl_assert(*this, right_); unsigned r_width = right_->test_width(des, scope, mode); width_mode_t saved_mode = mode; unsigned l_width = left_->test_width(des, scope, mode); if (debug_elaborate) { cerr << get_fileline() << ": PEBinary::test_width: " << "op_=" << op_ << ", l_width=" << l_width << ", r_width=" << r_width << ", saved_mode=" << saved_mode << endl; } // If the width mode changed, retest the right operand, as it // may choose a different width if it is in a lossless context. if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) r_width = right_->test_width(des, scope, mode); ivl_variable_type_t l_type = left_->expr_type(); ivl_variable_type_t r_type = right_->expr_type(); if (l_type == IVL_VT_REAL || r_type == IVL_VT_REAL) expr_type_ = IVL_VT_REAL; else if (l_type == IVL_VT_LOGIC || r_type == IVL_VT_LOGIC) expr_type_ = IVL_VT_LOGIC; else expr_type_ = IVL_VT_BOOL; if (expr_type_ == IVL_VT_REAL) { expr_width_ = 1; min_width_ = 1; signed_flag_ = true; } else { expr_width_ = max(l_width, r_width); min_width_ = max(left_->min_width(), right_->min_width()); signed_flag_ = left_->has_sign() && right_->has_sign(); // If the operands are different types, the expression is // forced to unsigned. In this case the lossless width // calculation is unreliable and we need to make sure the // final expression width is at least integer_width. if ((mode == LOSSLESS) && (left_->has_sign() != right_->has_sign())) mode = UPSIZE; switch (op_) { case '+': case '-': if (mode >= EXPAND) expr_width_ += 1; break; case '*': if (mode >= EXPAND) expr_width_ = l_width + r_width; break; case '%': case '/': min_width_ = UINT_MAX; // disable width pruning break; case 'l': // << Should be handled by PEBShift case 'r': // >> Should be handled by PEBShift case 'R': // >>> Should be handled by PEBShift case '<': // < Should be handled by PEBComp case '>': // > Should be handled by PEBComp case 'e': // == Should be handled by PEBComp case 'E': // === Should be handled by PEBComp case 'L': // <= Should be handled by PEBComp case 'G': // >= Should be handled by PEBComp case 'n': // != Should be handled by PEBComp case 'N': // !== Should be handled by PEBComp case 'p': // ** should be handled by PEBPower ivl_assert(*this, 0); default: break; } } return fix_width_(mode); } /* * Elaborate binary expressions. This involves elaborating the left * and right sides, and creating one of a variety of different NetExpr * types. */ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag ivl_assert(*this, left_); ivl_assert(*this, right_); // Handle the special case that one of the operands is a real // value and the other is a vector type. In that case, // elaborate the vectorable argument as self-determined. // Propagate the expression type (signed/unsigned) down to // any context-determined operands. unsigned l_width = expr_wid; unsigned r_width = expr_wid; if (left_->expr_type()==IVL_VT_REAL && type_is_vectorable(right_->expr_type())) { r_width = right_->expr_width(); } else { right_->cast_signed(signed_flag_); } if (right_->expr_type()==IVL_VT_REAL && type_is_vectorable(left_->expr_type())) { l_width = left_->expr_width(); } else { left_->cast_signed(signed_flag_); } NetExpr*lp = left_->elaborate_expr(des, scope, l_width, flags); NetExpr*rp = right_->elaborate_expr(des, scope, r_width, flags); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } return elaborate_expr_base_(des, lp, rp, expr_wid); } /* * This is the common elaboration of the operator. It presumes that the * operands are elaborated as necessary, and all I need to do is make * the correct NetEBinary object and connect the parameters. */ NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " << *this << " expr_width=" << expr_wid << endl; } NetExpr*tmp; switch (op_) { default: tmp = new NetEBinary(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; case 'a': case 'o': cerr << get_fileline() << ": internal error: " << "Elaboration of " << human_readable_op(op_) << " Should have been handled in NetEBLogic::elaborate." << endl; des->errors += 1; return 0; case 'p': cerr << get_fileline() << ": internal error: " << "Elaboration of " << human_readable_op(op_) << " Should have been handled in NetEBPower::elaborate." << endl; des->errors += 1; return 0; case '*': tmp = elaborate_expr_base_mult_(des, lp, rp, expr_wid); break; case '%': case '/': tmp = elaborate_expr_base_div_(des, lp, rp, expr_wid); break; case 'l': case 'r': case 'R': cerr << get_fileline() << ": internal error: " << "Elaboration of " << human_readable_op(op_) << " Should have been handled in NetEBShift::elaborate." << endl; des->errors += 1; return 0; case '^': case '&': case '|': case 'O': // NOR (~|) case 'A': // NAND (~&) case 'X': tmp = elaborate_expr_base_bits_(des, lp, rp, expr_wid); break; case '+': case '-': tmp = new NetEBAdd(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; case 'E': /* === */ case 'N': /* !== */ case 'e': /* == */ case 'n': /* != */ case 'L': /* <= */ case 'G': /* >= */ case '<': case '>': cerr << get_fileline() << ": internal error: " << "Elaboration of " << human_readable_op(op_) << " Should have been handled in NetEBComp::elaborate." << endl; des->errors += 1; return 0; case 'm': // min(l,r) case 'M': // max(l,r) tmp = new NetEBMinMax(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; } return tmp; } NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " << human_readable_op(op_) << " operator may not have REAL operands." << endl; des->errors += 1; return 0; } NetEBBits*tmp = new NetEBBits(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { /* The % operator does not support real arguments in baseline Verilog. But we allow it in our extended form of Verilog. */ if (op_ == '%' && ! gn_icarus_misc_flag) { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: Modulus operator " "may not have REAL operands." << endl; des->errors += 1; } } NetEBDiv*tmp = new NetEBDiv(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { // Keep constants on the right side. if (dynamic_cast(lp)) { NetExpr*tmp = lp; lp = rp; rp = tmp; } // Handle a few special case multiplies against constants. if (NetEConst*rp_const = dynamic_cast (rp)) { verinum rp_val = rp_const->value(); if (!rp_val.is_defined() && (lp->expr_type() == IVL_VT_LOGIC)) { NetEConst*tmp = make_const_x(expr_wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } if (rp_val.is_zero() && (lp->expr_type() == IVL_VT_BOOL)) { NetEConst*tmp = make_const_0(expr_wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } } NetEBMult*tmp = new NetEBMult(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) { ivl_assert(*this, left_); ivl_assert(*this, right_); // The width and type of a comparison are fixed and well known. expr_type_ = IVL_VT_LOGIC; expr_width_ = 1; min_width_ = 1; signed_flag_ = false; // The widths of the operands are semi-self-determined. They // affect each other, but not the result. width_mode_t mode = SIZED; unsigned r_width = right_->test_width(des, scope, mode); width_mode_t saved_mode = mode; unsigned l_width = left_->test_width(des, scope, mode); // If the width mode changed, retest the right operand, as it // may choose a different width if it is in a lossless context. if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) r_width = right_->test_width(des, scope, mode); ivl_variable_type_t l_type = left_->expr_type(); ivl_variable_type_t r_type = right_->expr_type(); l_width_ = l_width; if (type_is_vectorable(l_type) && (r_width > l_width)) l_width_ = r_width; r_width_ = r_width; if (type_is_vectorable(r_type) && (l_width > r_width)) r_width_ = l_width; // If the expression is lossless and smaller than the integer // minimum, then tweak the size up. // NOTE: I really would rather try to figure out what it would // take to get expand the sub-expressions so that they are // exactly the right width to behave just like infinite // width. I suspect that adding 1 more is sufficient in all // cases, but I'm not certain. Ideas? if (mode >= EXPAND) { if (type_is_vectorable(l_type) && (l_width_ < integer_width)) l_width_ += 1; if (type_is_vectorable(r_type) && (r_width_ < integer_width)) r_width_ += 1; } if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Comparison expression operands are " << l_type << " " << l_width << " bits and " << r_type << " " << r_width << " bits. Resorting to " << l_width_ << " bits and " << r_width_ << " bits." << endl; } return expr_width_; } NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag ivl_assert(*this, left_); ivl_assert(*this, right_); // Propagate the comparison type (signed/unsigned) down to // the operands. if (type_is_vectorable(left_->expr_type()) && !left_->has_sign()) right_->cast_signed(false); if (type_is_vectorable(right_->expr_type()) && !right_->has_sign()) left_->cast_signed(false); NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, flags); NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, flags); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } eval_expr(lp, l_width_); eval_expr(rp, r_width_); // Handle some operand-specific special cases... switch (op_) { case 'E': /* === */ case 'N': /* !== */ if (lp->expr_type() == IVL_VT_REAL || lp->expr_type() == IVL_VT_STRING || rp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_STRING) { cerr << get_fileline() << ": error: " << human_readable_op(op_) << " operator may not have REAL or STRING operands." << endl; des->errors += 1; return 0; } break; default: break; } NetExpr*tmp = new NetEBComp(op_, lp, rp); tmp->set_line(*this); tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) { // The width and type of a logical operation are fixed. expr_type_ = IVL_VT_LOGIC; expr_width_ = 1; min_width_ = 1; signed_flag_ = false; // The widths of the operands are self determined. We don't need // them now, so they can be tested when they are elaborated. return expr_width_; } NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { ivl_assert(*this, left_); ivl_assert(*this, right_); bool need_const = NEED_CONST & flags; NetExpr*lp = elab_and_eval(des, scope, left_, -1, need_const); NetExpr*rp = elab_and_eval(des, scope, right_, -1, need_const); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } lp = condition_reduce(lp); rp = condition_reduce(rp); NetExpr*tmp = new NetEBLogic(op_, lp, rp); tmp->set_line(*this); tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) { ivl_assert(*this, left_); ivl_assert(*this, right_); if (debug_elaborate) { cerr << get_fileline() << ": PEBLeftWidth::test_width: " << "op_=" << op_ << ", left_=" << *left_ << ", right_=" << *right_ << ", mode=" << width_mode_name(mode) << endl; } // The right operand is self determined. Test its type and // width for use later. We only need to know its width now // if the left operand is unsized and we need to calculate // the lossless width. width_mode_t r_mode = SIZED; unsigned r_width = right_->test_width(des, scope, r_mode); // The left operand is what will determine the size of the // expression. The l_mode will be converted to UNSIZED if the // expression does not have a well-determined size. width_mode_t l_mode = SIZED; expr_width_ = left_->test_width(des, scope, l_mode); expr_type_ = left_->expr_type(); signed_flag_ = left_->has_sign(); if (mode==SIZED) mode = l_mode; // The left operand width defines the size of the // expression. If the expression has a well-defined size, the // left_->test_width() above would have set mode==SIZED and we // can skip a lot of stuff. But if the mode is an undetermined // size, we need to figure out what we really want to keep a // lossless value. That's what the following if(...) {...} is // all about. if ((mode >= EXPAND) && type_is_vectorable(expr_type_)) { // We need to make our best guess at the right operand // value, to minimize the calculated width. This is // particularly important for the power operator... // Start off by assuming the maximum value for the // type and width of the right operand. long r_val = LONG_MAX; if (r_width < sizeof(long)*8) { r_val = (1L << r_width) - 1L; if ((op_ == 'p') && right_->has_sign()) r_val >>= 1; } // If the right operand is constant, we can use the // actual value. NetExpr*rp = right_->elaborate_expr(des, scope, r_width, NO_FLAGS); if (rp) { eval_expr(rp, r_width); } else { // error recovery PEVoid*tmp = new PEVoid(); tmp->set_line(*this); delete right_; right_ = tmp; } NetEConst*rc = dynamic_cast (rp); // Adjust the expression width that can be converter depending // on if the R-value is signed or not. unsigned c_width = sizeof(long)*8; if (! right_->has_sign()) c_width -= 1; if (rc && (r_width <= c_width)) r_val = rc->value().as_long(); if (debug_elaborate && rc) { cerr << get_fileline() << ": PEBLeftWidth::test_width: " << "Evaluated rc=" << *rc << ", r_val=" << r_val << ", width_cap=" << width_cap << endl; } // Clip to a sensible range to avoid underflow/overflow // in the following calculations. if (r_val < 0) r_val = 0; if ((unsigned long)r_val > width_cap) r_val = width_cap; // If the left operand is a simple unsized number, we // can calculate the actual width required for the power // operator. PENumber*lc = dynamic_cast (left_); // Now calculate the lossless width. unsigned use_width = expr_width_; switch (op_) { case 'l': // << if (l_mode != SIZED) use_width += (unsigned)r_val; break; case 'r': // >> case 'R': // >>> // A logical shift will effectively coerce a signed // operand to unsigned. We have to assume an arithmetic // shift may do the same, as we don't yet know the final // expression type. if ((mode == LOSSLESS) && signed_flag_) mode = UPSIZE; break; case 'p': // ** if (lc && rc) { verinum result = pow(lc->value(), rc->value()); use_width = max(use_width, result.len()); } else { if (signed_flag_) use_width -= 1; use_width *= (unsigned)r_val; if (signed_flag_) use_width += 2; } break; default: cerr << get_fileline() << ": internal error: " << "Unexpected opcode " << human_readable_op(op_) << " in PEBLeftWidth::test_width." << endl; des->errors += 1; } // If the right operand is not constant, we could end up // grossly overestimating the required width. So in this // case, don't expand beyond the width of an integer // (which meets the requirements of the standard). if ((rc == 0) && (use_width > expr_width_) && (use_width > integer_width)) use_width = integer_width; if (use_width >= width_cap) { cerr << get_fileline() << ": warning: " << "Unsized expression (" << *this << ")" << " expanded beyond and was clipped to " << use_width << " bits. Try using sized operands." << endl; } expr_width_ = use_width; } if (op_ == 'l') min_width_ = left_->min_width(); else min_width_ = UINT_MAX; // disable width pruning if (debug_elaborate) { cerr << get_fileline() << ": PEBLeftWidth::test_width: " << "Done calculating expr_width_=" << expr_width_ << ", min_width_=" << min_width_ << ", mode=" << width_mode_name(mode) << endl; } return fix_width_(mode); } NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag ivl_assert(*this, left_); // The left operand is always context determined, so propagate // down the expression type (signed/unsigned). left_->cast_signed(signed_flag_); unsigned r_width = right_->expr_width(); NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, flags); NetExpr*rp = right_->elaborate_expr(des, scope, r_width, flags); if (lp == 0 || rp == 0) { delete lp; delete rp; return 0; } // For shift operations, the right operand is always treated as // unsigned, so coerce it if necessary. if ((op_ != 'p') && rp->has_sign()) { rp = new NetESelect(rp, 0, rp->expr_width()); rp->cast_signed(false); rp->set_line(*this); } eval_expr(lp, expr_wid); eval_expr(rp, r_width); return elaborate_expr_leaf(des, lp, rp, expr_wid); } NetExpr*PEBPower::elaborate_expr_leaf(Design*, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " << *this << " expr_wid=" << expr_wid << endl; } NetExpr*tmp = new NetEBPow(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { switch (op_) { case 'l': // << case 'r': // >> case 'R': // >>> break; default: cerr << get_fileline() << ": internal error: " << "Unexpected opcode " << human_readable_op(op_) << " in PEBShift::elaborate_expr_leaf." << endl; des->errors += 1; return 0; } if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " << human_readable_op(op_) << " operator may not have REAL operands." << endl; des->errors += 1; delete lp; delete rp; return 0; } NetExpr*tmp; // If the left expression is constant, then there are some // special cases we can work with. If the left expression is // not constant, but the right expression is constant, then // there are some other interesting cases. But if neither are // constant, then there is the general case. if (NetEConst*lpc = dynamic_cast (lp)) { // Special case: The left expression is zero. If the // shift value contains no 'x' or 'z' bits, the result // is going to be zero. if (lpc->value().is_defined() && lpc->value().is_zero() && (rp->expr_type() == IVL_VT_BOOL)) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Shift of zero always returns zero." << " Elaborate as constant zero." << endl; tmp = make_const_0(expr_wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } } else if (NetEConst*rpc = dynamic_cast (rp)) { // Special case: The shift value contains 'x' or 'z' bits. // Elaborate as a constant-x. if (!rpc->value().is_defined()) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Shift by undefined value. " << "Elaborate as constant 'x'." << endl; tmp = make_const_x(expr_wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); delete lp; delete rp; return tmp; } unsigned long shift = rpc->value().as_ulong(); // Special case: The shift is zero. The result is simply // the left operand. if (shift == 0) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Shift by zero. Elaborate as the " << "left hand operand." << endl; delete rp; return lp; } // Special case: the shift is at least the size of the entire // left operand, and the shift is a signed right shift. // Elaborate as a replication of the top bit of the left // expression. if ((op_=='R' && signed_flag_) && (shift >= expr_wid)) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value signed-right-shifted " << shift << " beyond width of " << expr_wid << ". Elaborate as replicated top bit." << endl; tmp = new NetEConst(verinum(expr_wid-1)); tmp->set_line(*this); tmp = new NetESelect(lp, tmp, 1); tmp->set_line(*this); tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(true); delete rp; return tmp; } // Special case: The shift is at least the size of the entire // left operand, and the shift is not a signed right shift // (which is caught by the previous special case). Elaborate // as a constant-0. if (shift >= expr_wid) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value shifted " << shift << " beyond width of " << expr_wid << ". Elaborate as constant zero." << endl; tmp = make_const_0(expr_wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); delete lp; delete rp; return tmp; } } // Fallback, handle the general case. tmp = new NetEBShift(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, width_mode_t&mode) { perm_string name = peek_tail_name(path_); if (name=="$ivlh_to_unsigned") { ivl_assert(*this, parms_.size() == 2); // The Icarus Verilog specific $ivlh_to_unsigned() system // task takes a second argument which is the output // size. This can be an arbitrary constant function. PExpr*pexpr = parms_[1]; if (pexpr == 0) { cerr << get_fileline() << ": error: " << "Missing $ivlh_to_unsigned width." << endl; return 0; } NetExpr*nexpr = elab_and_eval(des, scope, pexpr, -1, true); if (nexpr == 0) { cerr << get_fileline() << ": error: " << "Unable to evaluate " << name << " width argument: " << *pexpr << endl; return 0; } long value = 0; bool rc = eval_as_long(value, nexpr); ivl_assert(*this, rc && value>=0); // The argument type/width is self-determined and doesn't // affect the result type/width. width_mode_t arg_mode = SIZED; parms_[0]->test_width(des, scope, arg_mode); expr_width_ = value; signed_flag_= false; return expr_width_; } if (name=="$signed" || name=="$unsigned") { PExpr*expr = parms_[0]; if (expr == 0) return 0; // The argument type/width is self-determined, but affects // the result width. width_mode_t arg_mode = SIZED; expr_width_ = expr->test_width(des, scope, arg_mode); expr_type_ = expr->expr_type(); min_width_ = expr->min_width(); signed_flag_ = (name[1] == 's'); if ((arg_mode >= EXPAND) && type_is_vectorable(expr_type_)) { if (mode < LOSSLESS) mode = LOSSLESS; if (expr_width_ < integer_width) expr_width_ = integer_width; } if (debug_elaborate) cerr << get_fileline() << ": debug: " << name << " argument width = " << expr_width_ << "." << endl; return expr_width_; } if (name=="$sizeof" || name=="$bits") { PExpr*expr = parms_[0]; if (expr == 0) return 0; if (! dynamic_cast(expr)) { // The argument type/width is self-determined and doesn't // affect the result type/width. Note that if the // argument is a type name (a special case) then // don't bother with this step. width_mode_t arg_mode = SIZED; expr->test_width(des, scope, arg_mode); } expr_type_ = IVL_VT_BOOL; expr_width_ = integer_width; min_width_ = integer_width; signed_flag_ = false; if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" << " of " << name << " returns test_width" << " of compiler integer." << endl; return expr_width_; } if (name=="$is_signed") { PExpr*expr = parms_[0]; if (expr == 0) return 0; // The argument type/width is self-determined and doesn't // affect the result type/width. width_mode_t arg_mode = SIZED; expr->test_width(des, scope, arg_mode); expr_type_ = IVL_VT_BOOL; expr_width_ = 1; min_width_ = 1; signed_flag_ = false; if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" << " of $is_signed returns test_width" << " of 1." << endl; return expr_width_; } /* Get the return type of the system function by looking it up in the sfunc_table. */ const struct sfunc_return_type*sfunc_info = lookup_sys_func(name); expr_type_ = sfunc_info->type; expr_width_ = sfunc_info->wid; min_width_ = expr_width_; signed_flag_ = sfunc_info->signed_flag; if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " << "of system function " << name << " returns wid=" << expr_width_ << ", type=" << expr_type_ << "." << endl; return expr_width_; } unsigned PECallFunction::test_width(Design*des, NetScope*scope, width_mode_t&mode) { if (peek_tail_name(path_)[0] == '$') return test_width_sfunc_(des, scope, mode); // The width of user defined functions depends only on the // width of the return value. The arguments are entirely // self-determined. NetFuncDef*def = des->find_function(scope, path_); if (def == 0) { // If this is an access function, then the width and // type are known by definition. if (find_access_function(path_)) { expr_type_ = IVL_VT_REAL; expr_width_ = 1; min_width_ = 1; signed_flag_ = true; return expr_width_; } if (test_width_method_(des, scope, mode)) { if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " << "of method returns width " << expr_width_ << ", type=" << expr_type_ << "." << endl; return expr_width_; } if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " << "cannot find definition of " << path_ << " in " << scope_path(scope) << "." << endl; return 0; } NetScope*dscope = def->scope(); assert(dscope); if (NetNet*res = dscope->find_signal(dscope->basename())) { expr_type_ = res->data_type(); expr_width_ = res->vector_width(); min_width_ = expr_width_; signed_flag_ = res->get_signed(); if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " << "of function returns width " << expr_width_ << ", type=" << expr_type_ << "." << endl; return expr_width_; } ivl_assert(*this, 0); return 0; } unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, width_mode_t&) { if (!gn_system_verilog()) return 0; // This is only useful if the path is at least 2 elements. For // example, foo.bar() is a method, bar() is not. if (path_.size() < 2) return 0; perm_string member_name; ivl_type_t member_type = 0; pform_name_t use_path = path_; perm_string method_name = peek_tail_name(use_path); use_path.pop_back(); NetNet *net = 0; const NetExpr *par; NetEvent *eve; const NetExpr *ex1, *ex2; symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); const netdarray_t*use_darray = 0; if (net != 0) use_darray = net->darray_type(); // Net is not found, but maybe it is a member of a // struct or class. Try to locate net without the member // name and test if it is a type that has members. if (net == 0 && use_path.size() >= 2) { pform_name_t tmp_path = use_path; member_name = peek_tail_name(tmp_path); tmp_path.pop_back(); net = 0; symbol_search(this, des, scope, tmp_path, net, par, eve, ex1, ex2); if (net && net->class_type()) { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Found net=" << tmp_path << ", member_name=" << member_name << ", method_name=" << method_name << endl; } const netclass_t* class_type = net->class_type(); int midx = class_type->property_idx_from_name(member_name); if (midx >= 0) member_type = class_type->get_prop_type(midx); else member_type = 0; use_path = tmp_path; use_darray = dynamic_cast (member_type); } else { member_name = perm_string(); net = 0; } } // After all, no sign of a net match. Give up. if (net == 0) return 0; // Look for built in string attributes. if (net->data_type()==IVL_VT_STRING) { if (method_name == "len") { expr_type_ = IVL_VT_BOOL; expr_width_ = 32; min_width_ = 32; signed_flag_= true; return expr_width_; } } // function int size() if (use_darray && method_name == "size") { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Match darray size() method." << endl; } expr_type_ = IVL_VT_BOOL; expr_width_ = 32; min_width_ = expr_width_; signed_flag_= true; return expr_width_; } if (use_darray && (method_name == "pop_back" || method_name=="pop_front")) { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Detected " << method_name << " method" << " of dynamic arrays." << endl; } expr_type_ = use_darray->element_base_type(); expr_width_ = use_darray->element_width(); min_width_ = expr_width_; signed_flag_= false; return expr_width_; } if (const netclass_t*class_type = net->class_type()) { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Try to find method " << method_name << " of class " << class_type->get_name() << endl; } NetScope*func = class_type->method_from_name(method_name); if (func == 0) { return 0; } // Get the function result size be getting the details // from the variable in the function scope that has the // name of the function. if (NetNet*res = func->find_signal(method_name)) { expr_type_ = res->data_type(); expr_width_= res->vector_width(); min_width_ = expr_width_; signed_flag_ = res->get_signed(); return expr_width_; } else { ivl_assert(*this, 0); } } return 0; } NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::cast_to_width_: " << "cast to " << wid << " bits " << (signed_flag_?"signed":"unsigned") << " from expr_width()=" << expr->expr_width() << endl; } /* If the expression is a const, then replace it with a new const. This is a more efficient result. */ if (NetEConst*tmp = dynamic_cast(expr)) { tmp->cast_signed(signed_flag_); if (wid != tmp->expr_width()) { tmp = new NetEConst(verinum(tmp->value(), wid)); tmp->set_line(*this); delete expr; } return tmp; } NetESelect*tmp = new NetESelect(expr, 0, wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } /* * Given a call to a system function, generate the proper expression * nodes to represent the call in the netlist. Since we don't support * size_tf functions, make assumptions about widths based on some * known function names. */ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { perm_string name = peek_tail_name(path_); /* Catch the special case that the system function is the $ivl_unsigned function. In this case the second argument is the size of the expression, but should already be accounted for so treat this very much like the $unsigned() function. */ if (name=="$ivlh_to_unsigned") { ivl_assert(*this, parms_.size()==2); PExpr*expr = parms_[0]; ivl_assert(*this, expr); NetExpr*sub = expr->elaborate_expr(des, scope, expr->expr_width(), flags); return cast_to_width_(sub, expr_wid); } /* Catch the special case that the system function is the $signed function. Its argument will be evaluated as a self-determined expression. */ if (name=="$signed" || name=="$unsigned") { if ((parms_.size() != 1) || (parms_[0] == 0)) { cerr << get_fileline() << ": error: The " << name << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_sfunc_: " << name << " expression is the argument cast to expr_wid=" << expr_wid << endl; } PExpr*expr = parms_[0]; NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, flags); return cast_to_width_(sub, expr_wid); } /* Interpret the internal $sizeof system function to return the bit width of the sub-expression. The value of the sub-expression is not used, so the expression itself can be deleted. */ if (name=="$sizeof" || name=="$bits") { if ((parms_.size() != 1) || (parms_[0] == 0)) { cerr << get_fileline() << ": error: The " << name << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } if (name=="$sizeof") cerr << get_fileline() << ": warning: $sizeof is deprecated." << " Use $bits() instead." << endl; PExpr*expr = parms_[0]; uint64_t use_width = 0; if (PETypename*type_expr = dynamic_cast(expr)) { ivl_type_t tmp_type = type_expr->get_type()->elaborate_type(des, scope); ivl_assert(*this, tmp_type); use_width = tmp_type->packed_width(); if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_sfunc_: " << " Packed width of type argument is " << use_width << endl; } } else { use_width = expr->expr_width(); if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_sfunc_: " << " Width of expression argument is " << use_width << endl; } } verinum val (use_width, integer_width); NetEConst*sub = new NetEConst(val); sub->set_line(*this); return cast_to_width_(sub, expr_wid); } /* Interpret the internal $is_signed system function to return a single bit flag -- 1 if the expression is signed, 0 otherwise. */ if (name=="$is_signed") { if ((parms_.size() != 1) || (parms_[0] == 0)) { cerr << get_fileline() << ": error: The " << name << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } PExpr*expr = parms_[0]; verinum val (expr->has_sign()? verinum::V1 : verinum::V0, 1); NetEConst*sub = new NetEConst(val); sub->set_line(*this); return cast_to_width_(sub, expr_wid); } /* How many parameters are there? The Verilog language allows empty parameters in certain contexts, so the parser will allow things like func(1,,3). It will also cause func() to be interpreted as a single empty parameter. Functions cannot really take empty parameters, but the case ``func()'' is the same as no parameters at all. So catch that special case here. */ unsigned nparms = parms_.size(); if ((nparms == 1) && (parms_[0] == 0)) nparms = 0; NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms); fun->set_line(*this); if (!fun->is_built_in()) { if (scope->need_const_func()) { cerr << get_fileline() << ": error: " << name << " is not a built-in function, so cannot" << " be used in a constant function." << endl; des->errors += 1; } scope->is_const_func(false); } /* Now run through the expected parameters. If we find that there are missing parameters, print an error message. While we're at it, try to evaluate the function parameter expression as much as possible, and use the reduced expression if one is created. */ bool need_const = NEED_CONST & flags; unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr, need_const); if (tmp) { fun->parm(idx, tmp); } else { parm_errors += 1; fun->parm(idx, 0); } } else { missing_parms += 1; fun->parm(idx, 0); } } if (missing_parms > 0) { cerr << get_fileline() << ": error: The function " << name << " has been called with missing/empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; des->errors += 1; } if (missing_parms || parm_errors) return 0; NetExpr*tmp = pad_to_width(fun, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t nature, unsigned expr_wid) const { // An access function must have 1 or 2 arguments. ivl_assert(*this, parms_.size()==2 || parms_.size()==1); NetBranch*branch = 0; if (parms_.size() == 1) { PExpr*arg1 = parms_[0]; PEIdent*arg_ident = dynamic_cast (arg1); ivl_assert(*this, arg_ident); const pform_name_t&path = arg_ident->path(); ivl_assert(*this, path.size()==1); perm_string name = peek_tail_name(path); NetNet*sig = scope->find_signal(name); ivl_assert(*this, sig); ivl_discipline_t dis = sig->get_discipline(); ivl_assert(*this, dis); ivl_assert(*this, nature == dis->potential() || nature == dis->flow()); NetNet*gnd = des->find_discipline_reference(dis, scope); if ( (branch = find_existing_implicit_branch(sig, gnd)) ) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Re-use implicit branch from " << branch->get_fileline() << endl; } else { branch = new NetBranch(dis); branch->set_line(*this); connect(branch->pin(0), sig->pin(0)); connect(branch->pin(1), gnd->pin(0)); des->add_branch(branch); join_island(branch); if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Create implicit branch." << endl; } } else { ivl_assert(*this, 0); } NetExpr*tmp = new NetEAccess(branch, nature); tmp->set_line(*this); tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } /* * Routine to look for and build enumeration method calls. */ static NetExpr* check_for_enum_methods(const LineInfo*li, Design*des, NetScope*scope, const netenum_t*netenum, pform_name_t use_path, perm_string method_name, NetExpr*expr, unsigned rtn_wid, PExpr*parg, unsigned args) { // The "num()" method returns the number of elements. if (method_name == "num") { if (args != 0) { cerr << li->get_fileline() << ": error: enumeration " "method " << use_path << ".num() does not " "take an argument." << endl; des->errors += 1; } NetEConst*tmp = make_const_val(netenum->size()); tmp->set_line(*li); delete expr; // The elaborated enum variable is not needed. return tmp; } // The "first()" method returns the first enumeration value. if (method_name == "first") { if (args != 0) { cerr << li->get_fileline() << ": error: enumeration " "method " << use_path << ".first() does not " "take an argument." << endl; des->errors += 1; } netenum_t::iterator item = netenum->first_name(); NetEConstEnum*tmp = new NetEConstEnum(scope, item->first, netenum, item->second); tmp->set_line(*li); delete expr; // The elaborated enum variable is not needed. return tmp; } // The "last()" method returns the first enumeration value. if (method_name == "last") { if (args != 0) { cerr << li->get_fileline() << ": error: enumeration " "method " << use_path << ".last() does not " "take an argument." << endl; des->errors += 1; } netenum_t::iterator item = netenum->last_name(); NetEConstEnum*tmp = new NetEConstEnum(scope, item->first, netenum, item->second); tmp->set_line(*li); delete expr; // The elaborated enum variable is not needed. return tmp; } NetESFunc*sys_expr; // Process the method argument if it is available. NetExpr* count = 0; if (args != 0 && parg) { count = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, IVL_VT_BOOL, 32, parg); if (count == 0) { cerr << li->get_fileline() << ": error: unable to elaborate " "enumeration method argument " << use_path << "." << method_name << "(" << parg << ")." << endl; args = 0; des->errors += 1; } else if (NetEEvent*evt = dynamic_cast (count)) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' cannot be an enumeration " "method argument." << endl; args = 0; des->errors += 1; } } // The "name()" method returns the name of the current // enumeration value. if (method_name == "name") { if (args != 0) { cerr << li->get_fileline() << ": error: enumeration " "method " << use_path << ".name() does not " "take an argument." << endl; des->errors += 1; } sys_expr = new NetESFunc("$ivl_enum_method$name", IVL_VT_STRING, rtn_wid, 2); sys_expr->parm(0, new NetENetenum(netenum)); sys_expr->parm(1, expr); /* The compiler/code generators need to be fixed to support a * string return value. In some contexts we could use the * expression width, but that doesn't always work. */ if (rtn_wid == 0) { cerr << li->get_fileline() << ": sorry: Enumeration method " "name() is not currently supported in this context " "(self-determined)." << endl; des->errors += 1; } // The "next()" method returns the next enumeration value. } else if (method_name == "next") { if (args > 1) { cerr << li->get_fileline() << ": error: enumeration " "method " << use_path << ".next() take at " "most one argument." << endl; des->errors += 1; } sys_expr = new NetESFunc("$ivl_enum_method$next", netenum, 2 + (args != 0)); sys_expr->parm(0, new NetENetenum(netenum)); sys_expr->parm(1, expr); if (args != 0) sys_expr->parm(2, count); // The "prev()" method returns the previous enumeration value. } else if (method_name == "prev") { if (args > 1) { cerr << li->get_fileline() << ": error: enumeration " "method " << use_path << ".prev() take at " "most one argument." << endl; des->errors += 1; } sys_expr = new NetESFunc("$ivl_enum_method$prev", netenum, 2 + (args != 0)); sys_expr->parm(0, new NetENetenum(netenum)); sys_expr->parm(1, expr); if (args != 0) sys_expr->parm(2, count); // This is an unknown enumeration method. } else { cerr << li->get_fileline() << ": error: Unknown enumeration " "method " << use_path << "." << method_name << "()." << endl; des->errors += 1; return expr; } sys_expr->set_line(*li); if (debug_elaborate) { cerr << li->get_fileline() << ": debug: Generate " << sys_expr->name() << "(" << use_path << ")" << endl; } return sys_expr; } /* * If the method matches a structure member then return the member otherwise * return 0. Also return the offset of the member. */ static const netstruct_t::member_t*get_struct_member(const LineInfo*li, Design*des, NetScope*, NetNet*net, perm_string method_name, unsigned long&off) { const netstruct_t*type = net->struct_type(); ivl_assert(*li, type); if (! type->packed()) { cerr << li->get_fileline() << ": sorry: unpacked structures not supported here. " << "Method=" << method_name << endl; des->errors += 1; return 0; } return type->packed_member(method_name, off); } bool calculate_part(const LineInfo*li, Design*des, NetScope*scope, const index_component_t&index, long&off, unsigned long&wid) { if (index.sel == index_component_t::SEL_BIT_LAST) { cerr << li->get_fileline() << ": sorry: " << "Last element select expression " << "not supported." << endl; des->errors += 1; return false; } // Evaluate the last index expression into a constant long. NetExpr*texpr = elab_and_eval(des, scope, index.msb, -1, true); long msb; if (texpr == 0 || !eval_as_long(msb, texpr)) { cerr << li->get_fileline() << ": error: " "Array/part index expressions must be constant here." << endl; des->errors += 1; return false; } delete texpr; long lsb = msb; if (index.lsb) { texpr = elab_and_eval(des, scope, index.lsb, -1, true); if (texpr==0 || !eval_as_long(lsb, texpr)) { cerr << li->get_fileline() << ": error: " "Array/part index expressions must be constant here." << endl; des->errors += 1; return false; } delete texpr; } switch (index.sel) { case index_component_t::SEL_BIT: off = msb; wid = 1; return true; case index_component_t::SEL_PART: if (msb >= lsb) { off = lsb; wid = msb - lsb + 1; } else { off = msb; wid = lsb - msb + 1; } return true; case index_component_t::SEL_IDX_UP: wid = lsb; off = msb; break; default: ivl_assert(*li, 0); break; } return true; } /* * Test if the tail name (method_name argument) is a member name and * the net is a struct. If that turns out to be the case, and the * struct is packed, then return a NetExpr that selects the member out * of the variable. */ static NetExpr* check_for_struct_members(const LineInfo*li, Design*des, NetScope*scope, NetNet*net, const list&base_index, const name_component_t&comp) { unsigned long off; const netstruct_t::member_t*mem = get_struct_member(li, des, 0, net, comp.name, off); if (mem == 0) return 0; ivl_assert(*li, mem->net_type && mem->net_type->packed()); unsigned use_width = mem->net_type->packed_width(); if (debug_elaborate) { cerr << li->get_fileline() << ": debug: check_for_struct_members: " << "Found struct member " << mem->name << " At offset " << off << ", member width = " << use_width << endl; } // The struct member may be a packed array. Process index // expression that address the member element. if ( ! comp.index.empty() ) { const netvector_t*mem_vec = dynamic_cast (mem->net_type); ivl_assert(*li, mem_vec); const vector&packed_dims = mem_vec->packed_dims(); // Evaluate all but the last index expression, into prefix_indices. listprefix_indices; bool rc = evaluate_index_prefix(des, scope, prefix_indices, comp.index); ivl_assert(*li, rc); // Make sure that index values that select array // elements are in fact like bit selects. The tail may // be part selects only if we are taking the part-select // of the word of an array. ivl_assert(*li, comp.index.size() >= packed_dims.size() || comp.index.back().sel == index_component_t::SEL_BIT); // Evaluate the part/bit select expressions. This may be // a bit select or a part select. In any case, assume // the arguments are constant and generate a part select // of the appropriate width. long poff = 0; unsigned long pwid = 0; rc = calculate_part(li, des, scope, comp.index.back(), poff, pwid); ivl_assert(*li, rc); // Now use the prefix_to_slice function to calculate the // offset and width of the addressed slice of the member. long loff; unsigned long lwid; prefix_to_slice(packed_dims, prefix_indices, poff, loff, lwid); if (debug_elaborate) { cerr << li->get_fileline() << ": debug: check_for_struct_members: " << "Evaluate prefix gives slice loff=" << loff << ", lwid=" << lwid << ", part select pwid=" << pwid << endl; } off += loff; if (comp.index.size() >= packed_dims.size()) use_width = pwid; else use_width = lwid; } // If the base symbol has dimensions, then this is a packed // array of structures. Convert an array of indices to a // single part select. For example, "net" is a packed array // of struct, and "mem" is the struct member. In Verilog it // looks something like "net[idx].mem". We've already // converted "mem" to an offset into the packed struct, so now // we just canonicalize "[idx]" and add the ".mem" offset to // get a collapsed index. NetExpr*packed_base = 0; if(net->packed_dimensions() > 1) { listtmp_index = base_index; index_component_t member_select; member_select.sel = index_component_t::SEL_BIT; member_select.msb = new PENumber(new verinum(off)); tmp_index.push_back(member_select); packed_base = collapse_array_exprs(des, scope, li, net, tmp_index); ivl_assert(*li, packed_base); if (debug_elaborate) { cerr << li->get_fileline() << ": debug: check_for_struct_members: " << "Got collapsed array expr: " << *packed_base << endl; } } long tmp; if (packed_base && eval_as_long(tmp, packed_base)) { off = tmp; delete packed_base; packed_base = 0; } NetESignal*sig = new NetESignal(net); NetExpr *base = packed_base? packed_base : make_const_val(off); if (debug_elaborate) { cerr << li->get_fileline() << ": debug: check_for_struct_members: " << "Convert packed indices/member select into part select: " << *base << endl; } NetESelect*sel = new NetESelect(sig, base, use_width); return sel; } static NetExpr* class_static_property_expression(const LineInfo*li, const netclass_t*class_type, perm_string name) { NetNet*sig = class_type->find_static_property(name); ivl_assert(*li, sig); NetESignal*expr = new NetESignal(sig); expr->set_line(*li); return expr; } static NetExpr* check_for_class_property(const LineInfo*li, Design*des, NetScope*scope, NetNet*net, const name_component_t&comp) { const netclass_t*class_type = net->class_type(); int pidx = class_type->property_idx_from_name(comp.name); if (pidx < 0) { cerr << li->get_fileline() << ": error: " << "Class " << class_type->get_name() << " has no property " << comp.name << "." << endl; des->errors += 1; return 0; } if (debug_elaborate) { cerr << li->get_fileline() << ": check_for_class_property: " << "Property " << comp.name << " of net " << net->name() << ", context scope=" << scope_path(scope) << endl; } property_qualifier_t qual = class_type->get_prop_qual(pidx); if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { cerr << li->get_fileline() << ": error: " << "Local property " << class_type->get_prop_name(pidx) << " is not accessible in this context." << " (scope=" << scope_path(scope) << ")" << endl; des->errors += 1; } if (qual.test_static()) { perm_string prop_name = lex_strings.make(class_type->get_prop_name(pidx)); return class_static_property_expression(li, class_type, prop_name); } NetEProperty*tmp = new NetEProperty(net, comp.name); tmp->set_line(*li); return tmp; } NetExpr* PECallFunction::elaborate_expr_pkg_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_expr_pkg_: " << "Elaborate " << path_ << " as function in package " << package_->pscope_name() << "." << endl; } // Find the package that contains this definition, and use the // package scope as the search starting point for the function // definition. NetScope*pscope = des->find_package(package_->pscope_name()); ivl_assert(*this, pscope); NetFuncDef*def = des->find_function(pscope, path_); ivl_assert(*this, def); NetScope*dscope = def->scope(); ivl_assert(*this, dscope); if (! check_call_matches_definition_(des, dscope)) return 0; return elaborate_base_(des, scope, dscope, expr_wid, flags); } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { if (package_) return elaborate_expr_pkg_(des, scope, expr_wid, flags); flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag if (peek_tail_name(path_)[0] == '$') return elaborate_sfunc_(des, scope, expr_wid, flags); NetFuncDef*def = des->find_function(scope, path_); if (def == 0) { // Not a user defined function. Maybe it is an access // function for a nature? If so then elaborate it that // way. ivl_nature_t access_nature = find_access_function(path_); if (access_nature) return elaborate_access_func_(des, scope, access_nature, expr_wid); // Maybe this is a method attached to a signal? If this // is SystemVerilog then try that possibility. if (gn_system_verilog()) { NetExpr*tmp = elaborate_expr_method_(des, scope, expr_wid); if (tmp) return tmp; } // Nothing was found so report this as an error. cerr << get_fileline() << ": error: No function named `" << path_ << "' found in this context (" << scope_path(scope) << ")." << endl; des->errors += 1; return 0; } ivl_assert(*this, def); NetScope*dscope = def->scope(); ivl_assert(*this, dscope); /* In SystemVerilog a method calling another method in the * current class needs to be elaborated as a method with an * implicit this added. */ if (gn_system_verilog() && (path_.size() == 1)) { const NetScope *c_scope = scope->get_class_scope(); if (c_scope && (c_scope == dscope->get_class_scope())) { NetExpr*tmp = elaborate_expr_method_(des, scope, expr_wid, true); assert(tmp); return tmp; } } bool need_const = NEED_CONST & flags; // It is possible to get here before the called function has been // fully elaborated. If this is the case, elaborate it now. This // ensures we know whether or not it is a constant function. if (dscope->elab_stage() < 3) { dscope->need_const_func(need_const || scope->need_const_func()); const PFunction*pfunc = dscope->func_pform(); ivl_assert(*this, pfunc); pfunc->elaborate(des, dscope); } if (dscope->parent() != scope->parent() || !dscope->is_const_func()) { if (scope->need_const_func()) { cerr << get_fileline() << ": error: A function invoked by " "a constant function must be a constant function " "local to the current module." << endl; des->errors += 1; } scope->is_const_func(false); } return elaborate_base_(des, scope, dscope, expr_wid, flags); } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const { const netdarray_t*darray = dynamic_cast(type); assert(darray); return elaborate_expr(des, scope, darray->element_type()->packed_width(), flags); } NetExpr* PECallFunction::elaborate_base_(Design*des, NetScope*scope, NetScope*dscope, unsigned expr_wid, unsigned flags) const { if (! check_call_matches_definition_(des, dscope)) return 0; NetFuncDef*def = dscope->func_def(); bool need_const = NEED_CONST & flags; // If this is a constant expression, it is possible that we // are being elaborated before the function definition. If // that's the case, try to elaborate the function as a const // function. if (need_const && ! def->proc()) { if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_base_: " << "Try to elaborate " << scope_path(dscope) << " as constant function." << endl; } dscope->set_elab_stage(2); dscope->need_const_func(true); const PFunction*pfunc = dscope->func_pform(); ivl_assert(*this, pfunc); pfunc->elaborate(des, dscope); } unsigned parms_count = def->port_count(); vector parms (parms_count); if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_base_: " << "Expecting " << parms_count << " argument for function " << scope_path(dscope) << "." << endl; } /* Elaborate the input expressions for the function. This is done in the scope of the function call, and not the scope of the function being called. The scope of the called function is elaborated when the definition is elaborated. */ unsigned parm_errors = elaborate_arguments_(des, scope, def, need_const, parms, 0); if (need_const && !dscope->is_const_func()) { // If this is the first time the function has been called in // a constant context, force the function to be re-elaborated. // This will generate the necessary error messages to allow // the user to diagnose the fault. if (!dscope->need_const_func()) { dscope->set_elab_stage(2); dscope->need_const_func(true); const PFunction*pfunc = dscope->func_pform(); ivl_assert(*this, pfunc); pfunc->elaborate(des, dscope); } cerr << get_fileline() << ": error: `" << dscope->basename() << "' is not a constant function." << endl; des->errors += 1; return 0; } if (parm_errors) return 0; /* Look for the return value signal for the called function. This return value is a magic signal in the scope of the function, that has the name of the function. The function code assigns to this signal to return a value. dscope, in this case, is the scope of the function, so the return value is the name within that scope. */ if (NetNet*res = dscope->find_signal(dscope->basename())) { NetESignal*eres = new NetESignal(res); NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms, need_const); func->set_line(*this); if(res->darray_type()) return func; NetExpr*tmp = pad_to_width(func, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } cerr << get_fileline() << ": internal error: Unable to locate " "function return value for " << path_ << " in " << dscope->basename() << "." << endl; des->errors += 1; return 0; } /* * Elaborate the arguments of a function or method. The parms vector * is where to place the elaborated expressions, so it an output. The * parm_off is where in the parms vector to start writing * arguments. This value is normally 0, but is 1 if this is a method * so that parms[0] can hold the "this" argument. In this latter case, * def->port(0) will be the "this" argument and should be skipped. */ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, NetFuncDef*def, bool need_const, vector&parms, unsigned parm_off) const { unsigned parm_errors = 0; unsigned missing_parms = 0; const unsigned parm_count = parms.size() - parm_off; const unsigned actual_count = parms_.size(); /* The parser can't distinguish between a function call with no arguments and a function call with one empty argument, and always supplies one empty argument. Handle the no argument case here. */ if ((parm_count == 0) && (actual_count == 1) && (parms_[0] == 0)) return 0; if (actual_count > parm_count) { cerr << get_fileline() << ": error: " << "Too many arguments (" << actual_count << ", expecting " << parm_count << ")" << " in call to function." << endl; des->errors += 1; } for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { unsigned pidx = idx + parm_off; PExpr*tmp = (idx < actual_count) ? parms_[idx] : 0; if (tmp) { parms[pidx] = elaborate_rval_expr(des, scope, def->port(pidx)->net_type(), def->port(pidx)->data_type(), (unsigned)def->port(pidx)->vector_width(), tmp, need_const); if (parms[pidx] == 0) { parm_errors += 1; continue; } if (NetEEvent*evt = dynamic_cast (parms[pidx])) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " "function argument." << endl; des->errors += 1; } if (debug_elaborate) cerr << get_fileline() << ": debug:" << " function " << path_ << " arg " << (idx+1) << " argwid=" << parms[pidx]->expr_width() << ": " << *parms[idx] << endl; } else if (def->port_defe(pidx)) { if (! gn_system_verilog()) { cerr << get_fileline() << ": internal error: " << "Found (and using) default function argument " << "requires SystemVerilog." << endl; des->errors += 1; } parms[pidx] = def->port_defe(pidx); } else { missing_parms += 1; parms[pidx] = 0; } } if (missing_parms > 0) { cerr << get_fileline() << ": error: The function " << path_ << " has been called with missing/empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; parm_errors += 1; des->errors += 1; } return parm_errors; } NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, unsigned expr_wid, bool add_this_flag) const { pform_name_t use_path = path_; perm_string method_name = peek_tail_name(use_path); use_path.pop_back(); /* Add the implicit this reference when requested. */ if (add_this_flag) { assert(use_path.empty()); use_path.push_front(name_component_t(perm_string::literal("@"))); } // If there is no object to the left of the method name, then // give up on the idea of looking for an object method. if (use_path.empty()) return 0; NetNet *net = 0; const NetExpr *par; NetEvent *eve; const NetExpr *ex1, *ex2; symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); if (net == 0) return 0; if (net->data_type() == IVL_VT_STRING) { if (method_name == "len") { NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$len", IVL_VT_BOOL, 32, 1); sys_expr->parm(0, new NetESignal(net)); return sys_expr; } if (method_name == "substr") { NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$substr", IVL_VT_STRING, 1, 3); sys_expr->set_line(*this); // First argument is the source string. sys_expr->parm(0, new NetESignal(net)); ivl_assert(*this, parms_.size() == 2); NetExpr*tmp; tmp = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, IVL_VT_BOOL, 32, parms_[0], false); sys_expr->parm(1, tmp); tmp = elaborate_rval_expr(des, scope, &netvector_t::atom2u32, IVL_VT_BOOL, 32, parms_[1], false); sys_expr->parm(2, tmp); return sys_expr; } } if (const netenum_t*netenum = net->enumeration()) { // We may need the net expression for the // enumeration variable so get it. NetESignal*expr = new NetESignal(net); expr->set_line(*this); // This expression cannot be a select! assert(use_path.back().index.empty()); PExpr*tmp = parms_.size() ? parms_[0] : 0; return check_for_enum_methods(this, des, scope, netenum, use_path, method_name, expr, expr_wid, tmp, parms_.size()); } if (net->darray_type()) { if (method_name == "size") { NetESFunc*sys_expr = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); sys_expr->parm(0, new NetESignal(net)); sys_expr->set_line(*this); return sys_expr; } if (method_name == "pop_back") { NetESFunc*sys_expr = new NetESFunc("$ivl_darray_method$pop_back", expr_type_, expr_width_, 1); sys_expr->parm(0, new NetESignal(net)); sys_expr->set_line(*this); return sys_expr; } if (method_name == "pop_front") { NetESFunc*sys_expr = new NetESFunc("$ivl_darray_method$pop_front", expr_type_, expr_width_, 1); sys_expr->parm(0, new NetESignal(net)); sys_expr->set_line(*this); return sys_expr; } } if (const netclass_t*class_type = net->class_type()) { NetScope*func = class_type->method_from_name(method_name); if (func == 0) { return 0; } NetFuncDef*def = func->func_def(); ivl_assert(*this, def); NetNet*res = func->find_signal(func->basename()); ivl_assert(*this, res); vectorparms; NetESignal*ethis = new NetESignal(net); ethis->set_line(*this); parms.push_back(ethis); parms.resize(1 + parms_.size()); elaborate_arguments_(des, scope, def, false, parms, 1); NetESignal*eres = new NetESignal(res); NetEUFunc*call = new NetEUFunc(scope, func, eres, parms, false); call->set_line(*this); return call; } return 0; } unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) { expr_width_ = size_; width_mode_t tmp_mode = PExpr::SIZED; base_->test_width(des, scope, tmp_mode); return size_; } NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, unsigned, unsigned) const { NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); NetESelect*sel = new NetESelect(sub, 0, size_); sel->set_line(*this); return sel; } unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&wid) { ivl_type_t t = target_->elaborate_type(des, scope); base_->test_width(des, scope, wid); if(const netdarray_t*use_darray = dynamic_cast (t)) { expr_type_ = use_darray->element_base_type(); expr_width_ = use_darray->element_width(); } else if(const netstring_t*use_string = dynamic_cast (t)) { expr_type_ = use_string->base_type(); expr_width_ = 8; } else { expr_type_ = t->base_type(); expr_width_ = t->packed_width(); } signed_flag_ = t->get_signed(); min_width_ = expr_width_; return expr_width_; } NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned) const { const netdarray_t*darray = NULL; const netvector_t*vector = NULL; // Casting array of vectors to dynamic array type if((darray = dynamic_cast(type)) && (vector = dynamic_cast(darray->element_type()))) { PExpr::width_mode_t mode = PExpr::SIZED; unsigned use_wid = base_->test_width(des, scope, mode); NetExpr*base = base_->elaborate_expr(des, scope, use_wid, NO_FLAGS); assert(vector->packed_width() > 0); assert(base->expr_width() > 0); // Find rounded up length that can fit the whole casted array of vectors int len = base->expr_width() + vector->packed_width() - 1; if(base->expr_width() > vector->packed_width()) { len /= vector->packed_width(); } else { len /= base->expr_width(); } // Number of words in the created dynamic array NetEConst*len_expr = new NetEConst(verinum(len)); return new NetENew(type, len_expr, base); } // Fallback return elaborate_expr(des, scope, (unsigned) 0, 0); } NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, unsigned, unsigned) const { NetExpr*expr = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); if(dynamic_cast(target_)) { return cast_to_real(expr); } if(const atom2_type_t*atom = dynamic_cast(target_)) { if(base_->expr_width() > expr_width_) { cerr << get_fileline() << ": cast type is not wide enough to store the result." << endl; ivl_assert(*this, 0); } if(base_->has_sign() != atom->signed_flag) { cerr << get_fileline() << ": cast type and subject differ in signedness." << endl; ivl_assert(*this, 0); } // That is how you both resize & cast to integers return new NetECast('2', expr, expr_width_, expr->has_sign()); } if(const vector_type_t*vec = dynamic_cast(target_)) { switch(vec->base_type) { case IVL_VT_BOOL: return cast_to_int2(expr, expr_width_); case IVL_VT_LOGIC: return cast_to_int4(expr, expr_width_); default: break; /* Suppress warnings */ } } else if(dynamic_cast(target_)) { if(base_->expr_type() == IVL_VT_STRING) return expr; // no conversion if((base_->expr_type() != IVL_VT_BOOL) && (base_->expr_type() != IVL_VT_LOGIC)) { cerr << get_fileline() << ": cannot be casted to string." << endl; ivl_assert(*this, false); } return expr; } cerr << get_fileline() << ": sorry: I don't know how to cast expression." << endl; ivl_assert(*this, false); return expr; } unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) { expr_width_ = 0; enum {NO, MAYBE, YES} expr_is_string = MAYBE; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { // Add in the width of this sub-expression. expr_width_ += parms_[idx]->test_width(des, scope, width_modes_[idx]); // If we already know this is not a string, then move on. if (expr_is_string == NO) continue; // If this expression is a string, then the // concatenation is a string until we find a reason to // deny it. if (parms_[idx]->expr_type()==IVL_VT_STRING) { expr_is_string = YES; continue; } // If this is a string literal, then this may yet be a string. if (dynamic_cast (parms_[idx])) continue; // Failed to allow a string result. expr_is_string = NO; } expr_type_ = (expr_is_string==YES)? IVL_VT_STRING : IVL_VT_LOGIC; signed_flag_ = false; // If there is a repeat expression, then evaluate the constant // value and set the repeat count. if (repeat_ && (scope != tested_scope_)) { NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1, true); if (tmp == 0) return 0; if (tmp->expr_type() == IVL_VT_REAL) { cerr << tmp->get_fileline() << ": error: Concatenation " << "repeat expression can not be REAL." << endl; des->errors += 1; return 0; } NetEConst*rep = dynamic_cast(tmp); if (rep == 0) { cerr << get_fileline() << ": error: " "Concatenation repeat expression is not constant." << endl; cerr << get_fileline() << ": : The expression is: " << *tmp << endl; des->errors += 1; return 0; } if (!rep->value().is_defined()) { cerr << get_fileline() << ": error: Concatenation repeat " << "may not be undefined (" << rep->value() << ")." << endl; des->errors += 1; return 0; } if (rep->value().is_negative()) { cerr << get_fileline() << ": error: Concatenation repeat " << "may not be negative (" << rep->value().as_long() << ")." << endl; des->errors += 1; return 0; } repeat_count_ = rep->value().as_ulong(); tested_scope_ = scope; } expr_width_ *= repeat_count_; min_width_ = expr_width_; return expr_width_; } // Keep track of the concatenation/repeat depth. static int concat_depth = 0; NetExpr* PEConcat::elaborate_expr(Design*, NetScope*, ivl_type_t type, unsigned /*flags*/) const { switch (type->base_type()) { case IVL_VT_QUEUE: if (parms_.size() == 0) { NetENull*tmp = new NetENull; tmp->set_line(*this); return tmp; } default: cerr << get_fileline() << ": internal error: " << "I don't know how to elaborate(ivl_type_t)" << " this expression: " << *this << endl; return 0; } } NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag concat_depth += 1; if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate expr=" << *this << ", expr_wid=" << expr_wid << endl; } if (repeat_count_ == 0 && concat_depth < 2) { cerr << get_fileline() << ": error: Concatenation repeat " << "may not be zero in this context." << endl; des->errors += 1; concat_depth -= 1; return 0; } unsigned wid_sum = 0; unsigned parm_cnt = 0; unsigned parm_errors = 0; svector parms(parms_.size()); /* Elaborate all the parameters and attach them to the concat node. */ for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == 0) { cerr << get_fileline() << ": error: Missing expression " << (idx+1) << " of concatenation list." << endl; des->errors += 1; continue; } assert(parms_[idx]); unsigned wid = parms_[idx]->expr_width(); NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, flags); if (ex == 0) continue; ex->set_line(*parms_[idx]); eval_expr(ex, -1); if (ex->expr_type() == IVL_VT_REAL) { cerr << ex->get_fileline() << ": error: " << "Concatenation operand can not be real: " << *parms_[idx] << endl; des->errors += 1; parm_errors += 1; continue; } if (width_modes_[idx] != SIZED) { cerr << ex->get_fileline() << ": error: " << "Concatenation operand \"" << *parms_[idx] << "\" has indefinite width." << endl; des->errors += 1; parm_errors += 1; continue; } /* We are going to ignore zero width constants. */ if ((ex->expr_width() == 0) && dynamic_cast(ex)) { parms[idx] = 0; } else { parms[idx] = ex; parm_cnt += 1; } wid_sum += ex->expr_width(); } if (parm_errors) { concat_depth -= 1; return 0; } /* Make the empty concat expression. */ NetEConcat*concat = new NetEConcat(parm_cnt, repeat_count_, expr_type_); concat->set_line(*this); /* Remove any zero width constants. */ unsigned off = 0; for (unsigned idx = 0 ; idx < parm_cnt ; idx += 1) { while (parms[off+idx] == 0) off += 1; concat->set(idx, parms[off+idx]); } if (wid_sum == 0) { cerr << get_fileline() << ": error: Concatenation/replication " << "may not have zero width in this context." << endl; des->errors += 1; concat_depth -= 1; delete concat; return 0; } NetExpr*tmp = pad_to_width(concat, expr_wid, *this); tmp->cast_signed(signed_flag_); concat_depth -= 1; return tmp; } /* * Floating point literals are not vectorable. It's not particularly * clear what to do about an actual width to return, but whatever the * width, it is unsigned. * * Absent any better idea, we call all real valued results a width of 1. */ unsigned PEFNumber::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_REAL; expr_width_ = 1; min_width_ = 1; signed_flag_ = true; return expr_width_; } NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, ivl_type_t, unsigned) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); return tmp; } NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); return tmp; } bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, list&prefix_indices) const { unsigned dimensions = net->unpacked_dimensions() + net->packed_dimensions(); switch (net->data_type()) { case IVL_VT_STRING: case IVL_VT_DARRAY: case IVL_VT_QUEUE: dimensions += 1; default: break; } if (path_.back().index.size() > dimensions) { cerr << get_fileline() << ": error: the number of indices (" << path_.back().index.size() << ") is greater than the number of dimensions (" << dimensions << ")." << endl; des->errors += 1; return false; } list index; index = path_.back().index; ivl_assert(*this, index.size() >= net->unpacked_dimensions()); for (size_t idx = 0 ; idx < net->unpacked_dimensions() ; idx += 1) index.pop_front(); return evaluate_index_prefix(des, scope, prefix_indices, index); } bool PEIdent::calculate_bits_(Design*des, NetScope*scope, long&msb, bool&defined) const { defined = true; const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.sel == index_component_t::SEL_BIT); ivl_assert(*this, index_tail.msb && !index_tail.lsb); /* This handles bit selects. In this case, there in one bit select expressions which must be constant. */ NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { cerr << index_tail.msb->get_fileline() << ": error: " "Bit select expressions must be constant." << endl; cerr << index_tail.msb->get_fileline() << ": : " "This msb expression violates the rule: " << *index_tail.msb << endl; des->errors += 1; /* Attempt to recover from error. */ msb = 0; } else { if (! msb_c->value().is_defined()) defined = false; msb = msb_c->value().as_long(); } delete msb_ex; return true; } /* * Given that the msb_ and lsb_ are part select expressions, this * function calculates their values. Note that this method does *not* * convert the values to canonical form. */ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, long&msb, long&lsb, bool&defined) const { defined = true; const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.sel == index_component_t::SEL_PART); ivl_assert(*this, index_tail.msb && index_tail.lsb); /* This handles part selects. In this case, there are two bit select expressions, and both must be constant. Evaluate them and pass the results back to the caller. */ NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*lsb_c = dynamic_cast(lsb_ex); if (lsb_c == 0) { cerr << index_tail.lsb->get_fileline() << ": error: " "Part select expressions must be constant." << endl; cerr << index_tail.lsb->get_fileline() << ": : " "This lsb expression violates the rule: " << *index_tail.lsb << endl; des->errors += 1; /* Attempt to recover from error. */ lsb = 0; } else { if (! lsb_c->value().is_defined()) defined = false; lsb = lsb_c->value().as_long(); } NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { cerr << index_tail.msb->get_fileline() << ": error: " "Part select expressions must be constant." << endl; cerr << index_tail.msb->get_fileline() << ": : " "This msb expression violates the rule: " << *index_tail.msb << endl; des->errors += 1; /* Attempt to recover from error. */ msb = lsb; } else { if (! msb_c->value().is_defined()) defined = false; msb = msb_c->value().as_long(); } delete msb_ex; delete lsb_ex; return true; } bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, unsigned long&wid) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.lsb && index_tail.msb); bool flag = true; /* Calculate the width expression (in the lsb_ position) first. If the expression is not constant, error but guess 1 so we can keep going and find more errors. */ NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*wid_c = dynamic_cast(wid_ex); wid = wid_c? wid_c->value().as_ulong() : 0; if (wid == 0) { cerr << index_tail.lsb->get_fileline() << ": error: " "Indexed part widths must be constant and greater than zero." << endl; cerr << index_tail.lsb->get_fileline() << ": : " "This part width expression violates the rule: " << *index_tail.lsb << endl; des->errors += 1; flag = false; wid = 1; } delete wid_ex; return flag; } /* * When we know that this is an indexed part select (up or down) this * method calculates the up/down base, as far at it can be calculated. */ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope, bool need_const) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.lsb != 0); ivl_assert(*this, index_tail.msb != 0); NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1, need_const); return tmp; } bool PEIdent::calculate_param_range_(Design*, NetScope*, const NetExpr*par_msb, long&par_msv, const NetExpr*par_lsb, long&par_lsv, long length) const { if (par_msb == 0) { // If the parameter doesn't have an explicit range, then // just return range values of [length-1:0]. ivl_assert(*this, par_lsb == 0); par_msv = length-1; par_lsv = 0; return true; } const NetEConst*tmp = dynamic_cast (par_msb); ivl_assert(*this, tmp); par_msv = tmp->value().as_long(); tmp = dynamic_cast (par_lsb); ivl_assert(*this, tmp); par_lsv = tmp->value().as_long(); return true; } unsigned PEIdent::test_width_method_(Design*des, NetScope*scope, width_mode_t&) { if (!gn_system_verilog()) return 0; if (path_.size() < 2) return 0; pform_name_t use_path = path_; perm_string member_name = peek_tail_name(path_); use_path.pop_back(); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::test_width_method_: " << "Try to find method=" << member_name << " of signal " << use_path << endl; } NetNet*net = 0; const NetExpr*par = 0; NetEvent*eve = 0; const NetExpr*ex1 = 0, *ex2 = 0; symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); if (net == 0) { if (debug_elaborate) cerr << get_fileline() << ": PEIdent::test_width_method_: " << "Only nets can have methods, so give up here." << endl; return 0; } if (/*const netdarray_t*dtype =*/ net->darray_type()) { if (member_name == "size") { expr_type_ = IVL_VT_BOOL; expr_width_ = 32; min_width_ = 32; signed_flag_= true; return 32; } } return 0; } unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) { NetNet* net = 0; const NetExpr*par = 0; NetEvent* eve = 0; const NetExpr*ex1, *ex2; NetScope*use_scope = scope; if (package_) { use_scope = des->find_package(package_->pscope_name()); ivl_assert(*this, use_scope); } if (unsigned tmp = test_width_method_(des, scope, mode)) { return tmp; } NetScope*found_in = symbol_search(this, des, use_scope, path_, net, par, eve, ex1, ex2); // If there is a part/bit select expression, then process it // here. This constrains the results no matter what kind the // name is. const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) { const index_component_t&index_tail = name_tail.index.back(); // Skip full array word net selects. if (!net || (name_tail.index.size() > net->unpacked_dimensions())) { use_sel = index_tail.sel; } } unsigned use_width = UINT_MAX; switch (use_sel) { case index_component_t::SEL_NONE: break; case index_component_t::SEL_PART: { long msb, lsb; bool parts_defined; calculate_parts_(des, scope, msb, lsb, parts_defined); if (parts_defined) use_width = 1 + ((msb>lsb)? (msb-lsb) : (lsb-msb)); else use_width = UINT_MAX; break; } case index_component_t::SEL_IDX_UP: case index_component_t::SEL_IDX_DO: { unsigned long tmp = 0; calculate_up_do_width_(des, scope, tmp); use_width = tmp; break; } case index_component_t::SEL_BIT: { ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb); } // If we have a net in hand, then we can predict what the // slice width will be. If not, then assume it will be a // simple bit select. If the net only has a single dimension // then this is still a simple bit select. if ((net == 0) || (net->packed_dimensions() <= 1)) use_width = 1; break; case index_component_t::SEL_BIT_LAST: if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::test_width: " << "Queue/Darray last index ($)" << endl; } break; default: ivl_assert(*this, 0); } if (const netdarray_t*darray = net? net->darray_type() : 0) { switch (use_sel) { case index_component_t::SEL_BIT: case index_component_t::SEL_BIT_LAST: expr_type_ = darray->element_base_type(); expr_width_ = darray->element_width(); min_width_ = expr_width_; signed_flag_ = net->get_signed(); break; default: expr_type_ = net->data_type(); expr_width_ = net->vector_width(); min_width_ = expr_width_; signed_flag_ = net->get_signed(); break; } return expr_width_; } if (use_width != UINT_MAX) { expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic expr_width_ = use_width; min_width_ = use_width; signed_flag_ = false; return expr_width_; } // The width of a signal expression is the width of the signal. if (net != 0) { size_t use_depth = name_tail.index.size(); // Account for unpacked dimensions by assuming that the // unpacked dimensions are consumed first, so subtract // the unpacked dimensions from the dimension depth // useable for making the slice. if (use_depth >= net->unpacked_dimensions()) { use_depth -= net->unpacked_dimensions(); } else { // In this case, we have a slice of an unpacked // array. This likely handled as an array instead // of a slice. Hmm... use_depth = 0; } expr_type_ = net->data_type(); expr_width_ = net->slice_width(use_depth); min_width_ = expr_width_; signed_flag_ = net->get_signed(); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::test_width: " << net->name() << " is a net, " << "type=" << expr_type_ << ", width=" << expr_width_ << ", signed_=" << (signed_flag_?"true":"false") << ", use_depth=" << use_depth << ", packed_dimensions=" << net->packed_dimensions() << ", unpacked_dimensions=" << net->unpacked_dimensions() << endl; } return expr_width_; } // The width of an enumeration literal is the width of the // enumeration base. if (const NetEConstEnum*par_enum = dynamic_cast (par)) { const netenum_t*use_enum = par_enum->enumeration(); ivl_assert(*this, use_enum != 0); expr_type_ = use_enum->base_type(); expr_width_ = use_enum->packed_width(); min_width_ = expr_width_; signed_flag_ = par_enum->has_sign(); return expr_width_; } // The width of a parameter is the width of the parameter value // (as evaluated earlier). if (par != 0) { expr_type_ = par->expr_type(); expr_width_ = par->expr_width(); min_width_ = expr_width_; signed_flag_ = par->has_sign(); if (!par->has_width() && (mode < LOSSLESS)) mode = LOSSLESS; return expr_width_; } if (path_.size() == 1 && scope->genvar_tmp.str() && strcmp(peek_tail_name(path_), scope->genvar_tmp) == 0) { verinum val (scope->genvar_tmp_val); expr_type_ = IVL_VT_BOOL; expr_width_ = val.len(); min_width_ = expr_width_; signed_flag_ = true; if (gn_strict_expr_width_flag) { expr_width_ = integer_width; mode = UNSIZED; } else if (mode < LOSSLESS) { mode = LOSSLESS; } return expr_width_; } // If this is SystemVerilog then maybe this is a structure element. if (gn_system_verilog() && found_in==0 && path_.size() >= 2) { pform_name_t use_path = path_; perm_string method_name = peek_tail_name(use_path); use_path.pop_back(); ivl_assert(*this, net == 0); symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); // Check to see if we have a net and if so is it a structure? if (net != 0) { // If this net is a struct, the method name may be // a struct member. If it is, then we know the // width of this identifier my knowing the width // of the member. We don't even need to know // anything about positions in containing arrays. if (net->struct_type() != 0) { if (debug_elaborate) { cerr << get_fileline() << ": debug: PEIdent::test_width: " << "Net " << use_path << " is a struct, " << "checking width of member " << method_name << endl; } const netstruct_t::member_t*mem; unsigned long unused; mem = get_struct_member(this, des, scope, net, method_name, unused); if (mem) { expr_type_ = mem->data_type(); expr_width_ = mem->net_type->packed_width(); min_width_ = expr_width_; signed_flag_ = mem->get_signed(); return expr_width_; } } if (const netclass_t*class_type = net->class_type()) { int pidx = class_type->property_idx_from_name(method_name); if (pidx >= 0) { ivl_type_t ptype = class_type->get_prop_type(pidx); expr_type_ = ptype->base_type(); expr_width_ = ptype->packed_width(); min_width_ = expr_width_; signed_flag_ = ptype->get_signed(); return expr_width_; } } } } // Not a net, and not a parameter? Give up on the type, but // set the width to 0. expr_type_ = IVL_VT_NO_TYPE; expr_width_ = 0; min_width_ = 0; signed_flag_ = false; return expr_width_; } NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, ivl_type_t ntype, unsigned flags) const { bool need_const = NEED_CONST & flags; NetNet* net = 0; const NetExpr*par = 0; NetEvent* eve = 0; const NetExpr*ex1, *ex2; NetScope*use_scope = scope; if (package_) { use_scope = des->find_package(package_->pscope_name()); ivl_assert(*this, use_scope); } if (NetExpr* tmp = elaborate_expr_class_member_(des, scope, 0, flags)) { return tmp; } /* NetScope*found_in = */ symbol_search(this, des, use_scope, path_, net, par, eve, ex1, ex2); if (net == 0 && gn_system_verilog() && path_.size() >= 2) { pform_name_t use_path = path_; name_component_t member_comp = use_path.back(); use_path.pop_back(); ivl_assert(*this, net == 0); symbol_search(this, des, use_scope, use_path, net, par, eve, ex1, ex2); if (net == 0) { // Nope, no struct/class with member. } else if (net->struct_type() != 0) { return check_for_struct_members(this, des, use_scope, net, use_path.back().index, member_comp); } else if (net->class_type()!=0) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr: " << "Ident " << use_path << " look for property " << member_comp << endl; } return check_for_class_property(this, des, scope, net, member_comp); } } if (net == 0) { cerr << get_fileline() << ": internal error: " << "Expecting idents with ntype to be signals." << endl; des->errors += 1; return 0; } if (! ntype->type_compatible(net->net_type())) { cerr << get_fileline() << ": internal_error: " << "net type doesn't match context type." << endl; cerr << get_fileline() << ": : " << "net type="; if (net->net_type()) net->net_type()->debug_dump(cerr); else cerr << ""; cerr << endl; cerr << get_fileline() << ": : " << "context type="; ivl_assert(*this, ntype); ntype->debug_dump(cerr); cerr << endl; } ivl_assert(*this, ntype->type_compatible(net->net_type())); const name_component_t&use_comp = path_.back(); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr: " << "Typed ident " << net->name() << " with " << use_comp.index.size() << " indices" << " and " << net->unpacked_dimensions() << " expected." << endl; } if (net->unpacked_dimensions() != use_comp.index.size()) { cerr << get_fileline() << ": sorry: " << "Net " << net->name() << " expects " << net->unpacked_dimensions() << ", but got " << use_comp.index.size() << "." << endl; des->errors += 1; NetESignal*tmp = new NetESignal(net); tmp->set_line(*this); return tmp; } if (net->unpacked_dimensions() == 0) { NetESignal*tmp = new NetESignal(net); tmp->set_line(*this); return tmp; } // Convert a set of index expressions to a single expression // that addresses the canonical element. listunpacked_indices; list unpacked_indices_const; indices_flags idx_flags; indices_to_expressions(des, scope, this, use_comp.index, net->unpacked_dimensions(), need_const, idx_flags, unpacked_indices, unpacked_indices_const); NetExpr*canon_index = 0; if (idx_flags.invalid) { // Nothing to do } else if (idx_flags.undefined) { cerr << get_fileline() << ": warning: " << "returning 'bx for undefined array access " << net->name() << as_indices(unpacked_indices) << "." << endl; } else if (idx_flags.variable) { ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); canon_index = normalize_variable_unpacked(net, unpacked_indices); } else { ivl_assert(*this, unpacked_indices_const.size() == net->unpacked_dimensions()); canon_index = normalize_variable_unpacked(net, unpacked_indices_const); } ivl_assert(*this, canon_index); NetESignal*tmp = new NetESignal(net, canon_index); tmp->set_line(*this); return tmp; } /* * Guess that the path_ is the name of a member of a containing class, * and see how that works. If it turns out that the current scope is * not a method, or the name is not in the parent class, then * fail. Otherwise, return a NetEProperty. */ NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope, unsigned, unsigned) const { if (!gn_system_verilog()) return 0; if (scope->parent() == 0) return 0; if (path_.size() != 1) return 0; const netclass_t*class_type = find_class_containing_scope(*this, scope); if (class_type == 0) return 0; const name_component_t&name_comp = path_.back(); perm_string member_name = name_comp.name; int pidx = class_type->property_idx_from_name(member_name); if (pidx < 0) return 0; NetScope*scope_method = find_method_containing_scope(*this, scope); ivl_assert(*this, scope_method); NetNet*this_net = scope_method->find_signal(perm_string::literal("@")); if (this_net == 0) { cerr << get_fileline() << ": internal error: " << "Unable to find 'this' port of " << scope_path(scope_method) << "." << endl; return 0; } if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member: " << "Found member " << member_name << " is a member of class " << class_type->get_name() << ", context scope=" << scope_path(scope) << ", type=" << *class_type->get_prop_type(pidx) << ", so making a NetEProperty." << endl; } property_qualifier_t qual = class_type->get_prop_qual(pidx); if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { cerr << get_fileline() << ": error: " << "Local property " << class_type->get_prop_name(pidx) << " is not accessible in this context." << " (scope=" << scope_path(scope) << ")" << endl; des->errors += 1; } if (qual.test_static()) { return class_static_property_expression(this, class_type, member_name); } NetExpr*canon_index = 0; ivl_type_t tmp_type = class_type->get_prop_type(pidx); if (const netuarray_t*tmp_ua = dynamic_cast(tmp_type)) { const std::vector&dims = tmp_ua->static_dimensions(); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member_: " << "Property " << class_type->get_prop_name(pidx) << " has " << dims.size() << " dimensions, " << " got " << name_comp.index.size() << " indices." << endl; } if (dims.size() != name_comp.index.size()) { cerr << get_fileline() << ": error: " << "Got " << name_comp.index.size() << " indices, " << "expecting " << dims.size() << " to index the property " << class_type->get_prop_name(pidx) << "." << endl; des->errors += 1; } else { canon_index = make_canonical_index(des, scope, this, name_comp.index, tmp_ua, false); } } if (debug_elaborate && canon_index) { cerr << get_fileline() << ": PEIdent::elaborate_expr_class_member_: " << "Property " << class_type->get_prop_name(pidx) << " canonical index: " << *canon_index << endl; } NetEProperty*tmp = new NetEProperty(this_net, member_name, canon_index); tmp->set_line(*this); return tmp; } NetExpr* PEIdent::elaborate_expr_method_(Design*des, NetScope*scope, unsigned, unsigned) const { if (!gn_system_verilog()) return 0; if (path_.size() < 2) return 0; pform_name_t use_path = path_; perm_string member_name = peek_tail_name(path_); use_path.pop_back(); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " << "Try to find method=" << member_name << " of signal " << use_path << endl; } NetNet*net = 0; const NetExpr*par = 0; NetEvent*eve = 0; const NetExpr*ex1 = 0, *ex2 = 0; symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); if (net == 0) { if (debug_elaborate) cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " << "Only nets can have methods, so give up here." << endl; return 0; } if (net->darray_type()) { if (member_name == "size") { NetESFunc*fun = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); fun->set_line(*this); NetESignal*arg = new NetESignal(net); arg->set_line(*net); fun->parm(0, arg); return fun; } return 0; } if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " << "Give up trying to find method " << member_name << " of " << path_ << "." << endl; } return 0; } /* * Elaborate an identifier in an expression. The identifier can be a * parameter name, a signal name or a memory name. It can also be a * scope name (Return a NetEScope) but only certain callers can use * scope names. However, we still support it here. * * Function names are not handled here, they are detected by the * parser and are elaborated by PECallFunction. * * The signal name may be escaped, but that affects nothing here. */ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { assert(scope); NetNet* net = 0; const NetExpr*par = 0; NetEvent* eve = 0; const NetExpr*ex1, *ex2; // Special case: Detect the special situation that this name // is the name of a variable in the class, and this is a class // method. We sense that this might be the case by noting that // the parent scope of where we are working is a // NetScope::CLASS, the path_ is a single component, and the // name is a property of the class. If that turns out to be // the case, then handle this specially. if (NetExpr*tmp = elaborate_expr_class_member_(des, scope, expr_wid, flags)) { return tmp; } if (path_.size() > 1) { if (NEED_CONST & flags) { cerr << get_fileline() << ": error: A hierarchical reference" " (`" << path_ << "') is not allowed in a constant" " expression." << endl; des->errors += 1; return 0; } if (scope->need_const_func()) { cerr << get_fileline() << ": error: A hierarchical reference" " (`" << path_ << "') is not allowed in a constant" " function." << endl; des->errors += 1; return 0; } scope->is_const_func(false); } if (debug_elaborate) cerr << get_fileline() << ": PEIdent::elaborate_expr: path_=" << path_ << endl; NetScope*use_scope = scope; if (package_) { use_scope = des->find_package(package_->pscope_name()); ivl_assert(*this, use_scope); } // Special case: Detect the special situation that the name is // a method of an object (including built-in methods) that has // no arguments. For example, "foo.size" is the call to the // size() method if foo is an array type. if (NetExpr*tmp = elaborate_expr_method_(des, scope, expr_wid, flags)) { return tmp; } NetScope*found_in = symbol_search(this, des, use_scope, path_, net, par, eve, ex1, ex2); // If the identifier name is a parameter name, then return // the parameter value. if (par != 0) { NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, ex1, ex2, expr_wid, flags); if (!tmp) return 0; tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. if (net != 0) { if (NEED_CONST & flags) { cerr << get_fileline() << ": error: A reference to a wire " "or reg (`" << path_ << "') is not allowed in " "a constant expression." << endl; des->errors += 1; return 0; } if (net->scope()->type() == NetScope::MODULE) { if (scope->need_const_func()) { cerr << get_fileline() << ": error: A reference to a " "non-local wire or reg (`" << path_ << "') is " "not allowed in a constant function." << endl; des->errors += 1; return 0; } scope->is_const_func(false); } NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, expr_wid, flags); if (!tmp) return 0; if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr: " << "Expression as net. expr_wid=" << expr_wid << ", tmp->expr_width()=" << tmp->expr_width() << ", tmp=" << *tmp << endl; } tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } // If the identifier is a named event // then create a NetEEvent node to handle it. if (eve != 0) { if (NEED_CONST & flags) { cerr << get_fileline() << ": error: A reference to a named " "event (`" << path_ << "') is not allowed in a " "constant expression." << endl; des->errors += 1; return 0; } if (eve->scope() != scope) { if (scope->need_const_func()) { cerr << get_fileline() << ": error: A reference to a " "non-local named event (`" << path_ << "') is " "not allowed in a constant function." << endl; des->errors += 1; return 0; } scope->is_const_func(false); } NetEEvent*tmp = new NetEEvent(eve); tmp->set_line(*this); return tmp; } // Hmm... maybe this is a genvar? This is only possible while // processing generate blocks, but then the genvar_tmp will be // set in the scope. if (path_.size() == 1 && scope->genvar_tmp.str() && strcmp(peek_tail_name(path_), scope->genvar_tmp) == 0) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << path_ << " is genvar with value " << scope->genvar_tmp_val << "." << endl; verinum val (scope->genvar_tmp_val, expr_wid); val.has_sign(true); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; } // Maybe this is a method attached to an enumeration name? If // this is SystemVerilog, then test to see if the name is // really a method attached to an object. if (gn_system_verilog() && found_in==0 && path_.size() >= 2) { pform_name_t use_path = path_; name_component_t member_comp = use_path.back(); use_path.pop_back(); if (debug_elaborate) cerr << get_fileline() << ": PEIdent::elaborate_expr: " << "Look for base_path " << use_path << " for member " << member_comp << "." << endl; ivl_assert(*this, net == 0); symbol_search(this, des, use_scope, use_path, net, par, eve, ex1, ex2); // Check to see if we have a net and if so is it an // enumeration? If so then check to see if this is an // enumeration method call. if (net != 0) { // If this net is actually an enum, the method may // be an enumeration method. if (const netenum_t*netenum = net->enumeration()) { // We may need the net expression for the // enumeration variable so get it. NetESignal*expr = new NetESignal(net); expr->set_line(*this); // This expression cannot be a select! assert(use_path.back().index.empty()); return check_for_enum_methods(this, des, use_scope, netenum, use_path, member_comp.name, expr, expr_wid, NULL, 0); } // If this net is a struct, the method name may be // a struct member. if (net->struct_type() != 0) { if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "PEIdent::elaborate_expr: " << "Ident " << use_path << " is a struct." << " Expecting " << net->packed_dims().size() << "-1 dimensions, " << "got " << use_path.back().index.size() << "." << endl; } NetExpr*tmp = check_for_struct_members(this, des, use_scope, net, use_path.back().index, member_comp); if (!tmp) return 0; tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(signed_flag_); return tmp; } if (net->class_type() != 0) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr: " << "Ident " << use_path << " look for property " << member_comp << endl; } return check_for_class_property(this, des, use_scope, net, member_comp); } } } // At this point we've exhausted all the possibilities that // are not scopes. If this is not a system task argument, then // it cannot be a scope name, so give up. if ( !(SYS_TASK_ARG & flags) ) { // I cannot interpret this identifier. Error message. cerr << get_fileline() << ": error: Unable to bind " << (NEED_CONST & flags ? "parameter" : "wire/reg/memory") << " `" << path_ << "' in `" << scope_path(scope) << "'" << endl; if (scope->need_const_func()) { cerr << get_fileline() << ": : `" << scope->basename() << "' is being used as a constant function, so may " "only reference local variables." << endl; } des->errors += 1; return 0; } // Finally, if this is a scope name, then return that. Look // first to see if this is a name of a local scope. Failing // that, search globally for a hierarchical name. if ((path_.size() == 1)) { hname_t use_name ( peek_tail_name(path_) ); if (NetScope*nsc = scope->child(use_name)) { NetEScope*tmp = new NetEScope(nsc); tmp->set_line(*this); if (debug_elaborate) cerr << get_fileline() << ": debug: Found scope " << use_name << " in scope " << scope->basename() << endl; return tmp; } } list spath = eval_scope_path(des, scope, path_); ivl_assert(*this, spath.size() == path_.size()); // Try full hierarchical scope name. if (NetScope*nsc = des->find_scope(spath)) { NetEScope*tmp = new NetEScope(nsc); tmp->set_line(*this); if (debug_elaborate) cerr << get_fileline() << ": debug: Found scope " << nsc->basename() << " path=" << path_ << endl; if ( !(SYS_TASK_ARG & flags) ) { cerr << get_fileline() << ": error: Scope name " << nsc->basename() << " not allowed here." << endl; des->errors += 1; } return tmp; } // Try relative scope name. if (NetScope*nsc = des->find_scope(scope, spath)) { NetEScope*tmp = new NetEScope(nsc); tmp->set_line(*this); if (debug_elaborate) cerr << get_fileline() << ": debug: Found scope " << nsc->basename() << " in " << scope_path(scope) << endl; return tmp; } // I cannot interpret this identifier. Error message. cerr << get_fileline() << ": error: Unable to bind wire/reg/memory " "`" << path_ << "' in `" << scope_path(scope) << "'" << endl; des->errors += 1; return 0; } static verinum param_part_select_bits(const verinum&par_val, long wid, long lsv) { verinum result (verinum::Vx, wid, true); for (long idx = 0 ; idx < wid ; idx += 1) { long off = idx + lsv; if (off < 0) continue; else if (off < (long)par_val.len()) result.set(idx, par_val.get(off)); else if (par_val.is_string()) // Pad strings with nulls. result.set(idx, verinum::V0); else if (par_val.has_len()) // Pad sized parameters with X continue; else // Unsized parameters are "infinite" width. result.set(idx, sign_bit(par_val)); } // If the input is a string, and the part select is working on // byte boundaries, then make the result into a string. if (par_val.is_string() && (labs(lsv)%8 == 0) && (wid%8 == 0)) return result.as_string(); return result; } NetExpr* PEIdent::elaborate_expr_param_bit_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); long par_msv, par_lsv; if(! calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv, par_ex->value().len())) return 0; const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb); ivl_assert(*this, !index_tail.lsb); NetExpr*sel = elab_and_eval(des, scope, index_tail.msb, -1, need_const); if (sel == 0) return 0; if (debug_elaborate) cerr << get_fileline() << ": debug: Calculate bit select " << "[" << *sel << "] from range " << "[" << par_msv << ":" << par_lsv << "]." << endl; perm_string name = peek_tail_name(path_); // Handle the special case that the selection is constant. In this // case, just precalculate the entire constant result. if (NetEConst*sel_c = dynamic_cast (sel)) { // Special case: If the bit select is constant and not fully // defined, then we know that the result must be 1'bx. if (! sel_c->value().is_defined()) { if (warn_ob_select) { cerr << get_fileline() << ": warning: " "Constant undefined bit select [" << sel_c->value() << "] for parameter '" << name << "'." << endl; cerr << get_fileline() << ": : " "Replacing select with a constant 1'bx." << endl; } NetEConst*res = make_const_x(1); res->set_line(*this); return res; } // Calculate the canonical index value. long sel_v = sel_c->value().as_long(); if (par_msv >= par_lsv) sel_v -= par_lsv; else sel_v = par_lsv - sel_v; // Select a bit from the parameter. verinum par_v = par_ex->value(); verinum::V rtn = verinum::Vx; // A constant in range select. if ((sel_v >= 0) && ((unsigned long) sel_v < par_v.len())) { rtn = par_v[sel_v]; // An unsized after select. } else if ((sel_v >= 0) && (! par_v.has_len())) { if (par_v.has_sign()) rtn = par_v[par_v.len()-1]; else rtn = verinum::V0; } else if (warn_ob_select) { cerr << get_fileline() << ": warning: " "Constant bit select [" << sel_c->value().as_long() << "] is "; if (sel_v < 0) cerr << "before "; else cerr << "after "; cerr << name << "["; if (par_v.has_len()) cerr << par_msv; else cerr << ""; cerr << ":" << par_lsv << "]." << endl; cerr << get_fileline() << ": : " "Replacing select with a constant 1'bx." << endl; } NetEConst*res = new NetEConst(verinum(rtn, 1)); res->set_line(*this); return res; } sel = normalize_variable_base(sel, par_msv, par_lsv, 1, true); /* Create a parameter reference for the variable select. */ NetEConstParam*ptmp = new NetEConstParam(found_in, name, par_ex->value()); NetScope::param_ref_t pref = found_in->find_parameter(name); ptmp->set_line((*pref).second); NetExpr*tmp = new NetESelect(ptmp, sel, 1); tmp->set_line(*this); return tmp; } NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, const NetExpr*par_lsb, unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; bool flag = calculate_parts_(des, scope, msv, lsv, parts_defined_flag); if (!flag) return 0; const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); long par_msv, par_lsv; if (! calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv, par_ex->value().len())) return 0; if (! parts_defined_flag) { if (warn_ob_select) { const index_component_t&psel = path_.back().index.back(); perm_string name = peek_tail_name(path_); cerr << get_fileline() << ": warning: " "Undefined part select [" << *(psel.msb) << ":" << *(psel.lsb) << "] for parameter '" << name << "'." << endl; cerr << get_fileline() << ": : " "Replacing select with a constant 'bx." << endl; } verinum val(verinum::Vx, expr_wid, true); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; } // Notice that the par_msv is not used in this function other // than for this test. It is used to tell the direction that // the bits are numbers, so that we can make sure the // direction matches the part select direction. After that, // we only need the par_lsv. if ((msv>lsv && par_msv=par_lsv)) { perm_string name = peek_tail_name(path_); cerr << get_fileline() << ": error: Part select " << name << "[" << msv << ":" << lsv << "] is out of order." << endl; des->errors += 1; return 0; } long wid = 1 + labs(msv-lsv); // Watch out for reversed bit numbering. We're making // the part select from LSB to MSB. long base; if (par_msv < par_lsv) { base = par_lsv - lsv; } else { base = lsv - par_lsv; } if (warn_ob_select) { if (base < 0) { perm_string name = peek_tail_name(path_); cerr << get_fileline() << ": warning: Part select " << "[" << msv << ":" << lsv << "] is selecting " "before the parameter " << name << "["; if (par_ex->value().has_len()) cerr << par_msv; else cerr << ""; cerr << ":" << par_lsv << "]." << endl; cerr << get_fileline() << ": : Replacing " "the out of bound bits with 'bx." << endl; } if (par_ex->value().has_len() && (base+wid > (long)par->expr_width())) { perm_string name = peek_tail_name(path_); cerr << get_fileline() << ": warning: Part select " << name << "[" << msv << ":" << lsv << "] is selecting " "after the parameter " << name << "[" << par_msv << ":" << par_lsv << "]." << endl; cerr << get_fileline() << ": : Replacing " "the out of bound bits with 'bx." << endl; } } verinum result = param_part_select_bits(par_ex->value(), wid, base); NetEConst*result_ex = new NetEConst(result); result_ex->set_line(*this); return result_ex; } static void warn_param_ob(long par_msv, long par_lsv, bool defined, long par_base, unsigned long wid, long pwid, const LineInfo *info, perm_string name, bool up) { long par_max; if (defined) { if (par_msv < par_lsv) par_max = par_lsv-par_msv; else par_max = par_msv-par_lsv; } else { if (pwid < 0) par_max = integer_width; else par_max = pwid; } /* Is this a select before the start of the parameter? */ if (par_base < 0) { cerr << info->get_fileline() << ": warning: " << name << "[" << par_base; if (up) cerr << "+:"; else cerr << "-:"; cerr << wid << "] is selecting before vector." << endl; } /* Is this a select after the end of the parameter? */ if (par_base + (long)wid - 1 > par_max) { cerr << info->get_fileline() << ": warning: " << name << "[" << par_base << "+:" << wid << "] is selecting after vector." << endl; } } NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); long par_msv, par_lsv; if(! calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv, par_ex->value().len())) return 0; NetExpr*base = calculate_up_do_base_(des, scope, need_const); if (base == 0) return 0; // Use the part select width already calculated by test_width(). unsigned long wid = min_width_; if (debug_elaborate) cerr << get_fileline() << ": debug: Calculate part select " << "[" << *base << "+:" << wid << "] from range " << "[" << par_msv << ":" << par_lsv << "]." << endl; perm_string name = peek_tail_name(path_); // Handle the special case that the base is constant. In this // case, just precalculate the entire constant result. if (NetEConst*base_c = dynamic_cast (base)) { if (! base_c->value().is_defined()) { NetEConst *ex; ex = new NetEConst(verinum(verinum::Vx, wid, true)); ex->set_line(*this); if (warn_ob_select) { cerr << get_fileline() << ": warning: " << name << "['bx+:" << wid << "] is always outside vector." << endl; } return ex; } long lsv = base_c->value().as_long(); long par_base = par_lsv; // Watch out for reversed bit numbering. We're making // the part select from LSB to MSB. if (par_msv < par_lsv) { par_base = lsv; lsv = par_lsv - wid + 1; } if (warn_ob_select) { bool defined = true; // Check to see if the parameter has a defined range. if (par_msb == 0) { assert(par_lsb == 0); defined = false; } // Get the parameter values width. long pwid = -1; if (par_ex->has_width()) pwid = par_ex->expr_width()-1; warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid, pwid, this, name, true); } verinum result = param_part_select_bits(par_ex->value(), wid, lsv-par_base); NetEConst*result_ex = new NetEConst(result); result_ex->set_line(*this); return result_ex; } base = normalize_variable_base(base, par_msv, par_lsv, wid, true); /* Create a parameter reference for the variable select. */ NetEConstParam*ptmp = new NetEConstParam(found_in, name, par_ex->value()); NetScope::param_ref_t pref = found_in->find_parameter(name); ptmp->set_line((*pref).second); NetExpr*tmp = new NetESelect(ptmp, base, wid, IVL_SEL_IDX_UP); tmp->set_line(*this); return tmp; } NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); long par_msv, par_lsv; if(! calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv, par_ex->value().len())) return 0; NetExpr*base = calculate_up_do_base_(des, scope, need_const); if (base == 0) return 0; // Use the part select width already calculated by test_width(). unsigned long wid = min_width_; if (debug_elaborate) cerr << get_fileline() << ": debug: Calculate part select " << "[" << *base << "-:" << wid << "] from range " << "[" << par_msv << ":" << par_lsv << "]." << endl; perm_string name = peek_tail_name(path_); // Handle the special case that the base is constant. In this // case, just precalculate the entire constant result. if (NetEConst*base_c = dynamic_cast (base)) { if (! base_c->value().is_defined()) { NetEConst *ex; ex = new NetEConst(verinum(verinum::Vx, wid, true)); ex->set_line(*this); if (warn_ob_select) { cerr << get_fileline() << ": warning: " << name << "['bx-:" << wid << "] is always outside vector." << endl; } return ex; } long lsv = base_c->value().as_long(); long par_base = par_lsv + wid - 1; // Watch out for reversed bit numbering. We're making // the part select from LSB to MSB. if (par_msv < par_lsv) { par_base = lsv; lsv = par_lsv; } if (warn_ob_select) { bool defined = true; // Check to see if the parameter has a defined range. if (par_msb == 0) { assert(par_lsb == 0); defined = false; } // Get the parameter values width. long pwid = -1; if (par_ex->has_width()) pwid = par_ex->expr_width()-1; warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid, pwid, this, name, false); } verinum result = param_part_select_bits(par_ex->value(), wid, lsv-par_base); NetEConst*result_ex = new NetEConst(result); result_ex->set_line(*this); return result_ex; } base = normalize_variable_base(base, par_msv, par_lsv, wid, false); /* Create a parameter reference for the variable select. */ NetEConstParam*ptmp = new NetEConstParam(found_in, name, par_ex->value()); NetScope::param_ref_t pref = found_in->find_parameter(name); ptmp->set_line((*pref).second); NetExpr*tmp = new NetESelect(ptmp, base, wid, IVL_SEL_IDX_DOWN); tmp->set_line(*this); return tmp; } /* * Handle the case that the identifier is a parameter reference. The * parameter expression has already been located for us (as the par * argument) so we just need to process the sub-expression. */ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, unsigned expr_wid, unsigned flags) const { bool need_const = NEED_CONST & flags; if (need_const && !(ANNOTATABLE & flags)) { perm_string name = peek_tail_name(path_); if (found_in->make_parameter_unannotatable(name)) { cerr << get_fileline() << ": warning: specparam '" << name << "' is being used in a constant expression." << endl; cerr << get_fileline() << ": : This will prevent it " "being annotated at run time." << endl; } } const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) use_sel = name_tail.index.back().sel; if (par->expr_type() == IVL_VT_REAL && use_sel != index_component_t::SEL_NONE) { perm_string name = peek_tail_name(path_); cerr << get_fileline() << ": error: " << "can not select part of real parameter: " << name << endl; des->errors += 1; return 0; } ivl_assert(*this, use_sel != index_component_t::SEL_BIT_LAST); if (use_sel == index_component_t::SEL_BIT) return elaborate_expr_param_bit_(des, scope, par, found_in, par_msb, par_lsb, need_const); if (use_sel == index_component_t::SEL_PART) return elaborate_expr_param_part_(des, scope, par, found_in, par_msb, par_lsb, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_param_idx_up_(des, scope, par, found_in, par_msb, par_lsb, need_const); if (use_sel == index_component_t::SEL_IDX_DO) return elaborate_expr_param_idx_do_(des, scope, par, found_in, par_msb, par_lsb, need_const); NetExpr*tmp = 0; const NetEConstEnum*etmp = dynamic_cast(par); if (etmp) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate parameter <" << path_ << "> as enumeration constant." << *etmp << endl; tmp = etmp->dup_expr(); tmp = pad_to_width(tmp, expr_wid, *this); } else { perm_string name = peek_tail_name(path_); /* No bit or part select. Make the constant into a NetEConstParam or NetECRealParam as appropriate. */ const NetEConst*ctmp = dynamic_cast(par); if (ctmp) { verinum cvalue = ctmp->value(); if (cvalue.has_len()) cvalue.has_sign(signed_flag_); cvalue = cast_to_width(cvalue, expr_wid); tmp = new NetEConstParam(found_in, name, cvalue); tmp->cast_signed(signed_flag_); tmp->set_line(*par); if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate parameter <" << name << "> as constant " << *tmp << endl; } const NetECReal*rtmp = dynamic_cast(par); if (rtmp) { tmp = new NetECRealParam(found_in, name, rtmp->value()); tmp->set_line(*par); if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate parameter <" << name << "> as constant " << *tmp << endl; } /* The numeric parameter value needs to have the file and line * information for the actual parameter not the expression. */ assert(tmp); NetScope::param_ref_t pref = found_in->find_parameter(name); tmp->set_line((*pref).second); } return tmp; } /* * Handle word selects of vector arrays. */ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, unsigned expr_wid, unsigned flags) const { bool need_const = NEED_CONST & flags; const name_component_t&name_tail = path_.back(); // Special case: This is the entire array, and we are a direct // argument of a system task. if (name_tail.index.empty() && (SYS_TASK_ARG & flags)) { NetESignal*res = new NetESignal(net, 0); res->set_line(*this); return res; } if (name_tail.index.empty()) { cerr << get_fileline() << ": error: Array " << path() << " needs an array index here." << endl; des->errors += 1; return 0; } // Make sure there are enough indices to address an array element. if (name_tail.index.size() < net->unpacked_dimensions()) { cerr << get_fileline() << ": error: Array " << path() << " needs " << net->unpacked_dimensions() << " indices," << " but got only " << name_tail.index.size() << "." << endl; des->errors += 1; return 0; } // Evaluate all the index expressions into an // "unpacked_indices" array. listunpacked_indices; list unpacked_indices_const; indices_flags idx_flags; indices_to_expressions(des, scope, this, name_tail.index, net->unpacked_dimensions(), need_const, idx_flags, unpacked_indices, unpacked_indices_const); NetExpr*canon_index = 0; if (idx_flags.invalid) { // Nothing to do. } else if (idx_flags.undefined) { cerr << get_fileline() << ": warning: " << "returning 'bx for undefined array access " << net->name() << as_indices(unpacked_indices) << "." << endl; } else if (idx_flags.variable) { ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); canon_index = normalize_variable_unpacked(net, unpacked_indices); } else { ivl_assert(*this, unpacked_indices_const.size() == net->unpacked_dimensions()); canon_index = normalize_variable_unpacked(net, unpacked_indices_const); if (canon_index == 0) { cerr << get_fileline() << ": warning: " << "returning 'bx for out of bounds array access " << net->name() << as_indices(unpacked_indices_const) << "." << endl; } } if (canon_index == 0) { NetEConst*xxx = make_const_x(net->vector_width()); xxx->set_line(*this); return xxx; } canon_index->set_line(*this); NetESignal*res = new NetESignal(net, canon_index); res->set_line(*this); // Detect that the word has a bit/part select as well. index_component_t::ctype_t word_sel = index_component_t::SEL_NONE; if (name_tail.index.size() > net->unpacked_dimensions()) word_sel = name_tail.index.back().sel; if (net->get_scalar() && word_sel != index_component_t::SEL_NONE) { cerr << get_fileline() << ": error: can not select part of "; if (res->expr_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << net->name() << as_indices(unpacked_indices) << endl; des->errors += 1; delete res; return 0; } if (word_sel == index_component_t::SEL_PART) return elaborate_expr_net_part_(des, scope, res, found_in, expr_wid); if (word_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_net_idx_up_(des, scope, res, found_in, need_const); if (word_sel == index_component_t::SEL_IDX_DO) return elaborate_expr_net_idx_do_(des, scope, res, found_in, need_const); if (word_sel == index_component_t::SEL_BIT) return elaborate_expr_net_bit_(des, scope, res, found_in, need_const); ivl_assert(*this, word_sel == index_component_t::SEL_NONE); return res; } /* * Handle part selects of NetNet identifiers. */ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, NetESignal*net, NetScope*, unsigned expr_wid) const { list prefix_indices; bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); if (!rc) return 0; long msv, lsv; bool parts_defined_flag; bool flag = calculate_parts_(des, scope, msv, lsv, parts_defined_flag); if (!flag) return 0; /* The indices of part selects are signed integers, so allow negative values. However, the width that they represent is unsigned. Remember that any order is possible, i.e., [1:0], [-4:6], etc. */ unsigned long wid = 1 + labs(msv-lsv); /* But wait... if the part select expressions are not fully defined, then fall back on the tested width. */ if (!parts_defined_flag) { if (warn_ob_select) { const index_component_t&psel = path_.back().index.back(); cerr << get_fileline() << ": warning: " "Undefined part select [" << *(psel.msb) << ":" << *(psel.lsb) << "] for "; if (net->word_index()) cerr << "array word"; else cerr << "vector"; cerr << " '" << net->name(); if (net->word_index()) cerr << "[]"; cerr << "'." << endl; cerr << get_fileline() << ": : " "Replacing select with a constant 'bx." << endl; } NetEConst*tmp = new NetEConst(verinum(verinum::Vx, expr_wid, true)); tmp->set_line(*this); return tmp; } long sb_lsb, sb_msb; if (prefix_indices.size()+1 < net->sig()->packed_dims().size()) { // Here we have a slice that doesn't have enough indices // to get to a single slice. For example: // wire [9:0][5:1] foo // ... foo[4:3] ... // Make this work by finding the indexed slices and // creating a generated slice that spans the whole // range. long loff, moff; unsigned long lwid, mwid; bool lrc; lrc = net->sig()->sb_to_slice(prefix_indices, lsv, loff, lwid); ivl_assert(*this, lrc); lrc = net->sig()->sb_to_slice(prefix_indices, msv, moff, mwid); ivl_assert(*this, lrc); ivl_assert(*this, lwid == mwid); if (moff > loff) { sb_lsb = loff; sb_msb = moff + mwid - 1; } else { sb_lsb = moff; sb_msb = loff + lwid - 1; } wid = sb_msb - sb_lsb + 1; } else { // This case, the prefix indices are enough to index // down to a single bit/slice. ivl_assert(*this, prefix_indices.size()+1 == net->sig()->packed_dims().size()); sb_lsb = net->sig()->sb_to_idx(prefix_indices, lsv); sb_msb = net->sig()->sb_to_idx(prefix_indices, msv); } if (sb_msb < sb_lsb) { cerr << get_fileline() << ": error: part select " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << msv << ":" << lsv << "] is out of order." << endl; des->errors += 1; //delete lsn; //delete msn; return net; } if (warn_ob_select) { if ((sb_lsb >= (signed) net->vector_width()) || (sb_msb >= (signed) net->vector_width())) { cerr << get_fileline() << ": warning: " "Part select " << "[" << msv << ":" << lsv << "] is selecting after the "; if (net->word_index()) cerr << "array word "; else cerr << "vector "; cerr << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << net->msi() << ":" << net->lsi() << "]." << endl; cerr << get_fileline() << ": : " << "Replacing the out of bound bits with 'bx." << endl; } if ((sb_msb < 0) || (sb_lsb < 0)) { cerr << get_fileline() << ": warning: " "Part select " << "[" << msv << ":" << lsv << "] is selecting before the "; if (net->word_index()) cerr << "array word "; else cerr << "vector "; cerr << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << net->msi() << ":" << net->lsi() << "]." << endl; cerr << get_fileline() << ": : " "Replacing the out of bound bits with 'bx." << endl; } } // If the part select covers exactly the entire // vector, then do not bother with it. Return the // signal itself, casting to unsigned if necessary. if (sb_lsb == 0 && wid == net->vector_width()) { net->cast_signed(false); return net; } // If the part select covers NONE of the vector, then return a // constant X. if ((sb_lsb >= (signed) net->vector_width()) || (sb_msb < 0)) { NetEConst*tmp = make_const_x(wid); tmp->set_line(*this); return tmp; } NetExpr*ex = new NetEConst(verinum(sb_lsb)); NetESelect*ss = new NetESelect(net, ex, wid); ss->set_line(*this); return ss; } /* * Part select indexed up, i.e. net[ +: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, NetScope*, bool need_const) const { listprefix_indices; bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); if (!rc) return 0; NetExpr*base = calculate_up_do_base_(des, scope, need_const); // Use the part select width already calculated by test_width(). unsigned long wid = min_width_; // Handle the special case that the base is constant as // well. In this case it can be converted to a conventional // part select. if (NetEConst*base_c = dynamic_cast (base)) { NetExpr*ex; if (base_c->value().is_defined()) { long lsv = base_c->value().as_long(); long offset = 0; // Get the signal range. const vector&packed = net->sig()->packed_dims(); ivl_assert(*this, packed.size() == prefix_indices.size()+1); // We want the last range, which is where we work. const netrange_t&rng = packed.back(); if (rng.get_msb() < rng.get_lsb()) { offset = -wid + 1; } long rel_base = net->sig()->sb_to_idx(prefix_indices, lsv); // If the part select covers exactly the entire // vector, then do not bother with it. Return the // signal itself. if (rel_base == 0 && wid == net->vector_width()) { delete base; net->cast_signed(false); return net; } // Otherwise, make a part select that covers the right // range. ex = new NetEConst(verinum(rel_base + offset)); if (warn_ob_select) { if (rel_base < 0) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << lsv << "+:" << wid << "] is selecting before vector." << endl; } if (rel_base + wid > net->vector_width()) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << lsv << "+:" << wid << "] is selecting after vector." << endl; } } } else { // Return 'bx for an undefined base. ex = new NetEConst(verinum(verinum::Vx, wid, true)); ex->set_line(*this); delete base; if (warn_ob_select) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "['bx+:" << wid << "] is always outside vector." << endl; } return ex; } NetESelect*ss = new NetESelect(net, ex, wid); ss->set_line(*this); delete base; return ss; } ivl_assert(*this, prefix_indices.size()+1 == net->sig()->packed_dims().size()); // Convert the non-constant part select index expression into // an expression that returns a canonical base. base = normalize_variable_part_base(prefix_indices, base, net->sig(), wid, true); NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_UP); ss->set_line(*this); if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate part " << "select base="<< *base << ", wid="<< wid << endl; } return ss; } /* * Part select indexed down, i.e. net[ -: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, NetESignal*net, NetScope*, bool need_const) const { listprefix_indices; bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); if (!rc) return 0; NetExpr*base = calculate_up_do_base_(des, scope, need_const); // Use the part select width already calculated by test_width(). unsigned long wid = min_width_; // Handle the special case that the base is constant as // well. In this case it can be converted to a conventional // part select. if (NetEConst*base_c = dynamic_cast (base)) { NetExpr*ex; if (base_c->value().is_defined()) { long lsv = base_c->value().as_long(); // If the part select covers exactly the entire // vector, then do not bother with it. Return the // signal itself. if (net->sig()->sb_to_idx(prefix_indices,lsv) == (signed) (wid-1) && wid == net->vector_width()) { delete base; net->cast_signed(false); return net; } long offset = 0; if (net->msi() > net->lsi()) { offset = -wid + 1; } // Otherwise, make a part select that covers the right // range. ex = new NetEConst(verinum(net->sig()->sb_to_idx(prefix_indices,lsv) + offset)); if (warn_ob_select) { long rel_base = net->sig()->sb_to_idx(prefix_indices,lsv) + offset; if (rel_base < 0) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << lsv << "+:" << wid << "] is selecting before vector." << endl; } if (rel_base + wid > net->vector_width()) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "[" << lsv << "-:" << wid << "] is selecting after vector." << endl; } } } else { // Return 'bx for an undefined base. ex = new NetEConst(verinum(verinum::Vx, wid, true)); ex->set_line(*this); delete base; if (warn_ob_select) { cerr << get_fileline() << ": warning: " << net->name(); if (net->word_index()) cerr << "[]"; cerr << "['bx-:" << wid << "] is always outside vector." << endl; } return ex; } NetESelect*ss = new NetESelect(net, ex, wid); ss->set_line(*this); delete base; return ss; } base = normalize_variable_base(base, net->msi(), net->lsi(), wid, false); NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_DOWN); ss->set_line(*this); if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate part " << "select base="<< *base << ", wid="<< wid << endl; } return ss; } NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetESignal*net, NetScope*, bool need_const) const { listprefix_indices; bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); if (!rc) return 0; const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1, need_const); if (const netdarray_t*darray = net->sig()->darray_type()) { // Special case: This is a select of a dynamic // array. Generate a NetESelect and attach it to // the NetESignal. This should be interpreted as // an array word select downstream. if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Bit select of a dynamic array becomes NetESelect." << endl; } NetESelect*res = new NetESelect(net, mux, darray->element_width()); res->set_line(*net); return res; } // If the bit select is constant, then treat it similar // to the part select, so that I save the effort of // making a mux part in the netlist. if (NetEConst*msc = dynamic_cast (mux)) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr_net_bit_: " << "mux is constant=" << *msc << ", packed_dims()=" << net->sig()->packed_dims() << ", packed_dims().size()=" << net->sig()->packed_dims().size() << ", prefix_indices.size()=" << prefix_indices.size() << endl; } // Special case: The bit select expression is constant // x/z. The result of the expression is 1'bx. if (! msc->value().is_defined()) { if (warn_ob_select) { cerr << get_fileline() << ": warning: " "Constant bit select [" << msc->value() << "] is undefined for "; if (net->word_index()) cerr << "array word"; else cerr << "vector"; cerr << " '" << net->name(); if (net->word_index()) cerr << "[]"; cerr << "'." << endl; cerr << get_fileline() << ": : " << "Replacing select with a constant 1'bx." << endl; } // FIXME: Should I be using slice_width() here? NetEConst*tmp = make_const_x(1); tmp->set_line(*this); delete mux; return tmp; } long msv = msc->value().as_long(); const vector& sig_packed = net->sig()->packed_dims(); if (prefix_indices.size()+2 <= sig_packed.size()) { // Special case: this is a slice of a multi-dimensional // packed array. For example: // reg [3:0][7:0] x; // ... x[2] ... // This shows up as the prefix_indices being too short // for the packed dimensions of the vector. What we do // here is convert to a "slice" of the vector. unsigned long lwid; long idx; rc = net->sig()->sb_to_slice(prefix_indices, msv, idx, lwid); ivl_assert(*this, rc); // Make an expression out of the index NetEConst*idx_c = new NetEConst(verinum(idx)); idx_c->set_line(*net); NetESelect*res = new NetESelect(net, idx_c, lwid); res->set_line(*net); return res; } if (net->sig()->data_type()==IVL_VT_STRING && (msv < 0)) { // Special case: This is a constant bit select of // a string, and the index is < 0. For example: // string foo; // ... foo[-1] ... // This is known to be 8'h00. NetEConst*tmp = make_const_0(8); tmp->set_line(*this); delete mux; return tmp; } if (net->sig()->data_type()==IVL_VT_STRING) { // Special case: This is a select of a string // variable. Generate a NetESelect and attach it // to the NetESignal. This should be interpreted // as a character select downstream. if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Bit select of string becomes NetESelect." << endl; } NetESelect*res = new NetESelect(net, mux, 8); res->set_line(*net); return res; } long idx = net->sig()->sb_to_idx(prefix_indices,msv); if (idx >= (long)net->vector_width() || idx < 0) { /* The bit select is out of range of the vector. This is legal, but returns a constant 1'bx value. */ if (warn_ob_select) { cerr << get_fileline() << ": warning: " "Constant bit select [" << msv << "] is "; if (idx < 0) cerr << "before "; else cerr << "after "; if (net->word_index()) cerr << "array word "; else cerr << "vector "; cerr << net->name(); if (net->word_index()) cerr << "[]"; cerr << net->sig()->packed_dims() << "." << endl; cerr << get_fileline() << ": : " << "Replacing select with a constant 1'bx." << endl; } NetEConst*tmp = make_const_x(1); tmp->set_line(*this); delete mux; return tmp; } // If the vector is only one bit, we are done. The // bit select will return the scalar itself. if (net->vector_width() == 1) return net; if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr_net_bit_: " << "Make bit select idx=" << idx << endl; } // Make an expression out of the index NetEConst*idx_c = new NetEConst(verinum(idx)); idx_c->set_line(*net); // Make a bit select with the canonical index NetESelect*res = new NetESelect(net, idx_c, 1); res->set_line(*net); return res; } const vector& sig_packed = net->sig()->packed_dims(); if (prefix_indices.size()+2 <= sig_packed.size()) { // Special case: this is a slice of a multi-dimensional // packed array. For example: // reg [3:0][7:0] x; // x[2] = ... // This shows up as the prefix_indices being too short // for the packed dimensions of the vector. What we do // here is convert to a "slice" of the vector. unsigned long lwid; mux = normalize_variable_slice_base(prefix_indices, mux, net->sig(), lwid); mux->set_line(*net); // Make a PART select with the canonical index NetESelect*res = new NetESelect(net, mux, lwid); res->set_line(*net); return res; } if (net->sig()->data_type() == IVL_VT_STRING) { // Special case: This is a select of a string. // This should be interpreted as a byte select. if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Bit select of a string becomes NetESelect." << endl; } NetESelect*res = new NetESelect(net, mux, 8); res->set_line(*net); return res; } // Non-constant bit select? punt and make a subsignal // device to mux the bit in the net. This is a fairly // complicated task because we need to generate // expressions to convert calculated bit select // values to canonical values that are used internally. mux = normalize_variable_bit_base(prefix_indices, mux, net->sig()); NetESelect*ss = new NetESelect(net, mux, 1); ss->set_line(*this); return ss; } NetExpr* PEIdent::elaborate_expr_net_bit_last_(Design*, NetScope*, NetESignal*net, NetScope* /* found_in */, bool need_const) const { if (need_const) { cerr << get_fileline() << ": error: " << "Expression with \"[$]\" is not constant." << endl; return 0; } unsigned use_width = 1; if (const netdarray_t*darray = net->sig()->darray_type()) { use_width = darray->element_width(); } NetELast*mux = new NetELast(net->sig()); mux->set_line(*this); NetESelect*ss = new NetESelect(net, mux, use_width); ss->set_line(*this); return ss; } NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, unsigned expr_wid, unsigned flags) const { if (net->unpacked_dimensions() > 0) return elaborate_expr_net_word_(des, scope, net, found_in, expr_wid, flags); bool need_const = NEED_CONST & flags; NetESignal*node = new NetESignal(net); node->set_line(*this); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (! path_.back().index.empty()) use_sel = path_.back().index.back().sel; if (net->get_scalar() && use_sel != index_component_t::SEL_NONE) { cerr << get_fileline() << ": error: can not select part of "; if (node->expr_type() == IVL_VT_REAL) cerr << "real: "; else cerr << "scalar: "; cerr << net->name() << endl; des->errors += 1; return 0; } list prefix_indices; bool rc = evaluate_index_prefix(des, scope, prefix_indices, path_.back().index); if (!rc) return 0; // If this is a part select of a signal, then make a new // temporary signal that is connected to just the // selected bits. The lsb_ and msb_ expressions are from // the foo[msb:lsb] expression in the original. if (use_sel == index_component_t::SEL_PART) return elaborate_expr_net_part_(des, scope, node, found_in, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_net_idx_up_(des, scope, node, found_in, need_const); if (use_sel == index_component_t::SEL_IDX_DO) return elaborate_expr_net_idx_do_(des, scope, node, found_in, need_const); if (use_sel == index_component_t::SEL_BIT) return elaborate_expr_net_bit_(des, scope, node, found_in, need_const); if (use_sel == index_component_t::SEL_BIT_LAST) return elaborate_expr_net_bit_last_(des, scope, node, found_in, need_const); // It's not anything else, so this must be a simple identifier // expression with no part or bit select. Return the signal // itself as the expression. assert(use_sel == index_component_t::SEL_NONE); return node; } unsigned PENewArray::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_DARRAY; expr_width_ = 1; min_width_ = 1; signed_flag_= false; return 1; } NetExpr* PENewArray::elaborate_expr(Design*des, NetScope*scope, ivl_type_t ntype, unsigned flags) const { // Elaborate the size expression. width_mode_t mode = LOSSLESS; unsigned use_wid = size_->test_width(des, scope, mode); NetExpr*size = size_->elaborate_expr(des, scope, use_wid, flags); NetExpr*init_val = 0; if (dynamic_cast (init_)) { // Special case: the initial value expression is an // array_pattern. Elaborate the expression like the // r-value to an assignment to array. init_val = init_->elaborate_expr(des, scope, ntype, flags); } else if (init_) { // Regular case: The initial value is an // expression. Elaborate the expression as an element // type. The run-time will assign this value to each element. const netarray_t*array_type = dynamic_cast (ntype); ivl_type_t elem_type = array_type->element_type(); init_val = init_->elaborate_expr(des, scope, elem_type, flags); } NetENew*tmp = new NetENew(ntype, size, init_val); tmp->set_line(*this); return tmp; } /* * This method should never actually be called. */ NetExpr* PENewArray::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { ivl_assert(*this, 0); return 0; } unsigned PENewClass::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_CLASS; expr_width_ = 1; min_width_ = 1; signed_flag_= false; return 1; } /* * This elaborates the constructor for a class. This arranges for the * call of class constructor, if present, and also * initializers in front of an explicit constructor. * * The derived argument is the type of the class derived from the * current one. This is used to get chained constructor arguments, if necessary. */ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope, const netclass_t*ctype, NetExpr*obj, unsigned /*flags*/) const { ivl_assert(*this, ctype); // If there is an initializer function, then pass the object // through that function first. Note that the initializer // function has no arguments other than the object itself. if (NetScope*new1_scope = ctype->method_from_name(perm_string::literal("new@"))) { NetFuncDef*def1 = new1_scope->func_def(); ivl_assert(*this, def1); ivl_assert(*this, def1->port_count()==1); vector parms1 (1); parms1[0] = obj; // The return value of the initializer is the "this" // variable, instead of the "new&" scope name. NetNet*res1 = new1_scope->find_signal(perm_string::literal("@")); ivl_assert(*this, res1); NetESignal*eres = new NetESignal(res1); NetEUFunc*tmp = new NetEUFunc(scope, new1_scope, eres, parms1, true); tmp->set_line(*this); obj = tmp; } NetScope*new_scope = ctype->method_from_name(perm_string::literal("new")); if (new_scope == 0) { // No constructor. if (parms_.size() > 0) { cerr << get_fileline() << ": error: " << "Class " << ctype->get_name() << " has no constructor, but you passed " << parms_.size() << " arguments to the new operator." << endl; des->errors += 1; } return obj; } NetFuncDef*def = new_scope->func_def(); if (def == 0) { cerr << get_fileline() << ": internal error: " << "Scope " << scope_path(new_scope) << " is missing constructor definition." << endl; des->errors += 1; } ivl_assert(*this, def); // Are there too many arguments passed to the function. If so, // generate an error message. The case of too few arguments // will be handled below, when we run out of arguments. if ((parms_.size()+1) > def->port_count()) { cerr << get_fileline() << ": error: Parm count mismatch" << " passing " << parms_.size() << " arguments " << " to constructor expecting " << (def->port_count()-1) << " arguments." << endl; des->errors += 1; } vector parms (def->port_count()); parms[0] = obj; int missing_parms = 0; int parm_errors = 0; for (size_t idx = 1 ; idx < parms.size() ; idx += 1) { // While there are default arguments, check them. if (idx <= parms_.size() && parms_[idx-1]) { PExpr*tmp = parms_[idx-1]; parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->net_type(), def->port(idx)->data_type(), def->port(idx)->vector_width(), tmp, false); if (parms[idx] == 0) parm_errors += 1; continue; } // Ran out of explicit arguments. Is there a default // argument we can use? if (NetExpr*tmp = def->port_defe(idx)) { parms[idx] = tmp; continue; } // If we run out of passed expressions, and there is no // default value for this port, then we will need to // report an error that we are missing parameters. missing_parms += 1; parms[idx] = 0; } if (missing_parms > 0) { cerr << get_fileline() << ": error: The " << scope_path(new_scope) << " constructor call is missing arguments." << endl; parm_errors += 1; des->errors += 1; } // The return value for the constructor is actually the "this" // variable, instead of the "new" scope name. NetNet*res = new_scope->find_signal(perm_string::literal("@")); ivl_assert(*this, res); NetESignal*eres = new NetESignal(res); NetEUFunc*con = new NetEUFunc(scope, new_scope, eres, parms, true); con->set_line(*this); return con; } NetExpr* PENewClass::elaborate_expr(Design*des, NetScope*scope, ivl_type_t ntype, unsigned flags) const { NetExpr*obj = new NetENew(ntype); obj->set_line(*this); // Find the constructor for the class. If there is no // constructor then the result of this expression is the // allocation alone. const netclass_t*ctype = dynamic_cast (ntype); obj = elaborate_expr_constructor_(des, scope, ctype, obj, flags); return obj; } unsigned PENewCopy::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_CLASS; expr_width_ = 1; min_width_ = 1; signed_flag_= false; return 1; } NetExpr* PENewCopy::elaborate_expr(Design*des, NetScope*scope, ivl_type_t obj_type, unsigned) const { NetExpr*copy_arg = src_->elaborate_expr(des, scope, obj_type, 0); if (copy_arg == 0) return 0; NetENew*obj_new = new NetENew(obj_type); obj_new->set_line(*this); NetEShallowCopy*copy = new NetEShallowCopy(obj_new, copy_arg); copy->set_line(*this); return copy; } /* * A "null" expression represents class objects/handles. This brings * up a ton of special cases, but we handle it here by setting the * expr_type_ and expr_width_ to fixed values. */ unsigned PENull::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_CLASS; expr_width_ = 1; min_width_ = 1; signed_flag_ = false; return expr_width_; } NetExpr* PENull::elaborate_expr(Design*, NetScope*, ivl_type_t, unsigned) const { NetENull*tmp = new NetENull; tmp->set_line(*this); return tmp; } NetExpr* PENull::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { NetENull*tmp = new NetENull; tmp->set_line(*this); return tmp; } unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) { expr_type_ = IVL_VT_LOGIC; expr_width_ = value_->len(); min_width_ = expr_width_; signed_flag_ = value_->has_sign(); if (!value_->has_len() && !value_->is_single()) { if (gn_strict_expr_width_flag) { expr_width_ = integer_width; mode = UNSIZED; } else if (mode < LOSSLESS) { if (expr_width_ < integer_width) { expr_width_ = integer_width; if (mode < UNSIZED) mode = UNSIZED; } else { mode = LOSSLESS; } } } if (debug_elaborate) { cerr << get_fileline() << ": PENumber::test_width: " << "Value=" << *value_ << ", width=" << expr_width_ << ", output mode=" << width_mode_name(mode) << endl; } return expr_width_; } NetExpr* PENumber::elaborate_expr(Design*des, NetScope*, ivl_type_t ntype, unsigned) const { const netvector_t*use_type = dynamic_cast (ntype); if (use_type == 0) { cerr << get_fileline() << ": internal error: " << "I don't know how cast numbers to this type." << endl; des->errors += 1; return 0; } // Special case: If the context type is REAL, then cast the // vector value to a real and return a NetECReal. if (ntype->base_type() == IVL_VT_REAL) { verireal val (value_->as_long()); NetECReal*tmp = new NetECReal(val); tmp->set_line(*this); return tmp; } verinum use_val = value(); use_val .has_sign( use_type->get_signed() ); use_val = cast_to_width(use_val, use_type->packed_width()); NetEConst*tmp = new NetEConst(use_val); tmp->set_line(*this); return tmp; } NetEConst* PENumber::elaborate_expr(Design*, NetScope*, unsigned expr_wid, unsigned) const { assert(value_); verinum val = *value_; if (val.has_len()) val.has_sign(signed_flag_); val = cast_to_width(val, expr_wid); NetEConst*tmp = new NetEConst(val); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_BOOL; expr_width_ = text_? verinum(text_).len() : 0; min_width_ = expr_width_; signed_flag_ = false; return expr_width_; } NetEConst* PEString::elaborate_expr(Design*, NetScope*, ivl_type_t, unsigned)const { verinum val(value()); NetEConst*tmp = new NetEConst(val); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } NetEConst* PEString::elaborate_expr(Design*, NetScope*, unsigned expr_wid, unsigned) const { verinum val(value()); val = pad_to_width(val, expr_wid); NetEConst*tmp = new NetEConst(val); tmp->cast_signed(signed_flag_); tmp->set_line(*this); return tmp; } unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { // The condition of the ternary is self-determined, so // we will test its width when we elaborate it. // Test the width of the true and false clauses. unsigned tru_width = tru_->test_width(des, scope, mode); width_mode_t saved_mode = mode; unsigned fal_width = fal_->test_width(des, scope, mode); // If the width mode changed, retest the true clause, as it // may choose a different width if it is in a lossless context. if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) { tru_width = tru_->test_width(des, scope, mode); } // If either of the alternatives is IVL_VT_REAL, then the // expression as a whole is IVL_VT_REAL. Otherwise, if either // of the alternatives is IVL_VT_LOGIC, then the expression as // a whole is IVL_VT_LOGIC. The fallback assumes that the // types are the same and we take that. ivl_variable_type_t tru_type = tru_->expr_type(); ivl_variable_type_t fal_type = fal_->expr_type(); if (tru_type == IVL_VT_REAL || fal_type == IVL_VT_REAL) { expr_type_ = IVL_VT_REAL; } else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) { expr_type_ = IVL_VT_LOGIC; } else { ivl_assert(*this, tru_type == fal_type); expr_type_ = tru_type; } if (expr_type_ == IVL_VT_REAL) { expr_width_ = 1; min_width_ = 1; signed_flag_ = true; } else { expr_width_ = max(tru_width, fal_width); min_width_ = max(tru_->min_width(), fal_->min_width()); signed_flag_ = tru_->has_sign() && fal_->has_sign(); // If the alternatives are different types, the expression // is forced to unsigned. In this case the lossless width // calculation is unreliable and we need to make sure the // final expression width is at least integer_width. if ((mode == LOSSLESS) && (tru_->has_sign() != fal_->has_sign())) mode = UPSIZE; } if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Ternary expression type=" << expr_type_ << ", width=" << expr_width_ << " (tru_type=" << tru_type << ", fal_type=" << fal_type << ")" << endl; return fix_width_(mode); } bool NetETernary::test_operand_compat(ivl_variable_type_t l, ivl_variable_type_t r) { if (l == IVL_VT_LOGIC && r == IVL_VT_BOOL) return true; if (l == IVL_VT_BOOL && r == IVL_VT_LOGIC) return true; if (l == IVL_VT_REAL && (r == IVL_VT_LOGIC || r == IVL_VT_BOOL)) return true; if (r == IVL_VT_REAL && (l == IVL_VT_LOGIC || l == IVL_VT_BOOL)) return true; if (l == r) return true; return false; } /* * Elaborate the Ternary operator. I know that the expressions were * parsed so I can presume that they exist, and call elaboration * methods. If any elaboration fails, then give up and return 0. */ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag ivl_assert(*this, expr_); ivl_assert(*this, tru_); ivl_assert(*this, fal_); // Elaborate and evaluate the condition expression. Note that // it is always self-determined. NetExpr*con = elab_and_eval(des, scope, expr_, -1, NEED_CONST & flags); if (con == 0) return 0; /* Make sure the condition expression reduces to a single bit. */ con = condition_reduce(con); // Verilog doesn't say that we must do short circuit // evaluation of ternary expressions, but it doesn't disallow // it. The disadvantage of doing this is that semantic errors // in the unused clause will be missed, but people don't seem // to mind, and do appreciate the optimization available here. if (NetEConst*tmp = dynamic_cast (con)) { verinum cval = tmp->value(); ivl_assert(*this, cval.len()==1); // Condition is constant TRUE, so we only need the true clause. if (cval.get(0) == verinum::V1) { if (debug_elaborate) cerr << get_fileline() << ": debug: Short-circuit " "elaborate TRUE clause of ternary." << endl; return elab_and_eval_alternative_(des, scope, tru_, expr_wid, flags, true); } // Condition is constant FALSE, so we only need the // false clause. if (cval.get(0) == verinum::V0) { if (debug_elaborate) cerr << get_fileline() << ": debug: Short-circuit " "elaborate FALSE clause of ternary." << endl; return elab_and_eval_alternative_(des, scope, fal_, expr_wid, flags, true); } // X and Z conditions need to blend both results, so we // can't short-circuit. } NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid, flags, false); if (tru == 0) { delete con; return 0; } NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid, flags, false); if (fal == 0) { delete con; delete tru; return 0; } if (! NetETernary::test_operand_compat(tru->expr_type(), fal->expr_type())) { cerr << get_fileline() << ": error: Data types " << tru->expr_type() << " and " << fal->expr_type() << " of ternary" << " do not match." << endl; des->errors += 1; return 0; } NetETernary*res = new NetETernary(con, tru, fal, expr_wid, signed_flag_); res->set_line(*this); return res; } /* * When elaborating the true or false alternative expression of a * ternary, take into account the overall expression type. If the type * is not vectorable, then the alternative expression is evaluated as * self-determined. */ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, PExpr*expr, unsigned expr_wid, unsigned flags, bool short_cct) const { int context_wid = expr_wid; if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) { expr_wid = expr->expr_width(); context_wid = -1; } else { expr->cast_signed(signed_flag_); } NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, flags); if (tmp == 0) return 0; if (short_cct && (expr_type_ == IVL_VT_REAL) && (expr->expr_type() != IVL_VT_REAL)) tmp = cast_to_real(tmp); eval_expr(tmp, context_wid); return tmp; } /* * A typename expression is only legal in very narrow cases. This is * just a placeholder. */ unsigned PETypename::test_width(Design*des, NetScope*, width_mode_t&) { cerr << get_fileline() << ": error: " << "Type names are not valid expressions here." << endl; des->errors += 1; expr_type_ = IVL_VT_NO_TYPE; expr_width_ = 1; min_width_ = 1; signed_flag_ = false; return expr_width_; } NetExpr*PETypename::elaborate_expr(Design*des, NetScope*, ivl_type_t, unsigned) const { cerr << get_fileline() << ": error: Type name not a valid expression here." << endl; des->errors += 1; return 0; } unsigned PEUnary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { switch (op_) { case '&': // Reduction AND case '|': // Reduction OR case '^': // Reduction XOR case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) case '!': { width_mode_t sub_mode = SIZED; unsigned sub_width = expr_->test_width(des, scope, sub_mode); expr_type_ = expr_->expr_type(); expr_width_ = 1; min_width_ = 1; signed_flag_ = false; if ((op_ == '!') && (expr_type_ != IVL_VT_BOOL)) expr_type_ = IVL_VT_LOGIC; if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Test width of sub-expression of " << op_ << " returns " << sub_width << "." << endl; } return expr_width_; } expr_width_ = expr_->test_width(des, scope, mode); expr_type_ = expr_->expr_type(); min_width_ = expr_->min_width(); signed_flag_ = expr_->has_sign(); return fix_width_(mode); } NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag ivl_variable_type_t t; unsigned sub_width = expr_wid; switch (op_) { // Reduction operators and ! always have a self determined width. case '!': case '&': // Reduction AND case '|': // Reduction OR case '^': // Reduction XOR case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) sub_width = expr_->expr_width(); break; // Other operators have context determined operands, so propagate // the expression type (signed/unsigned) down to the operands. default: expr_->cast_signed(signed_flag_); break; } NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, flags); if (ip == 0) return 0; ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE); NetExpr*tmp; switch (op_) { case 'i': case 'I': case 'D': case 'd': t = ip->expr_type(); if (expr_wid != expr_->expr_width()) { /* * TODO: Need to modify draw_unary_expr() to support * increment/decrement operations on slice of vector. */ cerr << get_fileline() << ": sorry: " << human_readable_op(op_, true) << " operation is not yet supported on " << "vector slice." << endl; des->errors += 1; return 0; } else if (t == IVL_VT_LOGIC || t == IVL_VT_BOOL || t == IVL_VT_REAL) { if (dynamic_cast (ip) || dynamic_cast (ip)) { /* * invalid operand: operand is a constant * or real number */ cerr << get_fileline() << ": error: " << "inappropriate use of " << human_readable_op(op_, true) << " operator." << endl; des->errors += 1; return 0; } /* * **** Valid use of operator *** * For REAL variables draw_unary_real() is invoked during * evaluation and for LOGIC/BOOLEAN draw_unary_expr() * is called for evaluation. */ tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); } else { cerr << get_fileline() << ": error: " << "inappropriate use of " << human_readable_op(op_, true) << " operator." << endl; des->errors += 1; return 0; } break; default: tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); break; case '-': if (NetEConst*ipc = dynamic_cast(ip)) { verinum val = - ipc->value(); tmp = new NetEConst(val); tmp->cast_signed(signed_flag_); tmp->set_line(*this); delete ip; } else if (NetECReal*ipr = dynamic_cast(ip)) { /* When taking the - of a real, fold this into the constant value. */ verireal val = - ipr->value(); tmp = new NetECReal(val); tmp->set_line(*this); delete ip; } else { tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); } break; case '+': tmp = ip; break; case '!': // Logical NOT /* If the operand to unary ! is a constant, then I can evaluate this expression here and return a logical constant in its place. */ if (NetEConst*ipc = dynamic_cast(ip)) { verinum val = ipc->value(); unsigned v1 = 0; unsigned vx = 0; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) switch (val[idx]) { case verinum::V0: break; case verinum::V1: v1 += 1; break; default: vx += 1; break; } verinum::V res; if (v1 > 0) res = verinum::V0; else if (vx > 0) res = verinum::Vx; else res = verinum::V1; verinum vres (res, 1, true); tmp = new NetEConst(vres); tmp->set_line(*this); delete ip; } else if (NetECReal*ipr = dynamic_cast(ip)) { verinum::V res; if (ipr->value().as_double() == 0.0) res = verinum::V1; else res = verinum::V0; verinum vres (res, 1, true); tmp = new NetEConst(vres); tmp->set_line(*this); delete ip; } else { if (ip->expr_type() == IVL_VT_REAL) { tmp = new NetEBComp('e', ip, new NetECReal(verireal(0.0))); } else { tmp = new NetEUReduce(op_, ip); } tmp->set_line(*this); } tmp = pad_to_width(tmp, expr_wid, *this); break; case '&': // Reduction AND case '|': // Reduction OR case '^': // Reduction XOR case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) if (ip->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " << human_readable_op(op_, true) << " operator may not have a REAL operand." << endl; des->errors += 1; return 0; } tmp = new NetEUReduce(op_, ip); tmp->set_line(*this); tmp = pad_to_width(tmp, expr_wid, *this); break; case '~': tmp = elaborate_expr_bits_(ip, expr_wid); break; } return tmp; } NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const { // Handle the special case that the operand is a // constant. Simply calculate the constant results of the // expression and return that. if (NetEConst*ctmp = dynamic_cast (operand)) { verinum value = ctmp->value(); // The only operand that I know can get here is the // unary not (~). ivl_assert(*this, op_ == '~'); value = ~value; ctmp = new NetEConst(value); ctmp->set_line(*this); delete operand; return ctmp; } NetEUBits*tmp = new NetEUBits(op_, operand, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { return 0; } NetNet* Design::find_discipline_reference(ivl_discipline_t dis, NetScope*scope) { NetNet*gnd = discipline_references_[dis->name()]; if (gnd) return gnd; string name = string(dis->name()) + "$gnd"; netvector_t*gnd_vec = new netvector_t(IVL_VT_REAL,0,0); gnd = new NetNet(scope, lex_strings.make(name), NetNet::WIRE, gnd_vec); gnd->set_discipline(dis); discipline_references_[dis->name()] = gnd; if (debug_elaborate) cerr << gnd->get_fileline() << ": debug: " << "Create an implicit reference terminal" << " for discipline=" << dis->name() << " in scope=" << scope_path(scope) << endl; return gnd; } iverilog-10_1/elab_lval.cc000066400000000000000000001257201265551621300156010ustar00rootroot00000000000000/* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PExpr.h" # include "PPackage.h" # include "netlist.h" # include "netmisc.h" # include "netstruct.h" # include "netclass.h" # include "netdarray.h" # include "netparray.h" # include "netvector.h" # include "compiler.h" # include # include # include # include "ivl_assert.h" /* * These methods generate a NetAssign_ object for the l-value of the * assignment. This is common code for the = and <= statements. * * What gets generated depends on the structure of the l-value. If the * l-value is a simple name (i.e., foo <= ) then the NetAssign_ * is created the width of the foo reg and connected to all the * bits. * * If there is a part select (i.e., foo[3:1] <= ) the NetAssign_ * is made only as wide as it needs to be (3 bits in this example) and * connected to the correct bits of foo. A constant bit select is a * special case of the part select. * * If the bit-select is non-constant (i.e., foo[] = ) the * NetAssign_ is made wide enough to connect to all the bits of foo, * then the mux expression is elaborated and attached to the * NetAssign_ node as a b_mux value. The target must interpret the * presence of a bmux value as taking a single bit and assigning it to * the bit selected by the bmux expression. * * If the l-value expression is non-trivial, but can be fully * evaluated at compile time (meaning any bit selects are constant) * then elaboration will make a single NetAssign_ that connects to a * synthetic reg that in turn connects to all the proper pins of the * l-value. * * This last case can turn up in statements like: {a, b[1]} = c; * rather than create a NetAssign_ for each item in the concatenation, * elaboration makes a single NetAssign_ and connects it up properly. */ /* * The default interpretation of an l-value to a procedural assignment * is to try to make a net elaboration, and see if the result is * suitable for assignment. */ NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const { NetNet*ll = 0; if (ll == 0) { cerr << get_fileline() << ": Assignment l-value too complex." << endl; return 0; } NetAssign_*lv = new NetAssign_(ll); return lv; } /* * Concatenation expressions can appear as l-values. Handle them here. * * If adjacent l-values in the concatenation are not bit selects, then * merge them into a single NetAssign_ object. This can happen is code * like ``{ ...a, b, ...}''. As long as "a" and "b" do not have bit * selects (or the bit selects are constant) we can merge the * NetAssign_ objects. * * Be careful to get the bit order right. In the expression ``{a, b}'' * a is the MSB and b the LSB. Connect the LSB to the low pins of the * NetAssign_ object. */ NetAssign_* PEConcat::elaborate_lval(Design*des, NetScope*scope, bool is_cassign, bool is_force) const { if (repeat_) { cerr << get_fileline() << ": error: Repeat concatenations make " "no sense in l-value expressions. I refuse." << endl; des->errors += 1; return 0; } NetAssign_*res = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == 0) { cerr << get_fileline() << ": error: Empty expressions " << "not allowed in concatenations." << endl; des->errors += 1; continue; } NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope, is_cassign, is_force); /* If the l-value doesn't elaborate, the error was already detected and printed. We just skip it and let the compiler catch more errors. */ if (tmp == 0) continue; if (tmp->expr_type() == IVL_VT_REAL) { cerr << parms_[idx]->get_fileline() << ": error: " << "concatenation operand can not be real: " << *parms_[idx] << endl; des->errors += 1; continue; } /* Link the new l-value to the previous one. */ NetAssign_*last = tmp; while (last->more) last = last->more; last->more = res; res = tmp; } return res; } NetAssign_*PEIdent::scan_lname_for_nested_members_(Design*des, NetScope*scope, const pform_name_t&cur_path) const { if (cur_path.size() == 1) return 0; pform_name_t use_path = cur_path; name_component_t tail = use_path.back(); use_path.pop_back(); NetNet* reg = 0; const NetExpr*par = 0; NetEvent* eve = 0; symbol_search(this, des, scope, use_path, reg, par, eve); if (reg == 0) { NetAssign_*tmp = scan_lname_for_nested_members_(des, scope, use_path); if (tmp == 0) return 0; tmp = new NetAssign_(tmp); tmp->set_property(tail.name); return tmp; } if (reg->struct_type() && reg->struct_type()->packed()) { NetAssign_*tmp = new NetAssign_(reg); elaborate_lval_net_packed_member_(des, scope, tmp, tail); return tmp; } #if 0 if (reg->struct_type() && reg->struct_type()->packed()) { cerr << get_fileline() << ": sorry: " << "I don't know what to do with packed struct " << use_path << " with member " << tail << "." << endl; return 0; } #endif if (reg->struct_type() && !reg->struct_type()->packed()) { cerr << get_fileline() << ": sorry: " << "I don't know what to do with unpacked struct " << use_path << " with member " << tail << "." << endl; return 0; } if (reg->class_type()) { return elaborate_lval_net_class_member_(des, scope, reg, tail.name); } return 0; } /* * Handle the ident as an l-value. This includes bit and part selects * of that ident. */ NetAssign_* PEIdent::elaborate_lval(Design*des, NetScope*scope, bool is_cassign, bool is_force) const { NetNet* reg = 0; const NetExpr*par = 0; NetEvent* eve = 0; perm_string method_name; if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval: " << "Elaborate l-value ident expression: " << *this << endl; } /* Try to detect the special case that we are in a method and the identifier is a member of the class. */ if (NetAssign_*tmp = elaborate_lval_method_class_member_(des, scope)) return tmp; /* Normally find the name in the passed scope. But if this is imported from a package, then located the variable from the package scope. */ NetScope*use_scope = scope; if (package_) { use_scope = des->find_package(package_->pscope_name()); ivl_assert(*this, use_scope); } symbol_search(this, des, use_scope, path_, reg, par, eve); /* If the signal is not found, check to see if this is a member of a struct. Take the name of the form "a.b.member", remove the member and store it into method_name, and retry the search with "a.b". */ if (reg == 0 && path_.size() >= 2) { pform_name_t use_path = path_; perm_string tmp_name = peek_tail_name(use_path); use_path.pop_back(); symbol_search(this, des, use_scope, use_path, reg, par, eve); if (reg && reg->struct_type()) { method_name = tmp_name; } else if (reg && reg->class_type()) { method_name = tmp_name; } else if (NetAssign_*subl = scan_lname_for_nested_members_(des, use_scope, path_)) { return subl; } else { reg = 0; } } if (reg == 0) { if (use_scope->type()==NetScope::FUNC && use_scope->func_def()->return_sig()==0 && use_scope->basename()==peek_tail_name(path_)) { cerr << get_fileline() << ": error: " << "Cannot assign to " << path_ << " because function " << scope_path(use_scope) << " is void." << endl; } else { cerr << get_fileline() << ": error: Could not find variable ``" << path_ << "'' in ``" << scope_path(use_scope) << "''" << endl; } des->errors += 1; return 0; } ivl_assert(*this, reg); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval: " << "Found l-value as reg." << " unpacked_dimensions()=" << reg->unpacked_dimensions() << endl; } // We are processing the tail of a string of names. For // example, the verilog may be "a.b.c", so we are processing // "c" at this point. (Note that if method_name is not nil, // then this is "a.b.c.method" and "a.b.c" is a struct or class.) const name_component_t&name_tail = path_.back(); // Use the last index to determine what kind of select // (bit/part/etc) we are processing. For example, the verilog // may be "a.b.c[1][2][]". All but the last index must // be simple expressions, only the may be a part // select etc., so look at it to determine how we will be // proceeding. index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) use_sel = name_tail.index.back().sel; // Special case: The l-value is an entire memory, or array // slice. This is, in fact, an error in l-values. Detect the // situation by noting if the index count is less than the // array dimensions (unpacked). if (reg->unpacked_dimensions() > name_tail.index.size()) { cerr << get_fileline() << ": error: Cannot assign to array " << path_ << ". Did you forget a word index?" << endl; des->errors += 1; return 0; } /* Get the signal referenced by the identifier, and make sure it is a register. Wires are not allowed in this context, unless this is the l-value of a force. */ if ((reg->type() != NetNet::REG) && (reg->type() != NetNet::UNRESOLVED_WIRE) && !is_force) { cerr << get_fileline() << ": error: " << path_ << " is not a valid l-value in " << scope_path(use_scope) << "." << endl; cerr << reg->get_fileline() << ": : " << path_ << " is declared here as " << reg->type() << "." << endl; des->errors += 1; return 0; } if (reg->struct_type() && !method_name.nil()) { NetAssign_*lv = new NetAssign_(reg); name_component_t tmp_name (method_name); elaborate_lval_net_packed_member_(des, use_scope, lv, tmp_name); return lv; } if (reg->class_type() && !method_name.nil() && gn_system_verilog()) { NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, method_name); return lv; } // Past this point, we should have taken care of the cases // where the name is a member/method of a struct/class. ivl_assert(*this, method_name.nil()); bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE); if (reg->unpacked_dimensions() > 0) return elaborate_lval_net_word_(des, scope, reg, need_const_idx); // This must be after the array word elaboration above! if (reg->get_scalar() && use_sel != index_component_t::SEL_NONE) { cerr << get_fileline() << ": error: can not select part of "; if (reg->data_type() == IVL_VT_REAL) cerr << "real: "; else cerr << "scalar: "; cerr << reg->name() << endl; des->errors += 1; return 0; } if (use_sel == index_component_t::SEL_PART) { NetAssign_*lv = new NetAssign_(reg); elaborate_lval_net_part_(des, scope, lv); return lv; } if (use_sel == index_component_t::SEL_IDX_UP || use_sel == index_component_t::SEL_IDX_DO) { NetAssign_*lv = new NetAssign_(reg); elaborate_lval_net_idx_(des, scope, lv, use_sel, need_const_idx); return lv; } if (use_sel == index_component_t::SEL_BIT) { if (reg->darray_type()) { NetAssign_*lv = new NetAssign_(reg); elaborate_lval_darray_bit_(des, scope, lv); return lv; } else { NetAssign_*lv = new NetAssign_(reg); elaborate_lval_net_bit_(des, scope, lv, need_const_idx); return lv; } } ivl_assert(*this, use_sel == index_component_t::SEL_NONE); if (reg->type()==NetNet::UNRESOLVED_WIRE && !is_force) { cerr << get_fileline() << ": error: " << path_ << " Unable to assign to unresolved wires." << endl; des->errors += 1; return 0; } /* No select expressions. */ NetAssign_*lv = new NetAssign_(reg); return lv; } NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des, NetScope*scope) const { if (!gn_system_verilog()) return 0; if (scope->parent() == 0) return 0; if (path_.size() != 1) return 0; const netclass_t*class_type = find_class_containing_scope(*this, scope); if (class_type == 0) return 0; const name_component_t&name_comp = path_.back(); perm_string member_name = name_comp.name; int pidx = class_type->property_idx_from_name(member_name); if (pidx < 0) return 0; NetScope*scope_method = find_method_containing_scope(*this, scope); ivl_assert(*this, scope_method); NetNet*this_net = scope_method->find_signal(perm_string::literal("@")); if (this_net == 0) { cerr << get_fileline() << ": internal error: " << "Unable to find 'this' port of " << scope_path(scope_method) << "." << endl; return 0; } if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " << "Ident " << member_name << " is a property of class " << class_type->get_name() << endl; } NetExpr*canon_index = 0; if (! name_comp.index.empty()) { ivl_type_t property_type = class_type->get_prop_type(pidx); if (const netsarray_t* stype = dynamic_cast (property_type)) { canon_index = make_canonical_index(des, scope, this, name_comp.index, stype, false); } else { cerr << get_fileline() << ": error: " << "Index expressions don't apply to this type of property." << endl; des->errors += 1; } } // Detect assignment to constant properties. Note that the // initializer constructor MAY assign to constant properties, // as this is how the property gets its value. property_qualifier_t qual = class_type->get_prop_qual(pidx); if (qual.test_const()) { if (class_type->get_prop_initialized(pidx)) { cerr << get_fileline() << ": error: " << "Property " << class_type->get_prop_name(pidx) << " is constant in this method." << " (scope=" << scope_path(scope) << ")" << endl; des->errors += 1; } else if (scope->basename()!="new" && scope->basename()!="new@") { cerr << get_fileline() << ": error: " << "Property " << class_type->get_prop_name(pidx) << " is constant in this method." << " (scope=" << scope_path(scope) << ")" << endl; des->errors += 1; } else { // Mark this property as initialized. This is used // to know that we have initialized the constant // object so the next assignment will be marked as // illegal. class_type->set_prop_initialized(pidx); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " << "Found initializers for property " << class_type->get_prop_name(pidx) << endl; } } } ivl_type_t tmp_type = class_type->get_prop_type(pidx); if (const netuarray_t*tmp_ua = dynamic_cast(tmp_type)) { const std::vector&dims = tmp_ua->static_dimensions(); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " << "Property " << class_type->get_prop_name(pidx) << " has " << dims.size() << " dimensions, " << " got " << name_comp.index.size() << " indices." << endl; if (canon_index) { cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " << "Canonical index is:" << *canon_index << endl; }; } if (dims.size() != name_comp.index.size()) { cerr << get_fileline() << ": error: " << "Got " << name_comp.index.size() << " indices, " << "expecting " << dims.size() << " to index the property " << class_type->get_prop_name(pidx) << "." << endl; des->errors += 1; } } NetAssign_*this_lval = new NetAssign_(this_net); this_lval->set_property(member_name); if (canon_index) this_lval->set_word(canon_index); return this_lval; } NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, NetScope*scope, NetNet*reg, bool need_const_idx) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval_net_word_: " << "Handle as n-dimensional array." << endl; } if (name_tail.index.size() < reg->unpacked_dimensions()) { cerr << get_fileline() << ": error: Array " << reg->name() << " needs " << reg->unpacked_dimensions() << " indices," << " but got only " << name_tail.index.size() << "." << endl; des->errors += 1; return 0; } // Make sure there are enough indices to address an array element. const index_component_t&index_head = name_tail.index.front(); if (index_head.sel == index_component_t::SEL_PART) { cerr << get_fileline() << ": error: cannot perform a part " << "select on array " << reg->name() << "." << endl; des->errors += 1; return 0; } // Evaluate all the index expressions into an // "unpacked_indices" array. listunpacked_indices; list unpacked_indices_const; indices_flags flags; indices_to_expressions(des, scope, this, name_tail.index, reg->unpacked_dimensions(), false, flags, unpacked_indices, unpacked_indices_const); NetExpr*canon_index = 0; if (flags.invalid) { // Nothing to do. } else if (flags.undefined) { cerr << get_fileline() << ": warning: " << "ignoring undefined l-value array access " << reg->name() << as_indices(unpacked_indices) << "." << endl; } else if (flags.variable) { if (need_const_idx) { cerr << get_fileline() << ": error: array '" << reg->name() << "' index must be a constant in this context." << endl; des->errors += 1; return 0; } ivl_assert(*this, unpacked_indices.size() == reg->unpacked_dimensions()); canon_index = normalize_variable_unpacked(reg, unpacked_indices); } else { ivl_assert(*this, unpacked_indices_const.size() == reg->unpacked_dimensions()); canon_index = normalize_variable_unpacked(reg, unpacked_indices_const); if (canon_index == 0) { cerr << get_fileline() << ": warning: " << "ignoring out of bounds l-value array access " << reg->name() << as_indices(unpacked_indices_const) << "." << endl; } } // Ensure invalid array accesses are ignored. if (canon_index == 0) canon_index = new NetEConst(verinum(verinum::Vx)); canon_index->set_line(*this); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval_net_word_: " << "canon_index=" << *canon_index << endl; } if (reg->type()==NetNet::UNRESOLVED_WIRE) { cerr << get_fileline() << ": error: " << "Unable to assign words of unresolved wire array." << endl; des->errors += 1; return 0; } NetAssign_*lv = new NetAssign_(reg); lv->set_word(canon_index); if (debug_elaborate) cerr << get_fileline() << ": debug: Set array word=" << *canon_index << endl; /* An array word may also have part selects applied to them. */ index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (name_tail.index.size() > reg->unpacked_dimensions()) use_sel = name_tail.index.back().sel; if (reg->get_scalar() && use_sel != index_component_t::SEL_NONE) { cerr << get_fileline() << ": error: can not select part of "; if (reg->data_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << reg->name() << as_indices(unpacked_indices) << endl; des->errors += 1; return 0; } if (use_sel == index_component_t::SEL_BIT) elaborate_lval_net_bit_(des, scope, lv, need_const_idx); if (use_sel == index_component_t::SEL_PART) elaborate_lval_net_part_(des, scope, lv); if (use_sel == index_component_t::SEL_IDX_UP || use_sel == index_component_t::SEL_IDX_DO) elaborate_lval_net_idx_(des, scope, lv, use_sel, need_const_idx); return lv; } bool PEIdent::elaborate_lval_net_bit_(Design*des, NetScope*scope, NetAssign_*lv, bool need_const_idx) const { listprefix_indices; bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); if (!rc) return false; const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); NetNet*reg = lv->sig(); ivl_assert(*this, reg); // Bit selects have a single select expression. Evaluate the // constant value and treat it as a part select with a bit // width of 1. NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1); long lsb = 0; if (NetEConst*index_con = dynamic_cast (mux)) { // The index has a constant defined value. if (index_con->value().is_defined()) { lsb = index_con->value().as_long(); mux = 0; // The index is undefined and this is a packed array. } else if (prefix_indices.size()+2 <= reg->packed_dims().size()) { long loff; unsigned long lwid; bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid); ivl_assert(*this, rcl); cerr << get_fileline() << ": warning: L-value packed array " << "select of " << reg->name(); if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << " has an undefined index." << endl; lv->set_part(new NetEConst(verinum(verinum::Vx)), lwid); return true; // The index is undefined and this is a bit select. } else { cerr << get_fileline() << ": warning: L-value bit select of " << reg->name(); if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << " has an undefined index." << endl; lv->set_part(new NetEConst(verinum(verinum::Vx)), 1); return true; } } if (debug_elaborate && (reg->type()==NetNet::UNRESOLVED_WIRE)) { cerr << get_fileline() << ": PEIdent::elaborate_lval_net_bit_: " << "Try to assign bits of unresolved wire." << endl; } // Notice that we might be assigning to an unresolved wire. This // can happen if we are actually assigning to a variable that // has a partial continuous assignment to it. If that is the // case, then the bit select must be constant. ivl_assert(*this, need_const_idx || (reg->type()!=NetNet::UNRESOLVED_WIRE)); if (prefix_indices.size()+2 <= reg->packed_dims().size()) { // Special case: this is a slice of a multi-dimensional // packed array. For example: // reg [3:0][7:0] x; // x[2] = ... // This shows up as the prefix_indices being too short // for the packed dimensions of the vector. What we do // here is convert to a "slice" of the vector. if (mux == 0) { long loff; unsigned long lwid; bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid); ivl_assert(*this, rcl); if (reg->type()==NetNet::UNRESOLVED_WIRE) { bool rct = reg->test_and_set_part_driver(loff+lwid-1, loff); if (rct) { cerr << get_fileline() << ": error: " << "These bits are already driven." << endl; des->errors += 1; } } lv->set_part(new NetEConst(verinum(loff)), lwid); } else { ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); unsigned long lwid; mux = normalize_variable_slice_base(prefix_indices, mux, reg, lwid); lv->set_part(mux, lwid); } } else if (reg->data_type() == IVL_VT_STRING) { ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); // Special case: This is a select of a string // variable. The target of the assignment is a character // select of a string. Force the r-value to be an 8bit // vector and set the "part" to be the character select // expression. The code generator knows what to do with // this. if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Bit select of string becomes character select." << endl; } if (mux) lv->set_part(mux, 8); else lv->set_part(new NetEConst(verinum(lsb)), 8); } else if (mux) { ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); // Non-constant bit mux. Correct the mux for the range // of the vector, then set the l-value part select // expression. if (need_const_idx) { cerr << get_fileline() << ": error: '" << reg->name() << "' bit select must be a constant in this context." << endl; des->errors += 1; return false; } mux = normalize_variable_bit_base(prefix_indices, mux, reg); lv->set_part(mux, 1); } else if (reg->vector_width() == 1 && reg->sb_is_valid(prefix_indices,lsb)) { // Constant bit mux that happens to select the only bit // of the l-value. Don't bother with any select at all. // NOTE: Don't know what to do about unresolved wires // here, but they are probably wrong. ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); } else { // Constant bit select that does something useful. long loff = reg->sb_to_idx(prefix_indices,lsb); if (loff < 0 || loff >= (long)reg->vector_width()) { cerr << get_fileline() << ": error: bit select " << reg->name() << "[" <errors += 1; return 0; } if (reg->type()==NetNet::UNRESOLVED_WIRE) { bool rct = reg->test_and_set_part_driver(loff, loff); if (rct) { cerr << get_fileline() << ": error: " << "Bit " << loff << " is already driven." << endl; des->errors += 1; } } lv->set_part(new NetEConst(verinum(loff)), 1); } return true; } bool PEIdent::elaborate_lval_darray_bit_(Design*des, NetScope*scope, NetAssign_*lv)const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); // For now, only support single-dimension dynamic arrays. ivl_assert(*this, name_tail.index.size() == 1); if (lv->sig()->type()==NetNet::UNRESOLVED_WIRE) { cerr << get_fileline() << ": error: " << path_ << " Unable to darray word select unresolved wires." << endl; des->errors += 1; return false; } const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); // Evaluate the select expression... NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1); lv->set_word(mux); return true; } bool PEIdent::elaborate_lval_net_part_(Design*des, NetScope*scope, NetAssign_*lv) const { list prefix_indices; bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); ivl_assert(*this, rc); // The range expressions of a part select must be // constant. The calculate_parts_ function calculates the // values into msb and lsb. long msb, lsb; bool parts_defined_flag; bool flag = calculate_parts_(des, scope, msb, lsb, parts_defined_flag); if (!flag) return false; NetNet*reg = lv->sig(); ivl_assert(*this, reg); if (! parts_defined_flag) { cerr << get_fileline() << ": warning: L-value part select of " << reg->name(); if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << " has an undefined index." << endl; // Use a width of two here so we can distinguish between an // undefined bit or part select. lv->set_part(new NetEConst(verinum(verinum::Vx)), 2); return true; } if (reg->type()==NetNet::UNRESOLVED_WIRE) { bool rct = reg->test_and_set_part_driver(msb, lsb); if (rct) { cerr << get_fileline() << ": error: " << path_ << "Part select is double-driving unresolved wire." << endl; des->errors += 1; return false; } } const vector&packed = reg->packed_dims(); long loff, moff; long wid; if (prefix_indices.size()+1 < packed.size()) { // If there are fewer indices then there are packed // dimensions, then this is a range of slices. Calculate // it into a big slice. bool lrc; unsigned long tmp_lwid, tmp_mwid; lrc = reg->sb_to_slice(prefix_indices,lsb, loff, tmp_lwid); ivl_assert(*this, lrc); lrc = reg->sb_to_slice(prefix_indices,msb, moff, tmp_mwid); ivl_assert(*this, lrc); if (loff < moff) { moff = moff + tmp_mwid - 1; } else { long ltmp = moff; moff = loff + tmp_lwid - 1; loff = ltmp; } wid = moff - loff + 1; } else { loff = reg->sb_to_idx(prefix_indices,lsb); moff = reg->sb_to_idx(prefix_indices,msb); wid = moff - loff + 1; if (moff < loff) { cerr << get_fileline() << ": error: part select " << reg->name() << "[" << msb<<":"<errors += 1; return false; } } // Special case: The range winds up selecting the entire // vector. Treat this as no part select at all. if (loff == 0 && moff == (long)(reg->vector_width()-1)) { return true; } /* If the part select extends beyond the extremes of the variable, then report an error. Note that loff is converted to normalized form so is relative the variable pins. */ if (loff < 0 || moff >= (long)reg->vector_width()) { cerr << get_fileline() << ": warning: Part select " << reg->name() << "[" << msb<<":"<set_part(new NetEConst(verinum(loff)), wid); return true; } bool PEIdent::elaborate_lval_net_idx_(Design*des, NetScope*scope, NetAssign_*lv, index_component_t::ctype_t use_sel, bool need_const_idx) const { listprefix_indices; bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); ivl_assert(*this, rc); const name_component_t&name_tail = path_.back();; ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb != 0); NetNet*reg = lv->sig(); assert(reg); unsigned long wid; calculate_up_do_width_(des, scope, wid); NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); ivl_select_type_t sel_type = IVL_SEL_OTHER; // Handle the special case that the base is constant. For this // case we can reduce the expression. if (NetEConst*base_c = dynamic_cast (base)) { // For the undefined case just let the constant pass and // we will handle it in the code generator. if (base_c->value().is_defined()) { long lsv = base_c->value().as_long(); long offset = 0; // Get the signal range. const vector&packed = reg->packed_dims(); ivl_assert(*this, packed.size() == prefix_indices.size()+1); // We want the last range, which is where we work. const netrange_t&rng = packed.back(); if (((rng.get_msb() < rng.get_lsb()) && use_sel == index_component_t::SEL_IDX_UP) || ((rng.get_msb() > rng.get_lsb()) && use_sel == index_component_t::SEL_IDX_DO)) { offset = -wid + 1; } delete base; long rel_base = reg->sb_to_idx(prefix_indices,lsv) + offset; /* If we cover the entire lvalue just skip the select. */ if (rel_base == 0 && wid == reg->vector_width()) return true; base = new NetEConst(verinum(rel_base)); if (warn_ob_select) { if (rel_base < 0) { cerr << get_fileline() << ": warning: " << reg->name(); if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << lsv; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; } else { cerr << "-:"; } cerr << wid << "] is selecting before vector." << endl; } if (rel_base + wid > reg->vector_width()) { cerr << get_fileline() << ": warning: " << reg->name(); if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << lsv; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; } else { cerr << "-:"; } cerr << wid << "] is selecting after vector." << endl; } } } else { cerr << get_fileline() << ": warning: L-value indexed part " << "select of " << reg->name(); if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << " has an undefined base." << endl; } } else { if (need_const_idx) { cerr << get_fileline() << ": error: '" << reg->name() << "' base index must be a constant in this context." << endl; des->errors += 1; return false; } ivl_assert(*this, prefix_indices.size()+1 == reg->packed_dims().size()); /* Correct the mux for the range of the vector. */ if (use_sel == index_component_t::SEL_IDX_UP) { base = normalize_variable_part_base(prefix_indices, base, reg, wid, true); sel_type = IVL_SEL_IDX_UP; } else { // This is assumed to be a SEL_IDX_DO. base = normalize_variable_part_base(prefix_indices, base, reg, wid, false); sel_type = IVL_SEL_IDX_DOWN; } } if (debug_elaborate) cerr << get_fileline() << ": debug: Set part select width=" << wid << ", base=" << *base << endl; lv->set_part(base, wid, sel_type); return true; } NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope, NetNet*sig, const perm_string&method_name) const { if (debug_elaborate) { cerr << get_fileline() << ": elaborate_lval_net_class_member_: " << "l-value is property " << method_name << " of " << sig->name() << "." << endl; } const netclass_t*class_type = sig->class_type(); ivl_assert(*this, class_type); /* Make sure the property is really present in the class. If not, then generate an error message and return an error. */ int pidx = class_type->property_idx_from_name(method_name); if (pidx < 0) { cerr << get_fileline() << ": error: Class " << class_type->get_name() << " does not have a property " << method_name << "." << endl; des->errors += 1; return 0; } property_qualifier_t qual = class_type->get_prop_qual(pidx); if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { cerr << get_fileline() << ": error: " << "Local property " << class_type->get_prop_name(pidx) << " is not accessible (l-value) in this context." << " (scope=" << scope_path(scope) << ")" << endl; des->errors += 1; } else if (qual.test_static()) { // Special case: this is a static property. Ignore the // "this" sig and use the property itself, which is not // part of the sig, as the l-value. NetNet*psig = class_type->find_static_property(method_name); ivl_assert(*this, psig); NetAssign_*lv = new NetAssign_(psig); return lv; } else if (qual.test_const()) { cerr << get_fileline() << ": error: " << "Property " << class_type->get_prop_name(pidx) << " is constant in this context." << endl; des->errors += 1; } NetAssign_*lv = new NetAssign_(sig); lv->set_property(method_name); ivl_type_t ptype = class_type->get_prop_type(pidx); const netdarray_t*mtype = dynamic_cast (ptype); if (mtype) { const name_component_t&name_tail = path_.back(); if (! name_tail.index.empty()) { cerr << get_fileline() << ": sorry: " << "Array index of array properties not supported." << endl; des->errors += 1; } } return lv; } bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, NetAssign_*lv, const name_component_t&member_comp) const { const perm_string&member_name = member_comp.name; NetNet*reg = lv->sig(); ivl_assert(*this, reg); const netstruct_t*struct_type = reg->struct_type(); ivl_assert(*this, struct_type); if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate lval packed member: " << "path_=" << path_ << endl; } if (! struct_type->packed()) { cerr << get_fileline() << ": sorry: Only packed structures " << "are supported in l-value." << endl; des->errors += 1; return false; } // Shouldn't be seeing unpacked arrays of packed structs... ivl_assert(*this, reg->unpacked_dimensions() == 0); // This is a packed member, so the name is of the form // "a.b[...].c[...]" which means that the path_ must have at // least 2 components. We are processing "c[...]" at that // point (otherwise known as member_name) so we'll save a // reference to it in name_tail. We are also processing "b[]" // so save that as name_base. ivl_assert(*this, path_.size() >= 2); pform_name_t::const_reverse_iterator name_idx = path_.rbegin(); ivl_assert(*this, name_idx->name == member_name); const name_component_t&name_tail = *name_idx; ++ name_idx; const name_component_t&name_base = *name_idx; // Calculate the offset within the packed structure of the // member, and any indices. We will add in the offset of the // struct into the packed array later. Note that this works // for packed unions as well (although the offset will be 0 // for union members). unsigned long off; const netstruct_t::member_t* member = struct_type->packed_member(member_name, off); if (member == 0) { cerr << get_fileline() << ": error: Member " << member_name << " is not a member of variable " << reg->name() << endl; des->errors += 1; return false; } unsigned long use_width = member->net_type->packed_width(); // Get the index component type. At this point, we only // support bit select or none. index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) use_sel = name_tail.index.back().sel; ivl_assert(*this, use_sel == index_component_t::SEL_NONE || use_sel == index_component_t::SEL_BIT); if (! name_tail.index.empty()) { // If there are index expressions in this l-value // expression, then the implicit assumption is that the // member is a vector type with packed dimensions. For // example, if the l-value expression is "foo.member[1][2]", // then the member should be something like: // ... logic [h:l][m:n] foo; // Get the dimensions from the netvector_t that this implies. const netvector_t*mem_vec = dynamic_cast(member->net_type); ivl_assert(*this, mem_vec); const vector&mem_packed_dims = mem_vec->packed_dims(); if (name_tail.index.size() > mem_packed_dims.size()) { cerr << get_fileline() << ": error: " << "Too many index expressions for member." << endl; des->errors += 1; return false; } // Evaluate all but the last index expression, into prefix_indices. listprefix_indices; bool rc = evaluate_index_prefix(des, scope, prefix_indices, name_tail.index); ivl_assert(*this, rc); // Evaluate the last index expression into a constant long. NetExpr*texpr = elab_and_eval(des, scope, name_tail.index.back().msb, -1, true); long tmp; if (texpr == 0 || !eval_as_long(tmp, texpr)) { cerr << get_fileline() << ": error: " "Array index expressions must be constant here." << endl; des->errors += 1; return false; } delete texpr; // Now use the prefix_to_slice function to calculate the // offset and width of the addressed slice of the member. long loff; unsigned long lwid; prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid); off += loff; use_width = lwid; } // The dimensions in the expression must match the packed // dimensions that are declared for the variable. For example, // if foo is a packed array of struct, then this expression // must be "b[n][m]" with the right number of dimensions to // match the declaration of "b". // Note that one of the packed dimensions is the packed struct // itself. ivl_assert(*this, name_base.index.size()+1 == reg->packed_dimensions()); // Generate an expression that takes the input array of // expressions and generates a canonical offset into the // packed array. NetExpr*packed_base = 0; if (reg->packed_dimensions() > 1) { listtmp_index = name_base.index; index_component_t member_select; member_select.sel = index_component_t::SEL_BIT; member_select.msb = new PENumber(new verinum(off)); tmp_index.push_back(member_select); packed_base = collapse_array_indices(des, scope, reg, tmp_index); } long tmp; if (packed_base && eval_as_long(tmp, packed_base)) { off = tmp; delete packed_base; packed_base = 0; } if (reg->type()==NetNet::UNRESOLVED_WIRE) { cerr << get_fileline() << ": error: " << path_ << " Unable to member-select unresolved wires." << endl; des->errors += 1; return false; } if (packed_base == 0) { lv->set_part(new NetEConst(verinum(off)), use_width); return true; } // Oops, packed_base is not fully evaluated, so I don't know // yet what to do with it. cerr << get_fileline() << ": internal error: " << "I don't know how to handle this index expression? " << *packed_base << endl; ivl_assert(*this, 0); return false; } NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool, bool) const { cerr << get_fileline() << ": error: Constant values not allowed " << "in l-value expressions." << endl; des->errors += 1; return 0; } iverilog-10_1/elab_net.cc000066400000000000000000001006121265551621300154220ustar00rootroot00000000000000/* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "PExpr.h" # include "netlist.h" # include "netmisc.h" # include "netstruct.h" # include "netvector.h" # include "compiler.h" # include # include # include # include "ivl_assert.h" /* * The concatenation is also OK an an l-value. This method elaborates * it as a structural l-value. The return values is the *input* net of * the l-value, which may feed via part selects to the final * destination. The caller can connect gate outputs to this signal to * make the l-value connections. */ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, bool bidirectional_flag) const { assert(scope); svectornets (parms_.size()); unsigned width = 0; unsigned errors = 0; if (repeat_) { cerr << get_fileline() << ": sorry: I do not know how to" " elaborate repeat concatenation nets." << endl; return 0; } /* Elaborate the operands of the concatenation. */ for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate subexpression " << idx << " of " << nets.count() << " l-values: " << *parms_[idx] << endl; } if (parms_[idx] == 0) { cerr << get_fileline() << ": error: Empty expressions " << "not allowed in concatenations." << endl; errors += 1; continue; } if (bidirectional_flag) { nets[idx] = parms_[idx]->elaborate_bi_net(des, scope); } else { nets[idx] = parms_[idx]->elaborate_lnet(des, scope); } if (nets[idx] == 0) { errors += 1; } else if (nets[idx]->data_type() == IVL_VT_REAL) { cerr << parms_[idx]->get_fileline() << ": error: " << "concatenation operand can no be real: " << *parms_[idx] << endl; errors += 1; continue; } else { width += nets[idx]->vector_width(); } } if (errors) { des->errors += errors; return 0; } /* Make the temporary signal that connects to all the operands, and connect it up. Scan the operands of the concat operator from most significant to least significant, which is the order they are given in the concat list. */ netvector_t*tmp2_vec = new netvector_t(nets[0]->data_type(),width-1,0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, tmp2_vec); /* Assume that the data types of the nets are all the same, so we can take the data type of any, the first will do. */ osig->local_flag(true); osig->set_line(*this); if (bidirectional_flag) { if (debug_elaborate) { cerr << get_fileline() << ": debug: Generating tran(VP) " << "to connect input l-value to subexpressions." << endl; } for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { unsigned wid = nets[idx]->vector_width(); unsigned off = width - wid; NetTran*ps = new NetTran(scope, scope->local_symbol(), osig->vector_width(), wid, off); des->add_node(ps); ps->set_line(*this); connect(ps->pin(0), osig->pin(0)); connect(ps->pin(1), nets[idx]->pin(0)); ivl_assert(*this, wid <= width); width -= wid; } } else { if (debug_elaborate) { cerr << get_fileline() << ": debug: Generating part selects " << "to connect input l-value to subexpressions." << endl; } NetPartSelect::dir_t part_dir = NetPartSelect::VP; for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { unsigned wid = nets[idx]->vector_width(); unsigned off = width - wid; NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir); des->add_node(ps); ps->set_line(*this); connect(ps->pin(1), osig->pin(0)); connect(ps->pin(0), nets[idx]->pin(0)); assert(wid <= width); width -= wid; } assert(width == 0); } return osig; } NetNet* PEConcat::elaborate_lnet(Design*des, NetScope*scope) const { return elaborate_lnet_common_(des, scope, false); } NetNet* PEConcat::elaborate_bi_net(Design*des, NetScope*scope) const { return elaborate_lnet_common_(des, scope, true); } bool PEConcat::is_collapsible_net(Design*des, NetScope*scope) const { assert(scope); // Repeat concatenations are not currently supported. if (repeat_) return false; // Test the operands of the concatenation. for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { // Empty expressions are not allowed in concatenations if (parms_[idx] == 0) return false; if (!parms_[idx]->is_collapsible_net(des, scope)) return false; } return true; } /* * This private method evaluates the part selects (if any) for the * signal. The sig argument is the NetNet already located for the * PEIdent name. The midx and lidx arguments are loaded with the * results, which may be the whole vector, or a single bit, or * anything in between. The values are in canonical indices. */ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, long&midx, long&lidx) const { list prefix_indices; bool rc = calculate_packed_indices_(des, scope, sig, prefix_indices); ivl_assert(*this, rc); const name_component_t&name_tail = path_.back(); // Only treat as part/bit selects any index that is beyond the // word selects for an array. This is not an array, then // dimensions==0 and any index is treated as a select. if (name_tail.index.size() <= sig->unpacked_dimensions()) { midx = sig->vector_width()-1; lidx = 0; return true; } ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); switch (index_tail.sel) { default: cerr << get_fileline() << ": internal error: " << "Unexpected sel_ value = " << index_tail.sel << endl; ivl_assert(*this, 0); break; case index_component_t::SEL_IDX_DO: case index_component_t::SEL_IDX_UP: { NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*tmp = dynamic_cast(tmp_ex); if (!tmp) { cerr << get_fileline() << ": error: indexed part select of " << sig->name() << " must be a constant in this context." << endl; des->errors += 1; return 0; } /* The width (a constant) is calculated here. */ unsigned long wid = 0; bool flag = calculate_up_do_width_(des, scope, wid); if (! flag) return false; /* We have an undefined index and that is out of range. */ if (! tmp->value().is_defined()) { if (warn_ob_select) { cerr << get_fileline() << ": warning: " << sig->name(); if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx"; if (index_tail.sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; } else { cerr << "-:"; } cerr << wid << "] is always outside the vector." << endl; } return false; } long midx_val = tmp->value().as_long(); midx = sig->sb_to_idx(prefix_indices, midx_val); delete tmp_ex; if (index_tail.sel == index_component_t::SEL_IDX_UP) lidx = sig->sb_to_idx(prefix_indices, midx_val+wid-1); else lidx = sig->sb_to_idx(prefix_indices, midx_val-wid+1); if (midx < lidx) { long tmpx = midx; midx = lidx; lidx = tmpx; } /* Warn about an indexed part select that is out of range. */ if (warn_ob_select && (lidx < 0)) { cerr << get_fileline() << ": warning: " << sig->name(); if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << midx_val; if (index_tail.sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; } else { cerr << "-:"; } cerr << wid << "] is selecting before vector." << endl; } if (warn_ob_select && (midx >= (long)sig->vector_width())) { cerr << get_fileline() << ": warning: " << sig->name(); if (sig->unpacked_dimensions() > 0) { cerr << "[]"; } cerr << "[" << midx_val; if (index_tail.sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; } else { cerr << "-:"; } cerr << wid << "] is selecting after vector." << endl; } /* This is completely out side the signal so just skip it. */ if (lidx >= (long)sig->vector_width() || midx < 0) { return false; } break; } case index_component_t::SEL_PART: { long msb, lsb; bool part_defined_flag; /* bool flag = */ calculate_parts_(des, scope, msb, lsb, part_defined_flag); /* We have an undefined index and that is out of range. */ if (!part_defined_flag) { if (warn_ob_select) { cerr << get_fileline() << ": warning: " << sig->name(); if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx] is always outside the vector." << endl; } return false; } long lidx_tmp = sig->sb_to_idx(prefix_indices, lsb); long midx_tmp = sig->sb_to_idx(prefix_indices, msb); /* Detect reversed indices of a part select. */ if (lidx_tmp > midx_tmp) { cerr << get_fileline() << ": error: Part select " << sig->name() << "[" << msb << ":" << lsb << "] indices reversed." << endl; cerr << get_fileline() << ": : Did you mean " << sig->name() << "[" << lsb << ":" << msb << "]?" << endl; long tmp = midx_tmp; midx_tmp = lidx_tmp; lidx_tmp = tmp; des->errors += 1; } /* Warn about a part select that is out of range. */ if (midx_tmp >= (long)sig->vector_width() || lidx_tmp < 0) { cerr << get_fileline() << ": warning: Part select " << sig->name(); if (sig->unpacked_dimensions() > 0) { cerr << "[]"; } cerr << "[" << msb << ":" << lsb << "] is out of range." << endl; } /* This is completely out side the signal so just skip it. */ if (lidx_tmp >= (long)sig->vector_width() || midx_tmp < 0) { return false; } midx = midx_tmp; lidx = lidx_tmp; break; } case index_component_t::SEL_BIT: if (name_tail.index.size() > sig->unpacked_dimensions()) { long msb; bool bit_defined_flag; /* bool flag = */ calculate_bits_(des, scope, msb, bit_defined_flag); /* We have an undefined index and that is out of range. */ if (!bit_defined_flag) { if (warn_ob_select) { cerr << get_fileline() << ": warning: " << sig->name(); if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx] is always outside the vector." << endl; } return false; } if (prefix_indices.size()+2 <= sig->packed_dims().size()) { long tmp_loff; unsigned long tmp_lwid; bool rcl = sig->sb_to_slice(prefix_indices, msb, tmp_loff, tmp_lwid); ivl_assert(*this, rcl); midx = tmp_loff + tmp_lwid - 1; lidx = tmp_loff; } else { midx = sig->sb_to_idx(prefix_indices, msb); if (midx >= (long)sig->vector_width()) { cerr << get_fileline() << ": error: Index " << sig->name() << "[" << msb << "] is out of range." << endl; des->errors += 1; midx = 0; } lidx = midx; } } else { cerr << get_fileline() << ": internal error: " << "Bit select " << path_ << endl; ivl_assert(*this, 0); midx = sig->vector_width() - 1; lidx = 0; } break; } return true; } /* * This is the common code for l-value nets and bi-directional * nets. There is very little that is different between the two cases, * so most of the work for both is done here. */ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, bool bidirectional_flag) const { assert(scope); NetNet* sig = 0; const NetExpr*par = 0; NetEvent* eve = 0; perm_string method_name; symbol_search(this, des, scope, path_, sig, par, eve); if (eve != 0) { cerr << get_fileline() << ": error: named events (" << path_ << ") cannot be l-values in continuous " << "assignments." << endl; des->errors += 1; return 0; } // Break the path_ into the tail name and the prefix. For // example, a name "a.b.c" is broken into name_tail="c" and // path_prefix="a.b". const name_component_t&path_tail = path_.back(); pform_name_t path_prefix = path_; path_prefix.pop_back(); /* If the signal is not found, check to see if this is a member of a struct. Take the name of the form "a.b.member", remove the member and store it into method_name, and retry the search with "a.b". */ if (sig == 0 && path_.size() >= 2) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " "Symbol not found, try again with path_prefix=" << path_prefix << " and method_name=" << path_tail.name << endl; } method_name = path_tail.name; symbol_search(this, des, scope, path_prefix, sig, par, eve); // Whoops, not a struct signal, so give up on this avenue. if (sig && sig->struct_type() == 0) { cerr << get_fileline() << ": XXXXX: sig=" << sig->name() << " is found, but not a struct with member " << method_name << endl; method_name = perm_string(); sig = 0; } } if (sig == 0) { cerr << get_fileline() << ": error: Net " << path_ << " is not defined in this context." << endl; des->errors += 1; return 0; } assert(sig); /* If this is SystemVerilog and the variable is not yet assigned by anything, then convert it to an unresolved wire. */ if (gn_var_can_be_uwire() && (sig->type() == NetNet::REG) && (sig->peek_lref() == 0) ) { sig->type(NetNet::UNRESOLVED_WIRE); } /* Don't allow registers as assign l-values. */ if (sig->type() == NetNet::REG) { cerr << get_fileline() << ": error: reg " << sig->name() << "; cannot be driven by primitives" << " or continuous assignment." << endl; des->errors += 1; return 0; } // Default part select is the entire word. unsigned midx = sig->vector_width()-1, lidx = 0; // The default word select is the first. long widx = 0; // Set this to true if we calculate the word index. This is // used to distinguish between unpacked array assignment and // array word assignment. bool widx_flag = false; list unpacked_indices_const; const netstruct_t*struct_type = 0; if ((struct_type = sig->struct_type()) && !method_name.nil()) { // Detect the variable is a structure and there was a // method name detected. We've already found that // the path_ is <>.sig.method_name and signal // (NetNet). We also know that sig is struct_type(), so // look for a method named method_name. if (debug_elaborate) cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " << "Signal " << sig->name() << " is a structure, " << "try to match member " << method_name << endl; unsigned long member_off = 0; const struct netstruct_t::member_t*member = struct_type->packed_member(method_name, member_off); ivl_assert(*this, member); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " << "Member " << method_name << " has type " << *member->net_type << "." << endl; cerr << get_fileline() << ": : " << "Tail name has " << path_tail.index.size() << " indices." << endl; } // Rewrite a member select of a packed structure as a // part select of the base variable. lidx = member_off; midx = lidx + member->net_type->packed_width() - 1; // The dimensions of the tail of the prefix must match // the dimensions of the signal at this point. (The sig // has a packed dimension for the packed struct size.) // For example, if the path_=a[][].member, then // sig must have 3 packed dimensions: one for the struct // members and two actual packed dimensions. ivl_assert(*this, path_prefix.back().index.size()+1 == sig->packed_dimensions()); // Elaborate an expression from the packed indices and // the member offset (into the structure) to get a // canonical expression into the packed signal vector. NetExpr*packed_base = 0; if (sig->packed_dimensions() > 1) { listtmp_index = path_prefix.back().index; index_component_t member_select; member_select.sel = index_component_t::SEL_BIT; member_select.msb = new PENumber(new verinum(member_off)); tmp_index.push_back(member_select); packed_base = collapse_array_indices(des, scope, sig, tmp_index); if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " << "packed_base=" << *packed_base << ", member_off=" << member_off << endl; } } long tmp; if (packed_base && eval_as_long(tmp, packed_base)) { lidx = tmp; midx = lidx + member->net_type->packed_width() - 1; delete packed_base; packed_base = 0; } // Currently, only support const dimensions here. ivl_assert(*this, packed_base == 0); // Now the lidx/midx values get us to the member. Next // up, deal with bit/part selects from the member // itself. //XXXXivl_assert(*this, member->packed_dims.size() <= 1); ivl_assert(*this, path_tail.index.size() <= 1); if (! path_tail.index.empty()) { long tmp_off; unsigned long tmp_wid; const index_component_t&tail_sel = path_tail.index.back(); ivl_assert(*this, tail_sel.sel == index_component_t::SEL_PART || tail_sel.sel == index_component_t::SEL_BIT); bool rc = calculate_part(this, des, scope, tail_sel, tmp_off, tmp_wid); ivl_assert(*this, rc); if (debug_elaborate) cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " << "tmp_off=" << tmp_off << ", tmp_wid=" << tmp_wid << endl; lidx += tmp_off; midx = lidx + tmp_wid - 1; } } else if (gn_system_verilog() && sig->unpacked_dimensions() > 0 && path_tail.index.empty()) { // In this case, we are doing a continuous assignment to // an unpacked array. The NetNet representation is a // NetNet with a pin for each array element, so there is // nothing more needed here. // // This can come up from code like this: // logic [...] data [0:3]; // assign data = ...; // In this case, "sig" is "data", and sig->pin_count() // is 4 to account for the unpacked size. if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " << "Net assign to unpacked array \"" << sig->name() << "\" with " << sig->pin_count() << " elements." << endl; } } else if (sig->unpacked_dimensions() > 0) { // Make sure there are enough indices to address an array element. if (path_tail.index.size() < sig->unpacked_dimensions()) { cerr << get_fileline() << ": error: Array " << path() << " needs " << sig->unpacked_dimensions() << " indices," << " but got only " << path_tail.index.size() << ". (net)" << endl; des->errors += 1; return 0; } // Evaluate all the index expressions into an // "unpacked_indices" array. listunpacked_indices; indices_flags flags; indices_to_expressions(des, scope, this, path_tail.index, sig->unpacked_dimensions(), true, flags, unpacked_indices, unpacked_indices_const); if (flags.invalid) { return 0; } else if (flags.variable) { cerr << get_fileline() << ": error: array '" << sig->name() << "' index must be a constant in this context." << endl; des->errors += 1; return 0; } else if (flags.undefined) { cerr << get_fileline() << ": warning: " << "ignoring undefined l-value array access " << sig->name() << as_indices(unpacked_indices) << "." << endl; widx = -1; widx_flag = true; } else { NetExpr*canon_index = 0; ivl_assert(*this, unpacked_indices_const.size() == sig->unpacked_dimensions()); canon_index = normalize_variable_unpacked(sig, unpacked_indices_const); if (canon_index == 0) { cerr << get_fileline() << ": warning: " << "ignoring out of bounds l-value array access " << sig->name() << as_indices(unpacked_indices_const) << "." << endl; widx = -1; widx_flag = true; } else { NetEConst*canon_const = dynamic_cast(canon_index); ivl_assert(*this, canon_const); widx = canon_const->value().as_long(); widx_flag = true; delete canon_index; } } if (debug_elaborate) cerr << get_fileline() << ": debug: Use [" << widx << "]" << " to index l-value array." << endl; /* The array has a part/bit select at the end. */ if (path_tail.index.size() > sig->unpacked_dimensions()) { if (sig->get_scalar()) { cerr << get_fileline() << ": error: " << "can not select part of "; if (sig->data_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << sig->name() << as_indices(unpacked_indices_const) << endl; des->errors += 1; return 0; } long midx_tmp, lidx_tmp; if (! eval_part_select_(des, scope, sig, midx_tmp, lidx_tmp)) return 0; if (lidx_tmp < 0) { cerr << get_fileline() << ": sorry: part selects " "straddling the start of signal (" << path_ << ") are not currently supported." << endl; des->errors += 1; return 0; } midx = midx_tmp; lidx = lidx_tmp; } } else if (!path_tail.index.empty()) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " << "path_tail.index.size()=" << path_tail.index.size() << endl; } // There are index expressions on the name, so this is a // bit/slice select of the name. Calculate a canonical // part select. if (sig->get_scalar()) { cerr << get_fileline() << ": error: " << "can not select part of "; if (sig->data_type() == IVL_VT_REAL) cerr << "real: "; else cerr << "scalar: "; cerr << sig->name() << endl; des->errors += 1; return 0; } long midx_tmp, lidx_tmp; if (! eval_part_select_(des, scope, sig, midx_tmp, lidx_tmp)) return 0; if (lidx_tmp < 0) { cerr << get_fileline() << ": sorry: part selects " "straddling the start of signal (" << path_ << ") are not currently supported." << endl; des->errors += 1; return 0; } midx = midx_tmp; lidx = lidx_tmp; } unsigned subnet_wid = midx-lidx+1; /* Check if the l-value bits are double-driven. */ if (sig->type() == NetNet::UNRESOLVED_WIRE && sig->test_and_set_part_driver(midx,lidx, widx_flag? widx : 0)) { cerr << get_fileline() << ": error: Unresolved net/uwire " << sig->name() << " cannot have multiple drivers." << endl; if (debug_elaborate) { cerr << get_fileline() << ": : Overlap in " << "[" << midx << ":" << lidx << "] (canonical)" << ", widx=" << (widx_flag? widx : 0) << ", vector width=" << sig->vector_width() << endl; } des->errors += 1; return 0; } if (sig->pin_count() > 1 && widx_flag) { if (widx < 0 || widx >= (long) sig->pin_count()) return 0; netvector_t*tmp2_vec = new netvector_t(sig->data_type(), sig->vector_width()-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), sig->type(), tmp2_vec); tmp->set_line(*this); tmp->local_flag(true); connect(sig->pin(widx), tmp->pin(0)); sig = tmp; } else if (sig->pin_count() > 1) { // If this turns out to be an l-value unpacked array, // then let the caller handle it. It will probably be // converted into an array of assignments. return sig; } /* If the desired l-value vector is narrower than the signal itself, then use a NetPartSelect node to arrange for connection to the desired bits. All this can be skipped if the desired width matches the original vector. */ if (subnet_wid != sig->vector_width()) { /* If we are processing a tran or inout, then the partselect is bi-directional. Otherwise, it is a Part-to-Vector select. */ if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate lnet part select " << sig->name() << "[base=" << lidx << " wid=" << subnet_wid <<"]" << endl; netvector_t*tmp2_vec = new netvector_t(sig->data_type(), subnet_wid-1,0); NetNet*subsig = new NetNet(sig->scope(), sig->scope()->local_symbol(), NetNet::WIRE, tmp2_vec); subsig->local_flag(true); subsig->set_line(*this); if (bidirectional_flag) { // Make a tran(VP) NetTran*sub = new NetTran(scope, scope->local_symbol(), sig->vector_width(), subnet_wid, lidx); sub->set_line(*this); des->add_node(sub); connect(sub->pin(0), sig->pin(0)); connect(sub->pin(1), subsig->pin(0)); } else { NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid, NetPartSelect::PV); des->add_node(sub); sub->set_line(*this); connect(sub->pin(0), subsig->pin(0)); collapse_partselect_pv_to_concat(des, sig); } sig = subsig; } return sig; } /* * Identifiers in continuous assignment l-values are limited to wires * and that ilk. Detect registers and memories here and report errors. */ NetNet* PEIdent::elaborate_lnet(Design*des, NetScope*scope) const { return elaborate_lnet_common_(des, scope, false); } NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const { return elaborate_lnet_common_(des, scope, true); } /* * This method is used to elaborate identifiers that are ports to a * scope. The scope is presumed to be that of the module that has the * port. This elaboration is done inside the module, and is only done * to PEIdent objects. This method is used by elaboration of a module * instantiation (PGModule::elaborate_mod_) to get NetNet objects for * the port. */ NetNet* PEIdent::elaborate_subport(Design*des, NetScope*scope) const { ivl_assert(*this, scope->type() == NetScope::MODULE); NetNet*sig = des->find_signal(scope, path_); if (sig == 0) { cerr << get_fileline() << ": error: no wire/reg " << path_ << " in module " << scope_path(scope) << "." << endl; des->errors += 1; return 0; } /* Check the port_type of the signal to make sure it is really a port, and its direction is resolved. */ switch (sig->port_type()) { case NetNet::PINPUT: case NetNet::POUTPUT: case NetNet::PINOUT: case NetNet::PREF: break; /* If the name matches, but the signal is not a port, then the user declared the object but there is no matching input/output/inout declaration. */ case NetNet::NOT_A_PORT: cerr << get_fileline() << ": error: signal " << path_ << " in" << " module " << scope_path(scope) << " is not a port." << endl; cerr << get_fileline() << ": : Are you missing an input/" << "output/inout declaration?" << endl; des->errors += 1; return 0; /* This should not happen. A PWire can only become PIMPLICIT if this is a UDP reg port, and the make_udp function should turn it into an output.... I think. */ case NetNet::PIMPLICIT: cerr << get_fileline() << ": internal error: signal " << path_ << " in module " << scope_path(scope) << " is left as " << "port type PIMPLICIT." << endl; des->errors += 1; return 0; } long midx; long lidx; if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_subport: " << "path_ = \"" << path_ << "\", unpacked_dimensions=" << sig->unpacked_dimensions() << ", port_type()=" << sig->port_type() << endl; } if (sig->unpacked_dimensions() && !gn_system_verilog()) { cerr << get_fileline() << ": error: " << "Ports cannot be unpacked arrays. Try enabling SystemVerilog support." << endl; des->errors += 1; return 0; } // There cannot be parts to an unpacked array, so process this // simply as an unpacked array. if (sig->unpacked_dimensions()) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_subport: " << "path_=\"" << path_ << "\" is an unpacked array with " << sig->pin_count() << " elements." << endl; } scope->add_module_port_net(sig); return sig; } /* Evaluate the part/bit select expressions, to get the part select of the signal that attaches to the port. Also handle range and direction checking here. */ if (! eval_part_select_(des, scope, sig, midx, lidx)) return 0; /* If this is a part select of the entire signal (or no part select at all) then we're done. */ if ((lidx == 0) && (midx == (long)sig->vector_width()-1)) { scope->add_module_port_net(sig); return sig; } unsigned swid = abs(midx - lidx) + 1; ivl_assert(*this, swid > 0 && swid < sig->vector_width()); netvector_t*tmp2_vec = new netvector_t(sig->data_type(),swid-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp2_vec); tmp->port_type(sig->port_type()); tmp->set_line(*this); tmp->local_flag(true); NetNode*ps = 0; switch (sig->port_type()) { case NetNet::PINPUT: ps = new NetPartSelect(sig, lidx, swid, NetPartSelect::PV); connect(tmp->pin(0), ps->pin(0)); sig = tmp; break; case NetNet::POUTPUT: ps = new NetPartSelect(sig, lidx, swid, NetPartSelect::VP); connect(tmp->pin(0), ps->pin(0)); sig = tmp; break; case NetNet::PREF: // For the purposes of module ports, treat ref ports // just like inout ports. case NetNet::PINOUT: ps = new NetTran(scope, scope->local_symbol(), sig->vector_width(), swid, lidx); connect(sig->pin(0), ps->pin(0)); connect(tmp->pin(0), ps->pin(1)); sig = tmp; break; default: ivl_assert(*this, 0); break; } ps->set_line(*this); des->add_node(ps); scope->add_module_port_net(sig); return sig; } NetNet*PEIdent::elaborate_unpacked_net(Design*des, NetScope*scope) const { NetNet* sig = 0; const NetExpr*par = 0; NetEvent* eve = 0; perm_string method_name; symbol_search(this, des, scope, path_, sig, par, eve); ivl_assert(*this, sig); return sig; } bool PEIdent::is_collapsible_net(Design*des, NetScope*scope) const { assert(scope); NetNet* sig = 0; const NetExpr*par = 0; NetEvent* eve = 0; symbol_search(this, des, scope, path_, sig, par, eve); if (eve != 0) return false; if (sig == 0) return false; assert(sig); /* If this is SystemVerilog and the variable is not yet assigned by anything, then convert it to an unresolved wire. */ if (gn_var_can_be_uwire() && (sig->type() == NetNet::REG) && (sig->peek_eref() == 0) ) { sig->type(NetNet::UNRESOLVED_WIRE); } if (sig->type() == NetNet::UNRESOLVED_WIRE && sig->pin(0).is_linked()) return false; if (sig->type() == NetNet::REG) return false; return true; } iverilog-10_1/elab_scope.cc000066400000000000000000002131751265551621300157560ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "netmisc.h" # include # include # include # include /* * Elaboration happens in two passes, generally. The first scans the * pform to generate the NetScope tree and attach it to the Design * object. The methods in this source file implement the elaboration * of the scopes. */ # include "Module.h" # include "PClass.h" # include "PExpr.h" # include "PEvent.h" # include "PClass.h" # include "PGate.h" # include "PGenerate.h" # include "PPackage.h" # include "PTask.h" # include "PWire.h" # include "Statement.h" # include "AStatement.h" # include "netlist.h" # include "netclass.h" # include "netenum.h" # include "parse_api.h" # include "util.h" # include # include # include "ivl_assert.h" typedef map::const_iterator mparm_it_t; static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, const LexicalScope::param_expr_t&cur, bool is_annotatable, bool local_flag) { NetScope::range_t*range_list = 0; for (LexicalScope::range_t*range = cur.range ; range ; range = range->next) { NetScope::range_t*tmp = new NetScope::range_t; tmp->exclude_flag = range->exclude_flag; tmp->low_open_flag = range->low_open_flag; tmp->high_open_flag = range->high_open_flag; if (range->low_expr) { tmp->low_expr = elab_and_eval(des, scope, range->low_expr, -1); ivl_assert(*range->low_expr, tmp->low_expr); } else { tmp->low_expr = 0; } if (range->high_expr && range->high_expr==range->low_expr) { // Detect the special case of a "point" // range. These are called out by setting the high // and low expression ranges to the same // expression. The exclude_flags should be false // in this case ivl_assert(*range->high_expr, tmp->low_open_flag==false && tmp->high_open_flag==false); tmp->high_expr = tmp->low_expr; } else if (range->high_expr) { tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1); ivl_assert(*range->high_expr, tmp->high_expr); } else { tmp->high_expr = 0; } tmp->next = range_list; range_list = tmp; } scope->set_parameter(name, is_annotatable, cur.expr, cur.type, cur.msb, cur.lsb, cur.signed_flag, local_flag, range_list, cur); } static void collect_scope_parameters_(Design*des, NetScope*scope, const map¶meters) { for (mparm_it_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur ) { // A parameter can not have the same name as a genvar. if (scope->find_genvar((*cur).first)) { cerr << cur->second.get_fileline() << ": error: parameter and genvar in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } collect_parm_item_(des, scope, (*cur).first, (*cur).second, false, false); } } static void collect_scope_localparams_(Design*des, NetScope*scope, const map&localparams) { for (mparm_it_t cur = localparams.begin() ; cur != localparams.end() ; ++ cur ) { // A localparam can not have the same name as a genvar. if (scope->find_genvar((*cur).first)) { cerr << cur->second.get_fileline() << ": error: localparam and genvar in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } collect_parm_item_(des, scope, (*cur).first, (*cur).second, false, true); } } static void collect_scope_specparams_(Design*des, NetScope*scope, const map&specparams) { for (mparm_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { // A specparam can not have the same name as a genvar. if (scope->find_genvar((*cur).first)) { cerr << cur->second.get_fileline() << ": error: specparam and genvar in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } collect_parm_item_(des, scope, (*cur).first, (*cur).second, true, false); } } /* * Elaborate the enumeration into the given scope. If scope==0, then * the enumeration goes into $root instead of a scope. */ static void elaborate_scope_enumeration(Design*des, NetScope*scope, enum_type_t*enum_type) { bool rc_flag; assert(enum_type->range->size() == 1); pform_range_t&range = enum_type->range->front(); NetExpr*msb_ex = elab_and_eval(des, scope, range.first, -1); NetExpr*lsb_ex = elab_and_eval(des, scope, range.second, -1); long msb = 0; rc_flag = eval_as_long(msb, msb_ex); assert(rc_flag); long lsb = 0; rc_flag = eval_as_long(lsb, lsb_ex); assert(rc_flag); netenum_t*use_enum = new netenum_t(enum_type->base_type, enum_type->signed_flag, enum_type->integer_flag, msb, lsb, enum_type->names->size(), enum_type); use_enum->set_line(enum_type->li); if (scope) scope->add_enumeration_set(enum_type, use_enum); else des->add_enumeration_set(enum_type, use_enum); size_t name_idx = 0; // Find the enumeration width. long raw_width = use_enum->packed_width(); assert(raw_width > 0); unsigned enum_width = (unsigned)raw_width; // Define the default start value and the increment value to be the // correct type for this enumeration. verinum cur_value ((uint64_t)0, enum_width); cur_value.has_sign(enum_type->signed_flag); verinum one_value ((uint64_t)1, enum_width); one_value.has_sign(enum_type->signed_flag); // Find the maximum allowed enumeration value. verinum max_value (0); if (enum_type->signed_flag) { max_value = pow(verinum(2), verinum(enum_width-1)) - one_value; } else { max_value = pow(verinum(2), verinum(enum_width)) - one_value; } max_value.has_sign(enum_type->signed_flag); // Variable to indicate when a defined value wraps. bool implicit_wrapped = false; // Process the enumeration definition. for (list::const_iterator cur = enum_type->names->begin() ; cur != enum_type->names->end() ; ++ cur, name_idx += 1) { // Check to see if the enumeration name has a value given. if (cur->parm) { // There is an explicit value. elaborate/evaluate // the value and assign it to the enumeration name. NetExpr*val = elab_and_eval(des, scope, cur->parm, -1); NetEConst*val_const = dynamic_cast (val); if (val_const == 0) { cerr << use_enum->get_fileline() << ": error: Enumeration expression for " << cur->name <<" is not an integer constant." << endl; des->errors += 1; continue; } cur_value = val_const->value(); // Clear the implicit wrapped flag if a parameter is given. implicit_wrapped = false; // A 2-state value can not have a constant with X/Z bits. if (enum_type->base_type==IVL_VT_BOOL && ! cur_value.is_defined()) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " can not have an undefined value." << endl; des->errors += 1; } // If this is a literal constant and it has a defined // width then the width must match the enumeration width. if (PENumber *tmp = dynamic_cast(cur->parm)) { if (tmp->value().has_len() && (tmp->value().len() != enum_width)) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has an incorrectly sized constant." << endl; des->errors += 1; } } // If we are padding/truncating a negative value for an // unsigned enumeration that is an error or if the new // value does not have a defined width. if (((cur_value.len() != enum_width) || ! cur_value.has_len()) && ! enum_type->signed_flag && cur_value.is_negative()) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has a negative value." << endl; des->errors += 1; } // Narrower values need to be padded to the width of the // enumeration and defined to have the specified width. if (cur_value.len() < enum_width) { cur_value = pad_to_width(cur_value, enum_width); } // Some wider values can be truncated. if (cur_value.len() > enum_width) { unsigned check_width = enum_width - 1; // Check that the upper bits match the MSB for (unsigned idx = enum_width; idx < cur_value.len(); idx += 1) { if (cur_value[idx] != cur_value[check_width]) { // If this is an unsigned enumeration // then zero padding is okay. if (! enum_type->signed_flag && (idx == enum_width) && (cur_value[idx] == verinum::V0)) { check_width += 1; continue; } if (cur_value.is_defined()) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has a value that is too " << ((cur_value > max_value) ? "large" : "small") << " " << cur_value << "." << endl; } else { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has trimmed bits that do " << "not match the enumeration " << "MSB: " << cur_value << "." << endl; } des->errors += 1; break; } } // If this is an unsigned value then make sure // The upper bits are not 1. if (! cur_value.has_sign() && (cur_value[enum_width] == verinum::V1)) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has a value that is too large: " << cur_value << "." << endl; des->errors += 1; break; } cur_value = verinum(cur_value, enum_width); } // At this point the value has the correct size and needs // to have the correct sign attribute set. cur_value.has_len(true); cur_value.has_sign(enum_type->signed_flag); } else if (! cur_value.is_defined()) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has an undefined inferred value." << endl; des->errors += 1; continue; } // Check to see if an implicitly wrapped value is used. if (implicit_wrapped) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " has an inferred value that overflowed." << endl; des->errors += 1; } // The enumeration value must be unique. perm_string dup_name = use_enum->find_value(cur_value); if (dup_name) { cerr << use_enum->get_fileline() << ": error: Enumeration name " << cur->name << " and " << dup_name << " have the same value: " << cur_value << endl; des->errors += 1; } rc_flag = use_enum->insert_name(name_idx, cur->name, cur_value); if (scope) rc_flag &= scope->add_enumeration_name(use_enum, cur->name); else rc_flag &= des->add_enumeration_name(use_enum, cur->name); if (! rc_flag) { cerr << use_enum->get_fileline() << ": error: Duplicate enumeration name " << cur->name << endl; des->errors += 1; } // In case the next name has an implicit value, // increment the current value by one. if (cur_value.is_defined()) { if (cur_value == max_value) implicit_wrapped = true; cur_value = cur_value + one_value; } } use_enum->insert_name_close(); } static void elaborate_scope_enumerations(Design*des, NetScope*scope, const set&enum_types) { for (set::const_iterator cur = enum_types.begin() ; cur != enum_types.end() ; ++ cur) { enum_type_t*curp = *cur; elaborate_scope_enumeration(des, scope, curp); } } void elaborate_rootscope_enumerations(Design*des) { for (set::const_iterator cur = pform_enum_sets.begin() ; cur != pform_enum_sets.end() ; ++ cur) { enum_type_t*curp = *cur; elaborate_scope_enumeration(des, 0, curp); } } /* * If the pclass includes an implicit and explicit constructor, then * merge the implicit constructor into the explicit constructor as * statements in the beginning. * * This is not necessary for proper functionality, it is an * optimization, so we can easily give up if it doesn't seem like it * will obviously work. */ static void blend_class_constructors(PClass*pclass) { perm_string new1 = perm_string::literal("new"); perm_string new2 = perm_string::literal("new@"); PFunction*use_new; PFunction*use_new2; // Locate the explicit constructor. map::iterator iter_new = pclass->funcs.find(new1); if (iter_new == pclass->funcs.end()) use_new = 0; else use_new = iter_new->second; // Locate the implicit constructor. map::iterator iter_new2 = pclass->funcs.find(new2); if (iter_new2 == pclass->funcs.end()) use_new2 = 0; else use_new2 = iter_new2->second; // If there are no constructors, then we are done. if (use_new==0 && use_new2==0) return; // While we're here, look for a super.new() call. If we find // it, strip it out of the constructor and set it aside for // when we actually call the chained constructor. PChainConstructor*chain_new = use_new? use_new->extract_chain_constructor() : 0; // If we do not have an explicit constructor chain, but there // is a parent class, then create an implicit chain. if (chain_new==0 && pclass->type->base_type!=0) { chain_new = new PChainConstructor(pclass->type->base_args); chain_new->set_line(*pclass); } // If there are both an implicit and explicit constructor, // then blend the implicit constructor into the explicit // constructor. This eases the task for the elaborator later. if (use_new && use_new2) { // These constructors must be methods of the same class. ivl_assert(*use_new, use_new->method_of() == use_new2->method_of()); Statement*def_new = use_new->get_statement(); Statement*def_new2 = use_new2->get_statement(); // It is possible, i.e. recovering from a parse error, // for the statement from the constructor to be // missing. In that case, create an empty one. if (def_new==0) { def_new = new PBlock(PBlock::BL_SEQ); use_new->set_statement(def_new); } if (def_new2) use_new->push_statement_front(def_new2); // Now the implicit initializations are all built into // the constructor. Delete the "new@" constructor. pclass->funcs.erase(iter_new2); delete use_new2; use_new2 = 0; } if (chain_new) { if (use_new2) { use_new2->push_statement_front(chain_new); } else { use_new->push_statement_front(chain_new); } chain_new = 0; } } static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) { class_type_t*use_type = pclass->type; if (debug_scopes) { cerr << pclass->get_fileline() <<": elaborate_scope_class: " << "Elaborate scope class " << pclass->pscope_name() << endl; } class_type_t*base_class = dynamic_cast (use_type->base_type); if (use_type->base_type && !base_class) { cerr << pclass->get_fileline() << ": error: " << "Base type of " << use_type->name << " is not a class." << endl; des->errors += 1; } netclass_t*use_base_class = 0; if (base_class) { ivl_assert(*pclass, scope); use_base_class = scope->find_class(base_class->name); if (use_base_class == 0) { cerr << pclass->get_fileline() << ": error: " << "Base class " << base_class->name << " not found." << endl; des->errors += 1; } } netclass_t*use_class = new netclass_t(use_type->name, use_base_class); ivl_assert(*pclass, use_type->save_elaborated_type == 0); use_type->save_elaborated_type = use_class; // Class scopes have no parent scope, because references are // not allowed to escape a class method. NetScope*class_scope = new NetScope(0, hname_t(pclass->pscope_name()), NetScope::CLASS); class_scope->set_line(pclass); class_scope->set_class_def(use_class); use_class->set_class_scope(class_scope); use_class->set_definition_scope(scope); // Collect the properties, elaborate them, and add them to the // elaborated class definition. for (map::iterator cur = use_type->properties.begin() ; cur != use_type->properties.end() ; ++ cur) { ivl_type_s*tmp = cur->second.type->elaborate_type(des, scope); ivl_assert(*pclass, tmp); if (debug_scopes) { cerr << pclass->get_fileline() << ": elaborate_scope_class: " << " Property " << cur->first << " type=" << *tmp << endl; } use_class->set_property(cur->first, cur->second.qual, tmp); } for (map::iterator cur = pclass->tasks.begin() ; cur != pclass->tasks.end() ; ++cur) { hname_t use_name (cur->first); NetScope*method_scope = new NetScope(class_scope, use_name, NetScope::TASK); // Task methods are always automatic... method_scope->is_auto(true); method_scope->set_line(cur->second); if (debug_scopes) { cerr << cur->second->get_fileline() << ": elaborate_scope_class: " << "Elaborate method (task) scope " << scope_path(method_scope) << endl; } cur->second->elaborate_scope(des, method_scope); } for (map::iterator cur = pclass->funcs.begin() ; cur != pclass->funcs.end() ; ++cur) { hname_t use_name (cur->first); NetScope*method_scope = new NetScope(class_scope, use_name, NetScope::FUNC); // Function methods are always automatic... method_scope->is_auto(true); method_scope->set_line(cur->second); if (debug_scopes) { cerr << cur->second->get_fileline() << ": elaborate_scope_class: " << "Elaborate method (function) scope " << scope_path(method_scope) << endl; } cur->second->elaborate_scope(des, method_scope); } if (scope) { scope->add_class(use_class); } else { des->add_class(use_class, pclass); } } static void elaborate_scope_classes(Design*des, NetScope*scope, const vector&classes) { for (size_t idx = 0 ; idx < classes.size() ; idx += 1) { blend_class_constructors(classes[idx]); elaborate_scope_class(des, scope, classes[idx]); } } void elaborate_rootscope_classes(Design*des) { if (pform_classes.empty()) return; for (map::iterator cur = pform_classes.begin() ; cur != pform_classes.end() ; ++ cur) { blend_class_constructors(cur->second); elaborate_scope_class(des, 0, cur->second); } } static void replace_scope_parameters_(NetScope*scope, const LineInfo&loc, const Module::replace_t&replacements) { for (Module::replace_t::const_iterator cur = replacements.begin() ; cur != replacements.end() ; ++ cur ) { PExpr*val = (*cur).second; if (val == 0) { cerr << loc.get_fileline() << ": internal error: " << "Missing expression in parameter replacement for " << (*cur).first << endl;; } assert(val); if (debug_scopes) { cerr << loc.get_fileline() << ": debug: " << "Replace " << (*cur).first << " with expression " << *val << " from " << val->get_fileline() << "." << endl; cerr << loc.get_fileline() << ": : " << "Type=" << val->expr_type() << endl; } bool flag = scope->replace_parameter((*cur).first, val, scope->parent()); if (! flag) { cerr << val->get_fileline() << ": warning: parameter " << (*cur).first << " not found in " << scope_path(scope) << "." << endl; } } } static void elaborate_scope_events_(Design*des, NetScope*scope, const map&events) { for (map::const_iterator et = events.begin() ; et != events.end() ; ++ et ) { (*et).second->elaborate_scope(des, scope); } } static void elaborate_scope_task(Design*des, NetScope*scope, PTask*task) { hname_t use_name( task->pscope_name() ); NetScope*task_scope = new NetScope(scope, use_name, NetScope::TASK); task_scope->is_auto(task->is_auto()); task_scope->set_line(task); if (scope==0) des->add_root_task(task_scope, task); if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_task: " << "Elaborate task scope " << scope_path(task_scope) << endl; } task->elaborate_scope(des, task_scope); } static void elaborate_scope_tasks(Design*des, NetScope*scope, const map&tasks) { typedef map::const_iterator tasks_it_t; for (tasks_it_t cur = tasks.begin() ; cur != tasks.end() ; ++ cur ) { hname_t use_name( (*cur).first ); // A task can not have the same name as another scope object. const NetScope *child = scope->child(use_name); if (child) { cerr << cur->second->get_fileline() << ": error: task and "; child->print_type(cerr); cerr << " in '" << scope->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; continue; } // A task can not have the same name as a genvar. if (scope->find_genvar((*cur).first)) { cerr << cur->second->get_fileline() << ": error: task and genvar in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } // A task can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(des, (*cur).first, ex_msb, ex_lsb); if (parm) { cerr << cur->second->get_fileline() << ": error: task and parameter in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } elaborate_scope_task(des, scope, cur->second); } } static void elaborate_scope_func(Design*des, NetScope*scope, PFunction*task) { hname_t use_name( task->pscope_name() ); NetScope*task_scope = new NetScope(scope, use_name, NetScope::FUNC); task_scope->is_auto(task->is_auto()); task_scope->set_line(task); if (scope==0) des->add_root_task(task_scope, task); if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_func: " << "Elaborate task scope " << scope_path(task_scope) << endl; } task->elaborate_scope(des, task_scope); } static void elaborate_scope_funcs(Design*des, NetScope*scope, const map&funcs) { typedef map::const_iterator funcs_it_t; for (funcs_it_t cur = funcs.begin() ; cur != funcs.end() ; ++ cur ) { hname_t use_name( (*cur).first ); // A function can not have the same name as another scope object. const NetScope *child = scope->child(use_name); if (child) { cerr << cur->second->get_fileline() << ": error: function and "; child->print_type(cerr); cerr << " in '" << scope->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; continue; } // A function can not have the same name as a genvar. if (scope->find_genvar((*cur).first)) { cerr << cur->second->get_fileline() << ": error: function and genvar in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } // A function can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(des, (*cur).first, ex_msb, ex_lsb); if (parm) { cerr << cur->second->get_fileline() << ": error: function and parameter in '" << scope->fullname() << "' have the same name '" << (*cur).first << "'." << endl; des->errors += 1; } elaborate_scope_func(des, scope, cur->second); } } void elaborate_rootscope_tasks(Design*des) { for (map::iterator cur = pform_tasks.begin() ; cur != pform_tasks.end() ; ++ cur) { if (PTask*task = dynamic_cast (cur->second)) { elaborate_scope_task(des, 0, task); continue; } if (PFunction*func = dynamic_cast(cur->second)) { elaborate_scope_func(des, 0, func); continue; } cerr << cur->second->get_fileline() << ": internal error: " << "elaborate_rootscope_tasks does not understand " << "this object," << endl; des->errors += 1; } } class generate_schemes_work_item_t : public elaborator_work_item_t { public: generate_schemes_work_item_t(Design*des__, NetScope*scope, Module*mod) : elaborator_work_item_t(des__), scope_(scope), mod_(mod) { } void elaborate_runrun() { if (debug_scopes) cerr << mod_->get_fileline() << ": debug: " << "Processing generate schemes for " << scope_path(scope_) << endl; // Generate schemes can create new scopes in the form of // generated code. Scan the generate schemes, and *generate* // new scopes, which is slightly different from simple // elaboration. typedef list::const_iterator generate_it_t; for (generate_it_t cur = mod_->generate_schemes.begin() ; cur != mod_->generate_schemes.end() ; ++ cur ) { (*cur) -> generate_scope(des, scope_); } } private: // The scope_ is the scope that contains the generate scheme // we are to work on. the mod_ is the Module definition for // that scope, and contains the parsed generate schemes. NetScope*scope_; Module*mod_; }; bool PPackage::elaborate_scope(Design*des, NetScope*scope) { if (debug_scopes) { cerr << get_fileline() << ": PPackage::elaborate_scope: " << "Elaborate package " << scope_path(scope) << "." << endl; } collect_scope_parameters_(des, scope, parameters); collect_scope_localparams_(des, scope, localparams); elaborate_scope_enumerations(des, scope, enum_sets); elaborate_scope_classes(des, scope, classes_lexical); elaborate_scope_funcs(des, scope, funcs); elaborate_scope_tasks(des, scope, tasks); return true; } bool Module::elaborate_scope(Design*des, NetScope*scope, const replace_t&replacements) { if (debug_scopes) { cerr << get_fileline() << ": Module::elaborate_scope: " << "Elaborate " << scope_path(scope) << "." << endl; } // Add the genvars to the scope. typedef map::const_iterator genvar_it_t; for (genvar_it_t cur = genvars.begin(); cur != genvars.end(); ++ cur ) { scope->add_genvar((*cur).first, (*cur).second); } // Scan the parameters in the module, and store the information // needed to evaluate the parameter expressions. The expressions // will be evaluated later, once all parameter overrides for this // module have been done. collect_scope_parameters_(des, scope, parameters); collect_scope_localparams_(des, scope, localparams); collect_scope_specparams_(des, scope, specparams); // Run parameter replacements that were collected from the // containing scope and meant for me. replace_scope_parameters_(scope, *this, replacements); elaborate_scope_enumerations(des, scope, enum_sets); assert(classes.size() == classes_lexical.size()); elaborate_scope_classes(des, scope, classes_lexical); // Run through the defparams for this module and save the result // in a table for later final override. typedef list::const_iterator defparms_iter_t; for (defparms_iter_t cur = defparms.begin() ; cur != defparms.end() ; ++ cur ) { scope->defparams.push_back(make_pair(cur->first, cur->second)); } // Evaluate the attributes. Evaluate them in the scope of the // module that the attribute is attached to. Is this correct? unsigned nattr; attrib_list_t*attr = evaluate_attributes(attributes, nattr, des, scope); for (unsigned idx = 0 ; idx < nattr ; idx += 1) scope->attribute(attr[idx].key, attr[idx].val); delete[]attr; // Generate schemes need to have their scopes elaborated, but // we can not do that until defparams are run, so push it off // into an elaborate work item. if (debug_scopes) cerr << get_fileline() << ": debug: " << "Schedule generates within " << scope_path(scope) << " for elaboration after defparams." << endl; des->elaboration_work_list.push_back(new generate_schemes_work_item_t(des, scope, this)); // Tasks introduce new scopes, so scan the tasks in this // module. Create a scope for the task and pass that to the // elaborate_scope method of the PTask for detailed // processing. elaborate_scope_tasks(des, scope, tasks); // Functions are very similar to tasks, at least from the // perspective of scopes. So handle them exactly the same // way. elaborate_scope_funcs(des, scope, funcs); // Look for implicit modules and implicit gates for them. for (map::iterator cur = nested_modules.begin() ; cur != nested_modules.end() ; ++cur) { // Skip modules that must be explicitly instantiated. if (cur->second->port_count() > 0) continue; PGModule*nested_gate = new PGModule(cur->second, cur->second->mod_name()); nested_gate->set_line(*cur->second); gates_.push_back(nested_gate); } // Gates include modules, which might introduce new scopes, so // scan all of them to create those scopes. typedef list::const_iterator gates_it_t; for (gates_it_t cur = gates_.begin() ; cur != gates_.end() ; ++ cur ) { (*cur) -> elaborate_scope(des, scope); } // initial and always blocks may contain begin-end and // fork-join blocks that can introduce scopes. Therefore, I // get to scan processes here. typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin() ; cur != behaviors.end() ; ++ cur ) { (*cur) -> statement() -> elaborate_scope(des, scope); } // Scan through all the named events in this scope. We do not // need anything more than the current scope to do this // elaboration, so do it now. This allows for normal // elaboration to reference these events. elaborate_scope_events_(des, scope, events); scope->is_cell(is_cell); return des->errors == 0; } bool PGenerate::generate_scope(Design*des, NetScope*container) { switch (scheme_type) { case GS_LOOP: return generate_scope_loop_(des, container); case GS_CONDIT: return generate_scope_condit_(des, container, false); case GS_ELSE: return generate_scope_condit_(des, container, true); case GS_CASE: return generate_scope_case_(des, container); case GS_NBLOCK: return generate_scope_nblock_(des, container); case GS_CASE_ITEM: cerr << get_fileline() << ": internal error: " << "Case item outside of a case generate scheme?" << endl; return false; default: cerr << get_fileline() << ": sorry: Generate of this sort" << " is not supported yet!" << endl; return false; } } /* * This is the elaborate scope method for a generate loop. */ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) { // Check that the loop_index variable was declared in a // genvar statement. NetScope*cscope = container; while (cscope && !cscope->find_genvar(loop_index)) cscope = cscope->parent(); if (!cscope) { cerr << get_fileline() << ": error: genvar is missing for " "generate \"loop\" variable '" << loop_index << "'." << endl; des->errors += 1; return false; } // We're going to need a genvar... int genvar; // The initial value for the genvar does not need (nor can it // use) the genvar itself, so we can evaluate this expression // the same way any other parameter value is evaluated. NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1, true); NetEConst*init = dynamic_cast (init_ex); if (init == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" << " init expression: " << *loop_init << endl; des->errors += 1; return false; } // Check the generate block name. // A generate "loop" can not have the same name as another // scope object. Find any scope with this name, not just an // exact match scope. const NetScope *child = container->child_byname(scope_name); if (child) { cerr << get_fileline() << ": error: generate \"loop\" and "; child->print_type(cerr); cerr << " in '" << container->fullname() << "' have the same name '" << scope_name << "'." << endl; des->errors += 1; return false; } // A generate "loop" can not have the same name as a genvar. if (container->find_genvar(scope_name)) { cerr << get_fileline() << ": error: generate \"loop\" and " "genvar in '" << container->fullname() << "' have the same name '" << scope_name << "'." << endl; des->errors += 1; } // A generate "loop" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { cerr << get_fileline() << ": error: generate \"loop\" and " "named event in '" << container->fullname() << "' have the same name '" << scope_name << "'." << endl; des->errors += 1; } // A generate "loop" can not have the same name as a parameter. const NetExpr*tmsb; const NetExpr*tlsb; const NetExpr*texpr = container->get_parameter(des, scope_name, tmsb, tlsb); if (texpr != 0) { cerr << get_fileline() << ": error: generate \"loop\" and " "parameter in '" << container->fullname() << "' have the same name '" << scope_name << "'." << endl; des->errors += 1; } // These have all been checked so we just need to skip the actual // generation for these name conflicts. Not skipping these two will // cause the compiler to have problems (assert, inf. loop, etc.). if (container->get_parameter(des, loop_index, tmsb, tlsb)) return false; if (container->find_event(loop_index)) return false; genvar = init->value().as_long(); delete init_ex; if (debug_scopes) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; container->genvar_tmp_val = genvar; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*test = dynamic_cast(test_ex); if (test == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" << " conditional expression: " << *loop_test << endl; des->errors += 1; return false; } while (test->value().as_long()) { // The actual name of the scope includes the genvar so // that each instance has a unique name in the // container. The format of using [] is part of the // Verilog standard. hname_t use_name (scope_name, genvar); if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create generated scope " << use_name << endl; NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); // Set in the scope a localparam for the value of the // genvar within this instance of the generate // block. Code within this scope thus has access to the // genvar as a constant. { verinum genvar_verinum(genvar); genvar_verinum.has_sign(true); NetEConstParam*gp = new NetEConstParam(scope, loop_index, genvar_verinum); // The file and line information should really come // from the genvar statement, not the for loop. scope->set_parameter(loop_index, gp, *this); if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create implicit localparam " << loop_index << " = " << genvar_verinum << endl; } elaborate_subscope_(des, scope); // Calculate the step for the loop variable. NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1, true); NetEConst*step = dynamic_cast(step_ex); if (step == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" << " step expression: " << *loop_step << endl; des->errors += 1; return false; } if (debug_scopes) cerr << get_fileline() << ": debug: genvar step from " << genvar << " to " << step->value().as_long() << endl; genvar = step->value().as_long(); container->genvar_tmp_val = genvar; delete step; delete test_ex; test_ex = elab_and_eval(des, container, loop_test, -1); test = dynamic_cast(test_ex); assert(test); } // Clear the genvar_tmp field in the scope to reflect that the // genvar is no longer valid for evaluating expressions. container->genvar_tmp = perm_string(); return true; } bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else_flag) { NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*test = dynamic_cast (test_ex); if (test == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" << " conditional expression: " << *loop_test << endl; des->errors += 1; return false; } // If the condition evaluates as false, then do not create the // scope. if ( (test->value().as_long() == 0 && !else_flag) || (test->value().as_long() != 0 && else_flag) ) { if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " value=" << test->value() << ": skip generation" << endl; delete test_ex; return true; } hname_t use_name (scope_name); // A generate "if" can not have the same name as another scope object. const NetScope *child = container->child(use_name); if (child) { cerr << get_fileline() << ": error: generate \"if\" and "; child->print_type(cerr); cerr << " in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; return false; } // A generate "if" can not have the same name as a genvar. if (container->find_genvar(scope_name)) { cerr << get_fileline() << ": error: generate \"if\" and " "genvar in '" << container->fullname() << "' have the same name '" << scope_name << "'." << endl; des->errors += 1; } // A generate "if" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { cerr << get_fileline() << ": error: generate \"if\" and " "named event in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } // A generate "if" can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = container->get_parameter(des, scope_name, ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: generate \"if\" and " "parameter in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " value=" << test->value() << ": Generate scope=" << use_name << endl; probe_for_direct_nesting_(); if (direct_nested_) { if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " detected direct nesting." << endl; elaborate_subscope_direct_(des, container); return true; } // If this is not directly nested, then generate a scope // for myself. That is what I will pass to the subscope. NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); elaborate_subscope_(des, scope); return true; } bool PGenerate::generate_scope_case_(Design*des, NetScope*container) { NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*case_value_co = dynamic_cast(case_value_ex); if (case_value_co == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar case" << " expression: " << *loop_test << endl; des->errors += 1; return false; } if (debug_scopes) cerr << get_fileline() << ": debug: Generate case " << "switch value=" << case_value_co->value() << endl; PGenerate*default_item = 0; typedef list::const_iterator generator_it_t; generator_it_t cur = generate_schemes.begin(); while (cur != generate_schemes.end()) { PGenerate*item = *cur; assert( item->scheme_type == PGenerate::GS_CASE_ITEM ); // Detect that the item is a default. if (item->item_test.size() == 0) { default_item = item; ++ cur; continue; } bool match_flag = false; for (unsigned idx = 0 ; idx < item->item_test.size() && !match_flag ; idx +=1 ) { NetExpr*item_value_ex = elab_and_eval(des, container, item->item_test[idx], -1, true); NetEConst*item_value_co = dynamic_cast(item_value_ex); if (item_value_co == 0) { cerr << get_fileline() << ": error: Cannot evaluate " << " genvar case item expression: " << *item->item_test[idx] << endl; des->errors += 1; return false; } if (debug_scopes) cerr << get_fileline() << ": debug: Generate case " << "item value=" << item_value_co->value() << endl; if (case_value_co->value() == item_value_co->value()) match_flag = true; delete item_value_co; } // If we stumble on the item that matches, then break out now. if (match_flag) break; ++ cur; } delete case_value_co; case_value_co = 0; PGenerate*item = (cur == generate_schemes.end())? default_item : *cur; if (item == 0) { cerr << get_fileline() << ": debug: " << "No generate items found" << endl; return true; } if (debug_scopes) cerr << get_fileline() << ": debug: " << "Generate case matches item at " << item->get_fileline() << endl; // The name of the scope to generate, whatever that item is. hname_t use_name (item->scope_name); // A generate "case" can not have the same name as another scope object. const NetScope *child = container->child(use_name); if (child) { cerr << get_fileline() << ": error: generate \"case\" and "; child->print_type(cerr); cerr << " in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; return false; } // A generate "case" can not have the same name as a genvar. if (container->find_genvar(item->scope_name)) { cerr << get_fileline() << ": error: generate \"case\" and " "genvar in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } // A generate "case" can not have the same name as a named event. const NetEvent *event = container->find_event(item->scope_name); if (event) { cerr << get_fileline() << ": error: generate \"case\" and " "named event in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } // A generate "case" can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = container->get_parameter(des, item->scope_name, ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: generate \"case\" and " "parameter in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } item->probe_for_direct_nesting_(); if (item->direct_nested_) { if (debug_scopes) cerr << get_fileline() << ": debug: Generate case item " << scope_name << " detected direct nesting." << endl; item->elaborate_subscope_direct_(des, container); return true; } if (debug_scopes) { cerr << get_fileline() << ": PGenerate::generate_scope_case_: " << "Generate subscope " << use_name << " and elaborate." << endl; } NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); item->elaborate_subscope_(des, scope); return true; } bool PGenerate::generate_scope_nblock_(Design*des, NetScope*container) { hname_t use_name (scope_name); // A generate "block" can not have the same name as another scope // object. const NetScope *child = container->child(use_name); if (child) { cerr << get_fileline() << ": error: generate \"block\" and "; child->print_type(cerr); cerr << " in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; return false; } // A generate "block" can not have the same name as a genvar. if (container->find_genvar(scope_name)) { cerr << get_fileline() << ": error: generate \"block\" and " "genvar in '" << container->fullname() << "' have the same name '" << scope_name << "'." << endl; des->errors += 1; } // A generate "block" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { cerr << get_fileline() << ": error: generate \"block\" and " "named event in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } // A generate "block" can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = container->get_parameter(des, scope_name, ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: generate \"block\" and " "parameter in '" << container->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } if (debug_scopes) cerr << get_fileline() << ": debug: Generate named block " << ": Generate scope=" << use_name << endl; NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); elaborate_subscope_(des, scope); return true; } void PGenerate::elaborate_subscope_direct_(Design*des, NetScope*scope) { typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*curp = *cur; if (debug_scopes) { cerr << get_fileline() << ": elaborate_subscope_direct_: " << "Elaborate direct subscope " << curp->scope_name << " within scope " << scope_name << endl; } curp -> generate_scope(des, scope); } } void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) { // Add the genvars to this scope. typedef map::const_iterator genvar_it_t; for (genvar_it_t cur = genvars.begin(); cur != genvars.end(); ++ cur ) { scope->add_genvar((*cur).first, (*cur).second); } // Scan the localparams in this scope, and store the information // needed to evaluate the parameter expressions. The expressions // will be evaluated later, once all parameter overrides for this // module have been done. collect_scope_localparams_(des, scope, localparams); // Run through the defparams for this scope and save the result // in a table for later final override. typedef list::const_iterator defparms_iter_t; for (defparms_iter_t cur = defparms.begin() ; cur != defparms.end() ; ++ cur ) { scope->defparams.push_back(make_pair(cur->first, cur->second)); } // Scan the generated scope for nested generate schemes, // and *generate* new scopes, which is slightly different // from simple elaboration. typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur) -> generate_scope(des, scope); } // Scan through all the task and function declarations in this // scope. elaborate_scope_tasks(des, scope, tasks); elaborate_scope_funcs(des, scope, funcs); // Scan the generated scope for gates that may create // their own scopes. typedef list::const_iterator pgate_list_it_t; for (pgate_list_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur ) { (*cur) ->elaborate_scope(des, scope); } typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin() ; cur != behaviors.end() ; ++ cur ) { (*cur) -> statement() -> elaborate_scope(des, scope); } // Scan through all the named events in this scope. elaborate_scope_events_(des, scope, events); if (debug_scopes) cerr << get_fileline() << ": debug: Generated scope " << scope_path(scope) << " for generate block " << scope_name << endl; // Save the scope that we created, for future use. scope_list_.push_back(scope); } class delayed_elaborate_scope_mod_instances : public elaborator_work_item_t { public: delayed_elaborate_scope_mod_instances(Design*des__, const PGModule*obj, Module*mod, NetScope*sc) : elaborator_work_item_t(des__), obj_(obj), mod_(mod), sc_(sc) { } ~delayed_elaborate_scope_mod_instances() { } virtual void elaborate_runrun(); private: const PGModule*obj_; Module*mod_; NetScope*sc_; }; void delayed_elaborate_scope_mod_instances::elaborate_runrun() { if (debug_scopes) cerr << obj_->get_fileline() << ": debug: " << "Resume scope elaboration of instances of " << mod_->mod_name() << "." << endl; obj_->elaborate_scope_mod_instances_(des, mod_, sc_); } /* * Here we handle the elaborate scope of a module instance. The caller * has already figured out that this "gate" is a module, and has found * the module definition. The "sc" argument is the scope that will * contain this instance. */ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const { if (get_name() == "") { cerr << get_fileline() << ": error: Instantiation of module " << mod->mod_name() << " requires an instance name." << endl; des->errors += 1; return; } // Missing module instance names have already been rejected. assert(get_name() != ""); // A module instance can not have the same name as another scope object. const NetScope *child = sc->child(hname_t(get_name())); if (child) { cerr << get_fileline() << ": error: module <" << mod->mod_name() << "> instance and "; child->print_type(cerr); cerr << " in '" << sc->fullname() << "' have the same name '" << get_name() << "'." << endl; des->errors += 1; return; } // A module instance can not have the same name as a genvar. if (sc->find_genvar(get_name())) { cerr << get_fileline() << ": error: module <" << mod->mod_name() << "> instance and genvar in '" << sc->fullname() << "' have the same name '" << get_name() << "'." << endl; des->errors += 1; } // A module instance can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = sc->get_parameter(des, get_name(), ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: module <" << mod->mod_name() << "> instance and parameter in '" << sc->fullname() << "' have the same name '" << get_name() << "'." << endl; des->errors += 1; } // check for recursive instantiation by scanning the current // scope and its parents. Look for a module instantiation of // the same module, but farther up in the scope. unsigned rl_count = 0; bool in_genblk = false; for (NetScope*scn = sc ; scn ; scn = scn->parent()) { // We need to know if we are inside a generate block to allow // recursive instances. if (scn->type() == NetScope::GENBLOCK) { in_genblk = true; continue; } if (scn->type() != NetScope::MODULE) continue; if (strcmp(mod->mod_name(), scn->module_name()) != 0) continue; // We allow nested scopes if they are inside a generate block, // but only to a certain nesting depth. if (in_genblk) { rl_count += 1; if (rl_count > recursive_mod_limit) { cerr << get_fileline() << ": error: instance " << scope_path(sc) << "." << get_name() << " of module " << mod->mod_name() << " is nested too deep." << endl; cerr << get_fileline() << ": : check for " "proper recursion termination or increase the " "limit (" << recursive_mod_limit << ") with the -pRECURSIVE_MOD_LIMIT flag." << endl; des->errors += 1; return; } continue; } cerr << get_fileline() << ": error: You can not instantiate " << "module " << mod->mod_name() << " within itself." << endl; cerr << get_fileline() << ": : The offending instance is " << get_name() << " within " << scope_path(scn) << "." << endl; des->errors += 1; return; } if (msb_ || lsb_) { // If there are expressions to evaluate in order to know // the actual number of instances that will be // instantiated, then we have to delay further scope // elaboration until after defparams (above me) are // run. Do that by appending a work item to the // elaboration work list. if (debug_scopes) cerr << get_fileline() << ": debug: delay elaborate_scope" << " of array of " << get_name() << " in scope " << scope_path(sc) << "." << endl; elaborator_work_item_t*tmp = new delayed_elaborate_scope_mod_instances(des, this, mod, sc); des->elaboration_work_list.push_back(tmp); } else { // If there are no expressions that need to be evaluated // to elaborate the scope of this next instances, then // get right to it. elaborate_scope_mod_instances_(des, mod, sc); } } /* * This method is called to process a module instantiation after basic * sanity testing is already complete. */ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const { NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1, true) : 0; NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1, true) : 0; NetEConst*msb = dynamic_cast (mse); NetEConst*lsb = dynamic_cast (lse); assert( (msb == 0) || (lsb != 0) ); long instance_low = 0; long instance_high = 0; long instance_count = 1; bool instance_array = false; if (msb) { instance_array = true; instance_high = msb->value().as_long(); instance_low = lsb->value().as_long(); if (instance_high > instance_low) instance_count = instance_high - instance_low + 1; else instance_count = instance_low - instance_high + 1; delete mse; delete lse; } NetScope::scope_vec_t instances (instance_count); if (debug_scopes) { cerr << get_fileline() << ": debug: Create " << instance_count << " instances of " << get_name() << "." << endl; } // Run through the module instances, and make scopes out of // them. Also do parameter overrides that are done on the // instantiation line. for (int idx = 0 ; idx < instance_count ; idx += 1) { hname_t use_name (get_name()); if (instance_array) { int instance_idx = idx; if (instance_low < instance_high) instance_idx = instance_low + idx; else instance_idx = instance_low - idx; use_name = hname_t(get_name(), instance_idx); } if (debug_scopes) { cerr << get_fileline() << ": debug: Module instance " << use_name << " becomes child of " << scope_path(sc) << "." << endl; } // Create the new scope as a MODULE with my name. Note // that if this is a nested module, mark it thus so that // scope searches will continue into the parent scope. NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, bound_type_? true : false, mod->program_block, mod->is_interface); my_scope->set_line(get_file(), mod->get_file(), get_lineno(), mod->get_lineno()); my_scope->set_module_name(mod->mod_name()); instances[idx] = my_scope; // Set time units and precision. my_scope->time_unit(mod->time_unit); my_scope->time_precision(mod->time_precision); my_scope->time_from_timescale(mod->time_from_timescale); des->set_precision(mod->time_precision); // Look for module parameter replacements. The "replace" map // maps parameter name to replacement expression that is // passed. It is built up by the ordered overrides or named // overrides. Module::replace_t replace; // Positional parameter overrides are matched to parameter // names by using the param_names list of parameter // names. This is an ordered list of names so the first name // is parameter 0, the second parameter 1, and so on. if (overrides_) { assert(parms_ == 0); list::const_iterator cur = mod->param_names.begin(); list::const_iterator jdx = overrides_->begin(); for (;;) { if (jdx == overrides_->end()) break; if (cur == mod->param_names.end()) break; // No expression means that the parameter is not // replaced at all. if (*jdx) replace[*cur] = *jdx; ++ jdx; ++ cur; } } // Named parameter overrides carry a name with each override // so the mapping into the replace list is much easier. if (parms_) { assert(overrides_ == 0); for (unsigned jdx = 0 ; jdx < nparms_ ; jdx += 1) { // No expression means that the parameter is not // replaced. if (parms_[jdx].parm) replace[parms_[jdx].name] = parms_[jdx].parm; } } // This call actually arranges for the description of the // module type to process this instance and handle parameters // and sub-scopes that might occur. Parameters are also // created in that scope, as they exist. (I'll override them // later.) mod->elaborate_scope(des, my_scope, replace); } /* Stash the instance array of scopes into the parent scope. Later elaboration passes will use this vector to further elaborate the array. Note that the array is ordered from LSB to MSB. We will use that fact in the main elaborate to connect things in the correct order. */ sc->instance_arrays[get_name()] = instances; } /* * The isn't really able to create new scopes, but it does create the * event name in the current scope, so can be done during the * elaborate_scope scan. Note that the name_ of the PEvent object has * no hierarchy, but neither does the NetEvent, until it is stored in * the NetScope object. */ void PEvent::elaborate_scope(Design*des, NetScope*scope) const { // A named event can not have the same name as another scope object. const NetScope *child = scope->child(hname_t(name_)); if (child) { cerr << get_fileline() << ": error: named event and "; child->print_type(cerr); cerr << " in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A named event can not have the same name as a genvar. if (scope->find_genvar(name_)) { cerr << get_fileline() << ": error: named event and " << "genvar in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A named event can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(des, name_, ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: named event and " << "parameter in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } NetEvent*ev = new NetEvent(name_); ev->set_line(*this); scope->add_event(ev); } void PFunction::elaborate_scope(Design*des, NetScope*scope) const { assert(scope->type() == NetScope::FUNC); // Save a reference to the pform representation of the function // in case we need to perform early elaboration. scope->set_func_pform(this); // Assume the function is a constant function until we // find otherwise. scope->is_const_func(true); // Scan the parameters in the function, and store the information // needed to evaluate the parameter expressions. collect_scope_parameters_(des, scope, parameters); collect_scope_localparams_(des, scope, localparams); // Scan through all the named events in this scope. elaborate_scope_events_(des, scope, events); if (statement_) statement_->elaborate_scope(des, scope); } void PTask::elaborate_scope(Design*des, NetScope*scope) const { assert(scope->type() == NetScope::TASK); // Scan the parameters in the task, and store the information // needed to evaluate the parameter expressions. collect_scope_parameters_(des, scope, parameters); collect_scope_localparams_(des, scope, localparams); // Scan through all the named events in this scope. elaborate_scope_events_(des, scope, events); if (statement_) statement_->elaborate_scope(des, scope); } /* * The base statement does not have sub-statements and does not * introduce any scope, so this is a no-op. */ void Statement::elaborate_scope(Design*, NetScope*) const { } /* * When I get a behavioral block, check to see if it has a name. If it * does, then create a new scope for the statements within it, * otherwise use the current scope. Use the selected scope to scan the * statements that I contain. */ void PBlock::elaborate_scope(Design*des, NetScope*scope) const { NetScope*my_scope = scope; if (pscope_name() != 0) { hname_t use_name(pscope_name()); // A named block can not have the same name as another scope // object. const NetScope *child = scope->child(use_name); if (child) { cerr << get_fileline() << ": error: named block and "; child->print_type(cerr); cerr << " in '" << scope->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; return; } // A named block can not have the same name as a genvar. if (scope->find_genvar(pscope_name())) { cerr << get_fileline() << ": error: named block and " "genvar in '" << scope->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } // A named block can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(des, pscope_name(), ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: named block and " "parameter in '" << scope->fullname() << "' have the same name '" << use_name << "'." << endl; des->errors += 1; } if (debug_scopes) cerr << get_fileline() << ": debug: " << "Elaborate block scope " << use_name << " within " << scope_path(scope) << endl; // The scope type is begin-end or fork-join. The // sub-types of fork-join are not interesting to the scope. my_scope = new NetScope(scope, use_name, bl_type_!=BL_SEQ ? NetScope::FORK_JOIN : NetScope::BEGIN_END); my_scope->set_line(get_file(), get_lineno()); my_scope->is_auto(scope->is_auto()); // Scan the parameters in the scope, and store the information // needed to evaluate the parameter expressions. collect_scope_parameters_(des, my_scope, parameters); collect_scope_localparams_(des, my_scope, localparams); // Scan through all the named events in this scope. elaborate_scope_events_(des, my_scope, events); } for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) list_[idx] -> elaborate_scope(des, my_scope); } /* * The case statement itself does not introduce scope, but contains * other statements that may be named blocks. So scan the case items * with the elaborate_scope method. */ void PCase::elaborate_scope(Design*des, NetScope*scope) const { assert(items_); for (unsigned idx = 0 ; idx < (*items_).count() ; idx += 1) { assert( (*items_)[idx] ); if (Statement*sp = (*items_)[idx]->stat) sp -> elaborate_scope(des, scope); } } /* * The conditional statement (if-else) does not introduce scope, but * the statements of the clauses may, so elaborate_scope the contained * statements. */ void PCondit::elaborate_scope(Design*des, NetScope*scope) const { if (if_) if_ -> elaborate_scope(des, scope); if (else_) else_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PDelayStatement::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PDoWhile::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PEventStatement::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * The standard says that we create an implicit scope for foreach * loops, but that is just to hold the index variables, and we'll * handle them by creating unique names. So just jump into the * contained statement for scope elaboration. */ void PForeach::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PForever::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PForStatement::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PRepeat::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained * statement. */ void PWhile::elaborate_scope(Design*des, NetScope*scope) const { if (statement_) statement_ -> elaborate_scope(des, scope); } iverilog-10_1/elab_sig.cc000066400000000000000000001222361265551621300154240ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include "Module.h" # include "PClass.h" # include "PExpr.h" # include "PGate.h" # include "PGenerate.h" # include "PPackage.h" # include "PTask.h" # include "PWire.h" # include "Statement.h" # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "netclass.h" # include "netenum.h" # include "netvector.h" # include "netdarray.h" # include "netparray.h" # include "netqueue.h" # include "util.h" # include "ivl_assert.h" using namespace std; static bool get_const_argument(NetExpr*exp, verinum&res) { switch (exp->expr_type()) { case IVL_VT_REAL: { NetECReal*cv = dynamic_cast(exp); if (cv == 0) return false; verireal tmp = cv->value(); res = verinum(tmp.as_long()); break; } case IVL_VT_BOOL: case IVL_VT_LOGIC: { NetEConst*cv = dynamic_cast(exp); if (cv == 0) return false; res = cv->value(); break; } default: assert(0);; } return true; } #if 0 /* This function is not currently used. */ static bool get_const_argument(NetExpr*exp, long&res) { verinum tmp; bool rc = get_const_argument(exp, tmp); if (rc == false) return false; res = tmp.as_long(); return true; } #endif void Statement::elaborate_sig(Design*, NetScope*) const { } bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const { bool flag = true; for (map::const_iterator wt = wires.begin() ; wt != wires.end() ; ++ wt ) { PWire*cur = (*wt).second; NetNet*sig = cur->elaborate_sig(des, scope); if (sig && (sig->scope() == scope) && (sig->port_type() == NetNet::PREF)) { cerr << cur->get_fileline() << ": sorry: " << "Reference ports not supported yet." << endl; des->errors += 1; } /* If the signal is an input and is also declared as a reg, then report an error. */ if (sig && (sig->scope() == scope) && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINPUT) && (sig->type() == NetNet::REG)) { cerr << cur->get_fileline() << ": error: Port " << cur->basename() << " of module " << scope->module_name() << " is declared as input and as a reg type." << endl; des->errors += 1; } if (sig && (sig->scope() == scope) && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINOUT) && (sig->type() == NetNet::REG)) { cerr << cur->get_fileline() << ": error: Port " << cur->basename() << " of module " << scope->module_name() << " is declared as inout and as a reg type." << endl; des->errors += 1; } if (sig && (sig->scope() == scope) && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINOUT) && (sig->data_type() == IVL_VT_REAL)) { cerr << cur->get_fileline() << ": error: Port " << cur->basename() << " of module " << scope->module_name() << " is declared as a real inout port." << endl; des->errors += 1; } } return flag; } static void elaborate_sig_funcs(Design*des, NetScope*scope, const map&funcs) { typedef map::const_iterator mfunc_it_t; for (mfunc_it_t cur = funcs.begin() ; cur != funcs.end() ; ++ cur ) { hname_t use_name ( (*cur).first ); NetScope*fscope = scope->child(use_name); if (fscope == 0) { cerr << (*cur).second->get_fileline() << ": internal error: " << "Child scope for function " << (*cur).first << " missing in " << scope_path(scope) << "." << endl; des->errors += 1; continue; } if (debug_elaborate) { cerr << cur->second->get_fileline() << ": elaborate_sig_funcs: " << "Elaborate function " << use_name << " in " << scope_path(fscope) << endl; } cur->second->elaborate_sig(des, fscope); } } static void elaborate_sig_tasks(Design*des, NetScope*scope, const map&tasks) { typedef map::const_iterator mtask_it_t; for (mtask_it_t cur = tasks.begin() ; cur != tasks.end() ; ++ cur ) { NetScope*tscope = scope->child( hname_t((*cur).first) ); assert(tscope); (*cur).second->elaborate_sig(des, tscope); } } static void elaborate_sig_classes(Design*des, NetScope*scope, const map&classes) { for (map::const_iterator cur = classes.begin() ; cur != classes.end() ; ++ cur) { netclass_t*use_class = scope->find_class(cur->second->pscope_name()); use_class->elaborate_sig(des, cur->second); } } bool PPackage::elaborate_sig(Design*des, NetScope*scope) const { bool flag = true; if (debug_elaborate) { cerr << get_fileline() << ": PPackage::elaborate_sig: " << "Start package scope=" << scope_path(scope) << endl; } flag = elaborate_sig_wires_(des, scope) && flag; // After all the wires are elaborated, we are free to // elaborate the ports of the tasks defined within this // module. Run through them now. elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); elaborate_sig_classes(des, scope, classes); if (debug_elaborate) { cerr << get_fileline() << ": PPackage::elaborate_sig: " << "Done package scope=" << scope_path(scope) << ", flag=" << flag << endl; } return flag; } bool Module::elaborate_sig(Design*des, NetScope*scope) const { bool flag = true; // Scan all the ports of the module, and make sure that each // is connected to wires that have port declarations. for (unsigned idx = 0 ; idx < ports.size() ; idx += 1) { Module::port_t*pp = ports[idx]; if (pp == 0) continue; // The port has a name and an array of expressions. The // expression are all identifiers that should reference // wires within the scope. map::const_iterator wt; for (unsigned cc = 0 ; cc < pp->expr.size() ; cc += 1) { pform_name_t port_path (pp->expr[cc]->path()); // A concatenated wire of a port really should not // have any hierarchy. if (port_path.size() != 1) { cerr << get_fileline() << ": internal error: " << "Port " << port_path << " has a funny name?" << endl; des->errors += 1; } wt = wires.find(peek_tail_name(port_path)); if (wt == wires.end()) { cerr << get_fileline() << ": error: " << "Port " << port_path << " (" << (idx+1) << ") of module " << mod_name() << " is not declared within module." << endl; des->errors += 1; continue; } if ((*wt).second->get_port_type() == NetNet::NOT_A_PORT) { cerr << get_fileline() << ": error: " << "Port " << pp->expr[cc]->path() << " (" << (idx+1) << ") of module " << mod_name() << " has no direction declaration." << endl; des->errors += 1; } } } flag = elaborate_sig_wires_(des, scope) && flag; // Run through all the generate schemes to elaborate the // signals that they hold. Note that the generate schemes hold // the scopes that they instantiated, so we don't pass any // scope in. typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur) -> elaborate_sig(des, scope); } // Get all the gates of the module and elaborate them by // connecting them to the signals. The gate may be simple or // complex. What we are looking for is gates that are modules // that can create scopes and signals. const list&gl = get_gates(); for (list::const_iterator gt = gl.begin() ; gt != gl.end() ; ++ gt ) { flag &= (*gt)->elaborate_sig(des, scope); } // After all the wires are elaborated, we are free to // elaborate the ports of the tasks defined within this // module. Run through them now. elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); elaborate_sig_classes(des, scope, classes); // initial and always blocks may contain begin-end and // fork-join blocks that can introduce scopes. Therefore, I // get to scan processes here. typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin() ; cur != behaviors.end() ; ++ cur ) { (*cur) -> statement() -> elaborate_sig(des, scope); } return flag; } void netclass_t::elaborate_sig(Design*des, PClass*pclass) { for (map::iterator cur = pclass->type->properties.begin() ; cur != pclass->type->properties.end() ; ++ cur) { if (! cur->second.qual.test_static()) continue; if (debug_elaborate) { cerr << pclass->get_fileline() << ": netclass_t::elaborate_sig: " << "Elaborate static property " << cur->first << " as signal in scope " << scope_path(class_scope_) << "." << endl; } list nil_list; ivl_type_t use_type = cur->second.type->elaborate_type(des, class_scope_); /* NetNet*sig = */ new NetNet(class_scope_, cur->first, NetNet::REG, nil_list, use_type); } for (map::iterator cur = pclass->funcs.begin() ; cur != pclass->funcs.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": netclass_t::elaborate_sig: " << "Elaborate signals in function method " << cur->first << endl; } NetScope*scope = class_scope_->child( hname_t(cur->first) ); ivl_assert(*cur->second, scope); cur->second->elaborate_sig(des, scope); } for (map::iterator cur = pclass->tasks.begin() ; cur != pclass->tasks.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": netclass_t::elaborate_sig: " << "Elaborate signals in task method " << cur->first << endl; } NetScope*scope = class_scope_->child( hname_t(cur->first) ); ivl_assert(*cur->second, scope); cur->second->elaborate_sig(des, scope); } } bool PGate::elaborate_sig(Design*, NetScope*) const { return true; } bool PGBuiltin::elaborate_sig(Design*, NetScope*) const { return true; } bool PGAssign::elaborate_sig(Design*, NetScope*) const { return true; } bool PGModule::elaborate_sig_mod_(Design*des, NetScope*scope, Module*rmod) const { bool flag = true; NetScope::scope_vec_t instance = scope->instance_arrays[get_name()]; for (unsigned idx = 0 ; idx < instance.size() ; idx += 1) { // I know a priori that the elaborate_scope created the scope // already, so just look it up as a child of the current scope. NetScope*my_scope = instance[idx]; assert(my_scope); if (my_scope->parent() != scope) { cerr << get_fileline() << ": internal error: " << "Instance " << scope_path(my_scope) << " is in parent " << scope_path(my_scope->parent()) << " instead of " << scope_path(scope) << endl; } assert(my_scope->parent() == scope); if (! rmod->elaborate_sig(des, my_scope)) flag = false; } return flag; } // Not currently used. #if 0 bool PGModule::elaborate_sig_udp_(Design*des, NetScope*scope, PUdp*udp) const { return true; } #endif bool PGenerate::elaborate_sig(Design*des, NetScope*container) const { if (direct_nested_) return elaborate_sig_direct_(des, container); bool flag = true; // Handle the special case that this is a CASE scheme. In this // case the PGenerate itself does not have the generated // item. Look instead for the case ITEM that has a scope // generated for it. if (scheme_type == PGenerate::GS_CASE) { if (debug_elaborate) cerr << get_fileline() << ": debug: generate case" << " elaborate_sig in scope " << scope_path(container) << "." << endl; typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*item = *cur; if (item->direct_nested_ || !item->scope_list_.empty()) { flag &= item->elaborate_sig(des, container); } } return flag; } typedef list::const_iterator scope_list_it_t; for (scope_list_it_t cur = scope_list_.begin() ; cur != scope_list_.end() ; ++ cur ) { NetScope*scope = *cur; if (scope->parent() != container) continue; if (debug_elaborate) cerr << get_fileline() << ": debug: Elaborate nets in " << "scope " << scope_path(*cur) << " in generate " << id_number << endl; flag = elaborate_sig_(des, *cur) & flag; } return flag; } bool PGenerate::elaborate_sig_direct_(Design*des, NetScope*container) const { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Direct nesting " << scope_name << " (scheme_type=" << scheme_type << ")" << " elaborate_sig in scope " << scope_path(container) << "." << endl; // Elaborate_sig for a direct nested generated scheme knows // that there are only sub_schemes to be elaborated. There // should be exactly 1 active generate scheme, search for it // using this loop. bool flag = true; typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*item = *cur; if (item->scheme_type == PGenerate::GS_CASE) { for (generate_it_t icur = item->generate_schemes.begin() ; icur != item->generate_schemes.end() ; ++ icur ) { PGenerate*case_item = *icur; if (case_item->direct_nested_ || !case_item->scope_list_.empty()) { flag &= case_item->elaborate_sig(des, container); } } } else { if (item->direct_nested_ || !item->scope_list_.empty()) { // Found the item, and it is direct nested. flag &= item->elaborate_sig(des, container); } } } return flag; } bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const { // Scan the declared PWires to elaborate the obvious signals // in the current scope. typedef map::const_iterator wires_it_t; for (wires_it_t wt = wires.begin() ; wt != wires.end() ; ++ wt ) { PWire*cur = (*wt).second; if (debug_elaborate) cerr << get_fileline() << ": debug: Elaborate PWire " << cur->basename() << " in scope " << scope_path(scope) << endl; cur->elaborate_sig(des, scope); } elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur) -> elaborate_sig(des, scope); } typedef list::const_iterator pgate_list_it_t; for (pgate_list_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur ) { (*cur) ->elaborate_sig(des, scope); } typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin() ; cur != behaviors.end() ; ++ cur ) { (*cur) -> statement() -> elaborate_sig(des, scope); } return true; } /* * A function definition exists within an elaborated module. This * matters when elaborating signals, as the ports of the function are * created as signals/variables for each instance of the * function. That is why PFunction has an elaborate_sig method. */ void PFunction::elaborate_sig(Design*des, NetScope*scope) const { if (scope->elab_stage() > 1) return; scope->set_elab_stage(2); perm_string fname = scope->basename(); assert(scope->type() == NetScope::FUNC); elaborate_sig_wires_(des, scope); NetNet*ret_sig; if (gn_system_verilog() && (fname=="new" || fname=="new@")) { // Special case: this is a constructor, so the return // signal is also the first argument. For example, the // source code for the definition may be: // function new(...); // endfunction // In this case, the "@" port is the synthetic "this" // argument and we also use it as a return value at the // same time. ret_sig = scope->find_signal(perm_string::literal("@")); ivl_assert(*this, ret_sig); if (debug_elaborate) cerr << get_fileline() << ": PFunction::elaborate_sig: " << "Scope " << scope_path(scope) << " is a CONSTRUCTOR, so use \"this\" argument" << " as return value." << endl; } else { ivl_type_t ret_type; if (return_type_) { if (dynamic_cast (return_type_)) { ret_type = 0; } else { ret_type = return_type_->elaborate_type(des, scope->parent()); ivl_assert(*this, ret_type); } } else { netvector_t*tmp = new netvector_t(IVL_VT_LOGIC); tmp->set_scalar(true); ret_type = tmp; } if (ret_type) { if (debug_elaborate) { cerr << get_fileline() << ": PFunction::elaborate_sig: " << "return type: " << *ret_type << endl; if (return_type_) return_type_->pform_dump(cerr, 8); } list ret_unpacked; ret_sig = new NetNet(scope, fname, NetNet::REG, ret_unpacked, ret_type); ret_sig->set_line(*this); ret_sig->port_type(NetNet::POUTPUT); } else { ret_sig = 0; if (debug_elaborate) { cerr << get_fileline() << ": PFunction::elaborate_sig: " << "Detected that function is void." << endl; } } } vectorports; vectorpdef; elaborate_sig_ports_(des, scope, ports, pdef); NetFuncDef*def = new NetFuncDef(scope, ret_sig, ports, pdef); if (debug_elaborate) cerr << get_fileline() << ": PFunction::elaborate_sig: " << "Attach function definition " << scope_path(scope) << " with ret_sig width=" << (ret_sig? ret_sig->vector_width() : 0) << "." << endl; scope->set_func_def(def); // Look for further signals in the sub-statement if (statement_) statement_->elaborate_sig(des, scope); } /* * A task definition is a scope within an elaborated module. When we * are elaborating signals, the scopes have already been created, as * have the reg objects that are the parameters of this task. The * elaborate_sig method of PTask is therefore left to connect the * signals to the ports of the NetTaskDef definition. We know for * certain that signals exist (They are in my scope!) so the port * binding is sure to work. */ void PTask::elaborate_sig(Design*des, NetScope*scope) const { assert(scope->type() == NetScope::TASK); elaborate_sig_wires_(des, scope); vectorports; vectorpdefs; elaborate_sig_ports_(des, scope, ports, pdefs); NetTaskDef*def = new NetTaskDef(scope, ports, pdefs); scope->set_task_def(def); // Look for further signals in the sub-statement if (statement_) statement_->elaborate_sig(des, scope); } void PTaskFunc::elaborate_sig_ports_(Design*des, NetScope*scope, vector&ports, vector&pdefs) const { if (ports_ == 0) { ports.clear(); pdefs.clear(); /* Make sure the function has at least one input port. If it fails this test, print an error message. Keep going so we can find more errors. */ if (scope->type()==NetScope::FUNC && !gn_system_verilog()) { cerr << get_fileline() << ": error: " << "Function " << scope->basename() << " has no ports." << endl; cerr << get_fileline() << ": : " << "Functions must have at least one input port." << endl; des->errors += 1; } return; } ports.resize(ports_->size()); pdefs.resize(ports_->size()); for (size_t idx = 0 ; idx < ports_->size() ; idx += 1) { perm_string port_name = ports_->at(idx).port->basename(); ports[idx] = 0; pdefs[idx] = 0; NetNet*tmp = scope->find_signal(port_name); NetExpr*tmp_def = 0; if (tmp == 0) { cerr << get_fileline() << ": internal error: " << "task/function " << scope_path(scope) << " is missing port " << port_name << "." << endl; scope->dump(cerr); cerr << get_fileline() << ": Continuing..." << endl; des->errors += 1; continue; } // If the port has a default expression, elaborate // that expression here. if (ports_->at(idx).defe != 0) { if (tmp->port_type() == NetNet::PINPUT) { tmp_def = elab_and_eval(des, scope, ports_->at(idx).defe, -1, scope->need_const_func()); if (tmp_def == 0) { cerr << get_fileline() << ": error: Unable to evaluate " << *ports_->at(idx).defe << " as a port default expression." << endl; des->errors += 1; } } else { cerr << get_fileline() << ": sorry: Default arguments " "for subroutine output or inout ports are not " "yet supported." << endl; des->errors += 1; } } if (tmp->port_type() == NetNet::NOT_A_PORT) { cerr << get_fileline() << ": internal error: " << "task/function " << scope_path(scope) << " port " << port_name << " is a port but is not a port?" << endl; des->errors += 1; scope->dump(cerr); continue; } ports[idx] = tmp; pdefs[idx] = tmp_def; if (scope->type()==NetScope::FUNC && tmp->port_type()!=NetNet::PINPUT) { cerr << tmp->get_fileline() << ": error: " << "Function " << scope_path(scope) << " port " << port_name << " is not an input port." << endl; cerr << tmp->get_fileline() << ": : " << "Function arguments must be input ports." << endl; des->errors += 1; } } } void PBlock::elaborate_sig(Design*des, NetScope*scope) const { NetScope*my_scope = scope; if (pscope_name() != 0) { hname_t use_name (pscope_name()); my_scope = scope->child(use_name); if (my_scope == 0) { cerr << get_fileline() << ": internal error: " << "Unable to find child scope " << pscope_name() << " in this context?" << endl; des->errors += 1; my_scope = scope; } else { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "elaborate_sig descending into " << scope_path(my_scope) << "." << endl; elaborate_sig_wires_(des, my_scope); } } // elaborate_sig in the statements included in the // block. There may be named blocks in there. for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) list_[idx] -> elaborate_sig(des, my_scope); } void PCase::elaborate_sig(Design*des, NetScope*scope) const { if (items_ == 0) return; for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { if ( (*items_)[idx]->stat ) (*items_)[idx]->stat ->elaborate_sig(des,scope); } } void PCondit::elaborate_sig(Design*des, NetScope*scope) const { if (if_) if_->elaborate_sig(des, scope); if (else_) else_->elaborate_sig(des, scope); } void PDelayStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PDoWhile::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PEventStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PForeach::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PForever::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PForStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PRepeat::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PWhile::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } static ivl_type_s*elaborate_type(Design*des, NetScope*scope, data_type_t*pform_type) { if (struct_type_t*struct_type = dynamic_cast(pform_type)) { ivl_type_s*use_type = struct_type->elaborate_type(des, scope); return use_type; } cerr << pform_type->get_fileline() << ": sorry: I don't know how to elaborate " << typeid(*pform_type).name() << " here." << endl; des->errors += 1; return 0; } static netparray_t* elaborate_parray_type(Design*des, NetScope*scope, parray_type_t*data_type) { vectorpacked_dimensions; bool bad_range = evaluate_ranges(des, scope, packed_dimensions, * data_type->dims); ivl_assert(*data_type, !bad_range); ivl_type_s*element_type = elaborate_type(des, scope, data_type->base_type); netparray_t*res = new netparray_t(packed_dimensions, element_type); //res->set_line(*data_type); return res; } bool test_ranges_eeq(const vector&lef, const vector&rig) { if (lef.size() != rig.size()) return false; vector::const_iterator lcur = lef.begin(); vector::const_iterator rcur = rig.begin(); while (lcur != lef.end()) { if (lcur->get_msb() != rcur->get_msb()) return false; if (lcur->get_lsb() != rcur->get_lsb()) return false; ++ lcur; ++ rcur; } return true; } /* * Elaborate a source wire. The "wire" is the declaration of wires, * registers, ports and memories. The parser has already merged the * multiple properties of a wire (i.e., "input wire"), so come the * elaboration this creates an object in the design that represents the * defined item. */ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const { // This sets the vector or array dimension size that will // cause a warning. For now, these warnings are permanently // enabled. const long warn_dimension_size = 1 << 30; NetNet::Type wtype = type_; bool is_implicit_scalar = false; if (wtype == NetNet::IMPLICIT) { wtype = NetNet::WIRE; is_implicit_scalar = true; } if (wtype == NetNet::IMPLICIT_REG) { wtype = NetNet::REG; is_implicit_scalar = true; } unsigned wid = 1; vectorpacked_dimensions; des->errors += error_cnt_; // A signal can not have the same name as a scope object. const NetScope *child = scope->child_byname(name_); if (child) { cerr << get_fileline() << ": error: signal and "; child->print_type(cerr); cerr << " in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A signal can not have the same name as a genvar. const LineInfo *genvar = scope->find_genvar(name_); if (genvar) { cerr << get_fileline() << ": error: signal and genvar in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A signal can not have the same name as a parameter. Note // that we treat enumeration literals similar to parameters, // so if the name matches an enumeration literal, it will be // caught here. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(des, name_, ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: signal and parameter in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A signal can not have the same name as a named event. const NetEvent *event = scope->find_event(name_); if (event) { cerr << get_fileline() << ": error: signal and named event in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } if (port_set_ || net_set_) { if (warn_implicit_dimensions && port_set_ && net_set_ && net_.empty() && !port_.empty()) { cerr << get_fileline() << ": warning: " << "var/net declaration of " << basename() << " inherits dimensions from port declaration." << endl; } if (warn_implicit_dimensions && port_set_ && net_set_ && port_.empty() && net_.empty()) { cerr << get_fileline() << ": warning: " << "Port declaration of " << basename() << " inherits dimensions from var/net." << endl; } bool bad_range = false; vector plist, nlist; /* If they exist get the port definition MSB and LSB */ if (port_set_ && !port_.empty()) { if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Evaluate ranges for port " << basename() << endl; } bad_range |= evaluate_ranges(des, scope, plist, port_); nlist = plist; /* An implicit port can have a range so note that here. */ is_implicit_scalar = false; } assert(port_set_ || port_.empty()); /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && !net_.empty() && !bad_range) { nlist.clear(); if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Evaluate ranges for net " << basename() << endl; } bad_range |= evaluate_ranges(des, scope, nlist, net_); } assert(net_set_ || net_.empty()); if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Calculated ranges for " << basename() << ". Now check for consistency." << endl; } /* We have a port size error */ if (port_set_ && net_set_ && !test_ranges_eeq(plist, nlist)) { /* Scalar port with a vector net/etc. definition */ if (port_.empty()) { if (!gn_io_range_error_flag) { cerr << get_fileline() << ": warning: Scalar port ``" << name_ << "'' has a vectored net declaration " << nlist << "." << endl; } else { cerr << get_fileline() << ": error: Scalar port ``" << name_ << "'' has a vectored net declaration " << nlist << "." << endl; des->errors += 1; return 0; } } /* Vectored port with a scalar net/etc. definition */ if (net_.empty()) { cerr << port_.front().first->get_fileline() << ": error: Vectored port ``" << name_ << "'' " << plist << " has a scalar net declaration at " << get_fileline() << "." << endl; des->errors += 1; return 0; } /* Both vectored, but they have different ranges. */ if (!port_.empty() && !net_.empty()) { cerr << port_.front().first->get_fileline() << ": error: Vectored port ``" << name_ << "'' " << plist << " has a net declaration " << nlist << " at " << net_.front().first->get_fileline() << " that does not match." << endl; des->errors += 1; return 0; } } packed_dimensions = nlist; wid = netrange_width(packed_dimensions); if (wid > warn_dimension_size) { cerr << get_fileline() << ": warning: Vector size " "is greater than " << warn_dimension_size << "." << endl; } } unsigned nattrib = 0; attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib, des, scope); listunpacked_dimensions; netdarray_t*netdarray = 0; for (list::const_iterator cur = unpacked_.begin() ; cur != unpacked_.end() ; ++cur) { PExpr*use_lidx = cur->first; PExpr*use_ridx = cur->second; // Special case: If we encounter an undefined // dimensions, then turn this into a dynamic array and // put all the packed dimensions there. if (use_lidx==0 && use_ridx==0) { netvector_t*vec = new netvector_t(packed_dimensions, data_type_); vec->set_signed(get_signed()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); netdarray = new netdarray_t(vec); continue; } // Special case: Detect the mark for a QUEUE // declaration, which is the dimensions [null:]. if (use_ridx==0 && dynamic_cast(use_lidx)) { netvector_t*vec = new netvector_t(packed_dimensions, data_type_); vec->set_signed(get_signed()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); netdarray = new netqueue_t(vec); continue; } // Cannot handle dynamic arrays of arrays yet. ivl_assert(*this, netdarray==0); ivl_assert(*this, use_lidx && use_ridx); NetExpr*lexp = elab_and_eval(des, scope, use_lidx, -1, true); NetExpr*rexp = elab_and_eval(des, scope, use_ridx, -1, true); if ((lexp == 0) || (rexp == 0)) { cerr << get_fileline() << ": internal error: There is " << "a problem evaluating indices for ``" << name_ << "''." << endl; des->errors += 1; return 0; } bool const_flag = true; verinum lval, rval; const_flag &= get_const_argument(lexp, lval); const_flag &= get_const_argument(rexp, rval); delete rexp; delete lexp; long index_l, index_r; if (! const_flag) { cerr << get_fileline() << ": error: The indices " << "are not constant for array ``" << name_ << "''." << endl; des->errors += 1; /* Attempt to recover from error, */ index_l = 0; index_r = 0; } else { index_l = lval.as_long(); index_r = rval.as_long(); } if (abs(index_r - index_l) > warn_dimension_size) { cerr << get_fileline() << ": warning: Array dimension " "is greater than " << warn_dimension_size << "." << endl; } unpacked_dimensions.push_back(netrange_t(index_l, index_r)); } if (data_type_ == IVL_VT_REAL && !packed_dimensions.empty()) { cerr << get_fileline() << ": error: real "; if (wtype == NetNet::REG) cerr << "variable"; else cerr << "net"; cerr << " '" << name_ << "' cannot be declared as a vector, found a range " << packed_dimensions << "." << endl; des->errors += 1; return 0; } /* If the net type is supply0 or supply1, replace it with a simple wire with a pulldown/pullup with supply strength. In other words, transform: supply0 foo; to: wire foo; pulldown #(supply0) (foo); This reduces the backend burden, and behaves exactly the same. */ NetLogic*pull = 0; if (wtype == NetNet::SUPPLY0 || wtype == NetNet::SUPPLY1) { NetLogic::TYPE pull_type = (wtype==NetNet::SUPPLY1) ? NetLogic::PULLUP : NetLogic::PULLDOWN; pull = new NetLogic(scope, scope->local_symbol(), 1, pull_type, wid); pull->set_line(*this); pull->pin(0).drive0(IVL_DR_SUPPLY); pull->pin(0).drive1(IVL_DR_SUPPLY); des->add_node(pull); wtype = NetNet::WIRE; if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Generate a SUPPLY pull for the "; if (wtype == NetNet::SUPPLY0) cerr << "supply0"; else cerr << "supply1"; cerr << " net." << endl; } } NetNet*sig = 0; if (class_type_t*class_type = dynamic_cast(set_data_type_)) { // If this is a class variable, then the class type // should already have been elaborated. All we need to // do right now is locate the netclass_t object for the // class, and use that to build the net. ivl_assert(*this, class_type->save_elaborated_type); netclass_t*use_type = class_type->save_elaborated_type; sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else if (struct_type_t*struct_type = dynamic_cast(set_data_type_)) { // If this is a struct type, then build the net with the // struct type. ivl_type_s*tmp_type = struct_type->elaborate_type(des, scope); netstruct_t*use_type = dynamic_cast(tmp_type); if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype; if (use_type->packed()) cerr << " " << use_type->packed_width() << " bit packed struct "; else cerr << " struct <> "; cerr << name_; cerr << " in scope " << scope_path(scope) << endl; } sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else if (enum_type_t*enum_type = dynamic_cast(set_data_type_)) { list::const_iterator sample_name = enum_type->names->begin(); const netenum_t*use_enum = scope->find_enumeration_for_name(sample_name->name); if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype << " enumeration " << name_ << " in scope " << scope_path(scope) << " with packed_dimensions=" << packed_dimensions << " and packed_width=" << use_enum->packed_width() << endl; } ivl_assert(*this, packed_dimensions.empty()); sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_enum); } else if (netdarray) { if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype << " dynamic array " << name_ << " in scope " << scope_path(scope) << endl; } ivl_assert(*this, packed_dimensions.empty()); ivl_assert(*this, unpacked_dimensions.empty()); sig = new NetNet(scope, name_, wtype, netdarray); } else if (parray_type_t*parray_type = dynamic_cast(set_data_type_)) { // The pform gives us a parray_type_t for packed arrays // that show up in type definitions. This can be handled // a lot like packed dimensions from other means. // The trick here is that the parray type has an // arbitrary sub-type, and not just a scalar bit... netparray_t*use_type = elaborate_parray_type(des, scope, parray_type); // Should not be getting packed dimensions other than // through the parray type declaration. ivl_assert(*this, packed_dimensions.empty()); if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype << " parray=" << use_type->static_dimensions() << " " << name_ << unpacked_dimensions << " in scope " << scope_path(scope) << endl; } sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else { if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype; if (!get_scalar()) { cerr << " " << packed_dimensions; } cerr << " " << name_ << unpacked_dimensions; cerr << " in scope " << scope_path(scope) << endl; } ivl_variable_type_t use_data_type = data_type_; if (use_data_type == IVL_VT_NO_TYPE) { use_data_type = IVL_VT_LOGIC; if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Signal " << name_ << " in scope " << scope_path(scope) << " defaults to data type " << use_data_type << endl; } } netvector_t*vec = new netvector_t(packed_dimensions, use_data_type); vec->set_signed(get_signed()); vec->set_isint(get_isint()); if (is_implicit_scalar) vec->set_scalar(true); else vec->set_scalar(get_scalar()); packed_dimensions.clear(); sig = new NetNet(scope, name_, wtype, unpacked_dimensions, vec); } if (wtype == NetNet::WIRE) sig->devirtualize_pins(); sig->set_line(*this); sig->port_type(port_type_); if (ivl_discipline_t dis = get_discipline()) { sig->set_discipline(dis); } if (pull) connect(sig->pin(0), pull->pin(0)); for (unsigned idx = 0 ; idx < nattrib ; idx += 1) sig->attribute(attrib_list[idx].key, attrib_list[idx].val); return sig; } void Design::root_elaborate_sig(void) { for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++ cur) { netclass_t*cur_class = cur->second; PClass*cur_pclass = class_to_pclass_[cur_class]; cur_class->elaborate_sig(this, cur_pclass); } for (map::iterator cur = root_tasks_.begin() ; cur != root_tasks_.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": root_elaborate_sig: " << "Elaborate_sig for root task/func " << scope_path(cur->first) << endl; } cur->second->elaborate_sig(this, cur->first); } } iverilog-10_1/elab_sig_analog.cc000066400000000000000000000016571265551621300167500ustar00rootroot00000000000000/* * Copyright (c) 2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "AStatement.h" # include # include iverilog-10_1/elab_type.cc000066400000000000000000000163161265551621300156240ustar00rootroot00000000000000/* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform_types.h" # include "netlist.h" # include "netclass.h" # include "netdarray.h" # include "netenum.h" # include "netparray.h" # include "netscalar.h" # include "netstruct.h" # include "netvector.h" # include "netmisc.h" # include # include "ivl_assert.h" using namespace std; /* * Some types have a list of ranges that need to be elaborated. This * function elaborates the ranges referenced by "dims" into the vector * "ranges". */ static void elaborate_array_ranges(Design*des, NetScope*scope, vector&ranges, const list*dims) { if (dims == 0) return; for (list::const_iterator cur = dims->begin() ; cur != dims->end() ; ++ cur) { NetExpr*me = elab_and_eval(des, scope, cur->first, 0, true); NetExpr*le = elab_and_eval(des, scope, cur->second, 0, true); /* If elaboration failed for either expression, we should have already reported the error, so just skip the following evaluation to recover. */ long mnum = 0, lnum = 0; if ( me && ! eval_as_long(mnum, me) ) { assert(0); des->errors += 1; } if ( le && ! eval_as_long(lnum, le) ) { assert(0); des->errors += 1; } ranges.push_back(netrange_t(mnum, lnum)); } } /* * Elaborations of types may vary depending on the scope that it is * done in, so keep a per-scope cache of the results. */ ivl_type_s* data_type_t::elaborate_type(Design*des, NetScope*scope) { Definitions*use_definitions = scope; if (use_definitions == 0) use_definitions = des; map::iterator pos = cache_type_elaborate_.lower_bound(use_definitions); if (pos != cache_type_elaborate_.end() && pos->first == use_definitions) return pos->second; ivl_type_s*tmp = elaborate_type_raw(des, scope); cache_type_elaborate_.insert(pos, pair(scope, tmp)); return tmp; } ivl_type_s* data_type_t::elaborate_type_raw(Design*des, NetScope*) const { cerr << get_fileline() << ": internal error: " << "Elaborate method not implemented for " << typeid(*this).name() << "." << endl; des->errors += 1; return 0; } ivl_type_s* atom2_type_t::elaborate_type_raw(Design*des, NetScope*) const { switch (type_code) { case 64: if (signed_flag) return &netvector_t::atom2s64; else return &netvector_t::atom2u64; case 32: if (signed_flag) return &netvector_t::atom2s32; else return &netvector_t::atom2u32; case 16: if (signed_flag) return &netvector_t::atom2s16; else return &netvector_t::atom2u16; case 8: if (signed_flag) return &netvector_t::atom2s8; else return &netvector_t::atom2u8; default: cerr << get_fileline() << ": internal error: " << "atom2_type_t type_code=" << type_code << "." << endl; des->errors += 1; return 0; } } ivl_type_s* class_type_t::elaborate_type_raw(Design*, NetScope*) const { ivl_assert(*this, save_elaborated_type); return save_elaborated_type; } /* * elaborate_type_raw for enumerations is actually mostly performed * during scope elaboration so that the enumeration literals are * available at the right time. At that time, the netenum_t* object is * stashed in the scope so that I can retrieve it here. */ ivl_type_s* enum_type_t::elaborate_type_raw(Design*des, NetScope*scope) const { ivl_assert(*this, scope); ivl_type_s*tmp = scope->enumeration_for_key(this); if (tmp) return tmp; tmp = des->enumeration_for_key(this); return tmp; } ivl_type_s* vector_type_t::elaborate_type_raw(Design*des, NetScope*scope) const { vector packed; elaborate_array_ranges(des, scope, packed, pdims.get()); netvector_t*tmp = new netvector_t(packed, base_type); tmp->set_signed(signed_flag); tmp->set_isint(integer_flag); return tmp; } ivl_type_s* real_type_t::elaborate_type_raw(Design*, NetScope*) const { switch (type_code) { case REAL: return &netreal_t::type_real; case SHORTREAL: return &netreal_t::type_shortreal; } return 0; } ivl_type_s* string_type_t::elaborate_type_raw(Design*, NetScope*) const { return &netstring_t::type_string; } ivl_type_s* parray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const { vectorpacked; elaborate_array_ranges(des, scope, packed, dims.get()); ivl_type_t etype = base_type->elaborate_type(des, scope); return new netparray_t(packed, etype); } netstruct_t* struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const { netstruct_t*res = new netstruct_t; res->packed(packed_flag); if (union_flag) res->union_flag(true); for (list::iterator cur = members->begin() ; cur != members->end() ; ++ cur) { // Elaborate the type of the member. struct_member_t*curp = *cur; ivl_type_t mem_vec = curp->type->elaborate_type(des, scope); if (mem_vec == 0) continue; // There may be several names that are the same type: // name1, name2, ...; // Process all the member, and give them a type. for (list::iterator name = curp->names->begin() ; name != curp->names->end() ; ++ name) { decl_assignment_t*namep = *name; netstruct_t::member_t memb; memb.name = namep->name; memb.net_type = mem_vec; res->append_member(des, memb); } } return res; } ivl_type_s* uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const { ivl_type_t btype = base_type->elaborate_type(des, scope); assert(dims->size() >= 1); list::const_iterator cur = dims->begin(); // Special case: if the dimension is nil:nil, this is a // dynamic array. Note that we only know how to handle dynamic // arrays with 1 dimension at a time. if (cur->first==0 && cur->second==0) { assert(dims->size()==1); ivl_type_s*res = new netdarray_t(btype); return res; } vector dimensions; bool bad_range = evaluate_ranges(des, scope, dimensions, *dims); if (bad_range) { cerr << get_fileline() << " : warning: " << "Bad dimensions for type here." << endl; } ivl_assert(*this, btype); ivl_type_s*res = new netuarray_t(dimensions, btype); return res; } iverilog-10_1/elaborate.cc000066400000000000000000005777261265551621300156370ustar00rootroot00000000000000/* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * Elaboration takes as input a complete parse tree and the name of a * root module, and generates as output the elaborated design. This * elaborated design is presented as a Module, which does not * reference any other modules. It is entirely self contained. */ # include # include # include # include # include "pform.h" # include "PClass.h" # include "PEvent.h" # include "PGenerate.h" # include "PPackage.h" # include "PSpec.h" # include "netlist.h" # include "netenum.h" # include "netvector.h" # include "netdarray.h" # include "netparray.h" # include "netclass.h" # include "netmisc.h" # include "util.h" # include "parse_api.h" # include "compiler.h" # include "ivl_assert.h" void PGate::elaborate(Design*, NetScope*) const { cerr << "internal error: what kind of gate? " << typeid(*this).name() << endl; } /* * Elaborate the continuous assign. (This is *not* the procedural * assign.) Elaborate the lvalue and rvalue, and do the assignment. */ void PGAssign::elaborate(Design*des, NetScope*scope) const { assert(scope); NetExpr* rise_time, *fall_time, *decay_time; eval_delays(des, scope, rise_time, fall_time, decay_time, true); ivl_drive_t drive0 = strength0(); ivl_drive_t drive1 = strength1(); assert(pin(0)); assert(pin(1)); /* Elaborate the l-value. */ NetNet*lval = pin(0)->elaborate_lnet(des, scope); if (lval == 0) { return; } // If this turns out to be an assignment to an unpacked array, // then handle that special case elsewhere. if (lval->pin_count() > 1) { elaborate_unpacked_array_(des, scope, lval); return; } ivl_assert(*this, lval->pin_count() == 1); if (debug_elaborate) { cerr << get_fileline() << ": PGAssign::elaborate: elaborated l-value" << " width=" << lval->vector_width() << ", pin_count=" << lval->pin_count() << endl; } NetExpr*rval_expr = elaborate_rval_expr(des, scope, lval->net_type(), lval->data_type(), lval->vector_width(), pin(1)); if (rval_expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate r-value: " << *pin(1) << endl; des->errors += 1; return; } #if 0 // MTW, 01-Mar-2013. The expression elaboration rework should have // ensured that this can no longer occur. Leaving this here for the // moment, but it should be safe to remove it. if (type_is_vectorable(rval_expr->expr_type()) && type_is_vectorable(lval->data_type()) && rval_expr->expr_width() < lval->vector_width()) { if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "r-value expressions width "<expr_width() << " of " << (rval_expr->has_sign()? "signed":"unsigned") << " expression is to small for l-value width " << lval->vector_width() << "." << endl; } rval_expr = pad_to_width(rval_expr, lval->vector_width(), *this); } #endif NetNet*rval = rval_expr->synthesize(des, scope, rval_expr); if (rval == 0) { cerr << get_fileline() << ": internal error: " << "Failed to synthesize expression: " << *rval_expr << endl; des->errors += 1; return; } if (debug_elaborate) { cerr << get_fileline() << ": debug: PGAssign: elaborated r-value" << " width="<< rval->vector_width() << ", type="<< rval->data_type() << ", expr=" << *rval_expr << endl; } ivl_assert(*this, lval && rval); ivl_assert(*this, rval->pin_count() == 1); // Detect the case that the rvalue-expression is a simple // expression. In this case, we will need to create a driver // (later) to carry strengths. bool need_driver_flag = false; if (dynamic_cast(rval_expr)) need_driver_flag = true; // expression elaboration should have caused the rval width to // match the l-value by now. if (rval->vector_width() < lval->vector_width()) { cerr << get_fileline() << ": internal error: " << "lval-rval width mismatch: " << "rval->vector_width()==" << rval->vector_width() << ", lval->vector_width()==" << lval->vector_width() << endl; } ivl_assert(*this, rval->vector_width() >= lval->vector_width()); /* If the r-value insists on being larger than the l-value, use a part select to chop it down down to size. */ if (lval->vector_width() < rval->vector_width()) { NetPartSelect*tmp = new NetPartSelect(rval, 0,lval->vector_width(), NetPartSelect::VP); des->add_node(tmp); tmp->set_line(*this); netvector_t*osig_vec = new netvector_t(rval->data_type(), lval->vector_width()-1,0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, osig_vec); osig->set_line(*this); osig->local_flag(true); connect(osig->pin(0), tmp->pin(0)); rval = osig; need_driver_flag = false; } /* When we are given a non-default strength value and if the drive * source is a bit, part, indexed select or a concatenation we need * to add a driver (BUFZ) to convey the strength information. */ if ((drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG) && ((dynamic_cast(rval_expr)) || (dynamic_cast(rval_expr)))) { need_driver_flag = true; } if (need_driver_flag) { NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(), rval->vector_width(), false); driver->set_line(*this); des->add_node(driver); connect(rval->pin(0), driver->pin(1)); netvector_t*tmp_vec = new netvector_t(rval->data_type(), rval->vector_width()-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*this); tmp->local_flag(true); connect(driver->pin(0), tmp->pin(0)); rval = tmp; } /* Set the drive and delays for the r-val. */ if (drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG) rval->pin(0).drivers_drive(drive0, drive1); if (rise_time || fall_time || decay_time) rval->pin(0).drivers_delays(rise_time, fall_time, decay_time); connect(lval->pin(0), rval->pin(0)); if (lval->local_flag()) delete lval; } void PGAssign::elaborate_unpacked_array_(Design*des, NetScope*scope, NetNet*lval) const { PEIdent*rval_pident = dynamic_cast (pin(1)); ivl_assert(*this, rval_pident); NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope); ivl_assert(*this, rval_net->pin_count() == lval->pin_count()); assign_unpacked_with_bufz(des, scope, this, lval, rval_net); } unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, long&high, long&low) const { unsigned count = 1; high = 0; low = 0; /* If the Verilog source has a range specification for the gates, then I am expected to make more than one gate. Figure out how many are desired. */ if (msb_) { NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true); NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true); NetEConst*msb_con = dynamic_cast(msb_exp); NetEConst*lsb_con = dynamic_cast(lsb_exp); if (msb_con == 0) { cerr << get_fileline() << ": error: Unable to evaluate " "expression " << *msb_ << endl; des->errors += 1; return 0; } if (lsb_con == 0) { cerr << get_fileline() << ": error: Unable to evaluate " "expression " << *lsb_ << endl; des->errors += 1; return 0; } verinum msb = msb_con->value(); verinum lsb = lsb_con->value(); delete msb_exp; delete lsb_exp; if (msb.as_long() > lsb.as_long()) count = msb.as_long() - lsb.as_long() + 1; else count = lsb.as_long() - msb.as_long() + 1; low = lsb.as_long(); high = msb.as_long(); if (debug_elaborate) { cerr << get_fileline() << ": debug: PGBuiltin: Make array " << "[" << high << ":" << low << "]" << " of " << count << " gates for " << get_name() << endl; } } return count; } void PGBuiltin::calculate_gate_and_lval_count_(unsigned&gate_count, unsigned&lval_count) const { switch (type()) { case BUF: case NOT: if (pin_count() > 2) gate_count = pin_count() - 1; else gate_count = 1; lval_count = gate_count; break; case PULLDOWN: case PULLUP: gate_count = pin_count(); lval_count = gate_count; break; case TRAN: case RTRAN: case TRANIF0: case TRANIF1: case RTRANIF0: case RTRANIF1: gate_count = 1; lval_count = 2; break; default: gate_count = 1; lval_count = 1; break; } } NetNode* PGBuiltin::create_gate_for_output_(Design*des, NetScope*scope, perm_string inst_name, unsigned instance_width) const { NetNode*gate = 0; switch (type()) { case AND: if (pin_count() < 2) { cerr << get_fileline() << ": error: the AND " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::AND, instance_width); } break; case BUF: if (pin_count() < 2) { cerr << get_fileline() << ": error: the BUF " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, 2, NetLogic::BUF, instance_width); } break; case BUFIF0: if (pin_count() != 3) { cerr << get_fileline() << ": error: the BUFIF0 " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::BUFIF0, instance_width); } break; case BUFIF1: if (pin_count() != 3) { cerr << get_fileline() << ": error: the BUFIF1 " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::BUFIF1, instance_width); } break; case CMOS: if (pin_count() != 4) { cerr << get_fileline() << ": error: the CMOS " "primitive must have four arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::CMOS, instance_width); } break; case NAND: if (pin_count() < 2) { cerr << get_fileline() << ": error: the NAND " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::NAND, instance_width); } break; case NMOS: if (pin_count() != 3) { cerr << get_fileline() << ": error: the NMOS " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::NMOS, instance_width); } break; case NOR: if (pin_count() < 2) { cerr << get_fileline() << ": error: the NOR " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::NOR, instance_width); } break; case NOT: if (pin_count() < 2) { cerr << get_fileline() << ": error: the NOT " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, 2, NetLogic::NOT, instance_width); } break; case NOTIF0: if (pin_count() != 3) { cerr << get_fileline() << ": error: the NOTIF0 " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::NOTIF0, instance_width); } break; case NOTIF1: if (pin_count() != 3) { cerr << get_fileline() << ": error: the NOTIF1 " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::NOTIF1, instance_width); } break; case OR: if (pin_count() < 2) { cerr << get_fileline() << ": error: the OR " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::OR, instance_width); } break; case RCMOS: if (pin_count() != 4) { cerr << get_fileline() << ": error: the RCMOS " "primitive must have four arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::RCMOS, instance_width); } break; case RNMOS: if (pin_count() != 3) { cerr << get_fileline() << ": error: the RNMOS " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::RNMOS, instance_width); } break; case RPMOS: if (pin_count() != 3) { cerr << get_fileline() << ": error: the RPMOS " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::RPMOS, instance_width); } break; case PMOS: if (pin_count() != 3) { cerr << get_fileline() << ": error: the PMOS " "primitive must have three arguments." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::PMOS, instance_width); } break; case PULLDOWN: gate = new NetLogic(scope, inst_name, 1, NetLogic::PULLDOWN, instance_width); break; case PULLUP: gate = new NetLogic(scope, inst_name, 1, NetLogic::PULLUP, instance_width); break; case XNOR: if (pin_count() < 2) { cerr << get_fileline() << ": error: the XNOR " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::XNOR, instance_width); } break; case XOR: if (pin_count() < 2) { cerr << get_fileline() << ": error: the XOR " "primitive must have an input." << endl; des->errors += 1; } else { gate = new NetLogic(scope, inst_name, pin_count(), NetLogic::XOR, instance_width); } break; case TRAN: if (pin_count() != 2) { cerr << get_fileline() << ": error: Pin count for " << "tran device." << endl; des->errors += 1; } else { gate = new NetTran(scope, inst_name, IVL_SW_TRAN, instance_width); } break; case RTRAN: if (pin_count() != 2) { cerr << get_fileline() << ": error: Pin count for " << "rtran device." << endl; des->errors += 1; } else { gate = new NetTran(scope, inst_name, IVL_SW_RTRAN, instance_width); } break; case TRANIF0: if (pin_count() != 3) { cerr << get_fileline() << ": error: Pin count for " << "tranif0 device." << endl; des->errors += 1; } else { gate = new NetTran(scope, inst_name, IVL_SW_TRANIF0, instance_width); } break; case RTRANIF0: if (pin_count() != 3) { cerr << get_fileline() << ": error: Pin count for " << "rtranif0 device." << endl; des->errors += 1; } else { gate = new NetTran(scope, inst_name, IVL_SW_RTRANIF0, instance_width); } break; case TRANIF1: if (pin_count() != 3) { cerr << get_fileline() << ": error: Pin count for " << "tranif1 device." << endl; des->errors += 1; } else { gate = new NetTran(scope, inst_name, IVL_SW_TRANIF1, instance_width); } break; case RTRANIF1: if (pin_count() != 3) { cerr << get_fileline() << ": error: Pin count for " << "rtranif1 device." << endl; des->errors += 1; } else { gate = new NetTran(scope, inst_name, IVL_SW_RTRANIF1, instance_width); } break; default: cerr << get_fileline() << ": internal error: unhandled " "gate type." << endl; des->errors += 1; break; } return gate; } bool PGBuiltin::check_delay_count(Design*des) const { switch (type()) { case AND: case NAND: case OR: case NOR: case XOR: case XNOR: case BUF: case NOT: if (delay_count() > 2) { cerr << get_fileline() << ": error: More than two delays " << "given to a " << gate_name() << " gate." << endl; des->errors += 1; return true; } break; case BUFIF0: case NOTIF0: case BUFIF1: case NOTIF1: if (delay_count() > 3) { cerr << get_fileline() << ": error: More than three delays " << "given to a " << gate_name() << " gate." << endl; des->errors += 1; return true; } break; case NMOS: case RNMOS: case PMOS: case RPMOS: case CMOS: case RCMOS: if (delay_count() > 3) { cerr << get_fileline() << ": error: More than three delays " << "given to a " << gate_name() << " switch." << endl; des->errors += 1; return true; } break; case TRAN: case RTRAN: if (delay_count() != 0) { cerr << get_fileline() << ": error: A " << gate_name() << " switch does not take any delays." << endl; des->errors += 1; return true; } break; case TRANIF0: case TRANIF1: if (delay_count() > 2) { cerr << get_fileline() << ": error: More than two delays " << "given to a " << gate_name() << " switch." << endl; des->errors += 1; return true; } break; case RTRANIF0: case RTRANIF1: if (delay_count() > 2) { cerr << get_fileline() << ": error: More than two delays " << "given to an " << gate_name() << " switch." << endl; des->errors += 1; return true; } break; case PULLUP: case PULLDOWN: if (delay_count() != 0) { cerr << get_fileline() << ": error: A " << gate_name() << " source does not take any delays." << endl; des->errors += 1; return true; } break; default: cerr << get_fileline() << ": internal error: unhandled " "gate type." << endl; des->errors += 1; return true; break; } return false; } /* * Elaborate a Builtin gate. These normally get translated into * NetLogic nodes that reflect the particular logic function. */ void PGBuiltin::elaborate(Design*des, NetScope*scope) const { unsigned instance_width = 1; perm_string name = get_name(); if (name == "") name = scope->local_symbol(); /* Calculate the array bounds and instance count for the gate, as described in the Verilog source. If there is none, then the count is 1, and high==low==0. */ long low=0, high=0; unsigned array_count = calculate_array_count_(des, scope, high, low); if (array_count == 0) return; unsigned gate_count = 0, lval_count = 0; calculate_gate_and_lval_count_(gate_count, lval_count); /* Now we have a gate count. Elaborate the lval (output or bi-directional) expressions only. We do it early so that we can see if we can make wide gates instead of an array of gates. */ vectorlval_sigs (lval_count); for (unsigned idx = 0 ; idx < lval_count ; idx += 1) { if (pin(idx) == 0) { cerr << get_fileline() << ": error: Logic gate port " "expressions are not optional." << endl; des->errors += 1; return; } if (lval_count > gate_count) lval_sigs[idx] = pin(idx)->elaborate_bi_net(des, scope); else lval_sigs[idx] = pin(idx)->elaborate_lnet(des, scope); // The only way this should return zero is if an error // happened, so for that case just return. if (lval_sigs[idx] == 0) return; // For now, assume all the outputs are the same width. ivl_assert(*this, idx == 0 || lval_sigs[idx]->vector_width() == lval_sigs[0]->vector_width()); } /* Detect the special case that the l-value width exactly matches the gate count. In this case, we will make a single gate that has the desired vector width. NOTE: This assumes that all the outputs have the same width. For gates with 1 output, this is trivially true. */ if (lval_sigs[0]->vector_width() == array_count) { instance_width = array_count; array_count = 1; if (debug_elaborate && instance_width != 1) cerr << get_fileline() << ": debug: PGBuiltin: " "Collapsed gate array into single wide " "(" << instance_width << ") instance." << endl; } /* Calculate the gate delays from the delay expressions given in the source. For logic gates, the decay time is meaningless because it can never go to high impedance. However, the bufif devices can generate 'bz output, so we will pretend that anything can. If only one delay value expression is given (i.e., #5 nand(foo,...)) then rise, fall and decay times are all the same value. If two values are given, rise and fall times are use, and the decay time is the minimum of the rise and fall times. Finally, if all three values are given, they are taken as specified. */ if (check_delay_count(des)) return; NetExpr* rise_time, *fall_time, *decay_time; eval_delays(des, scope, rise_time, fall_time, decay_time); struct attrib_list_t*attrib_list; unsigned attrib_list_n = 0; attrib_list = evaluate_attributes(attributes, attrib_list_n, des, scope); /* Allocate all the netlist nodes for the gates. */ vectorcur (array_count*gate_count); /* Now make as many gates as the bit count dictates. Give each a unique name, and set the delay times. */ for (unsigned idx = 0 ; idx < array_count*gate_count ; idx += 1) { unsigned array_idx = idx/gate_count; unsigned gate_idx = idx%gate_count; ostringstream tmp; unsigned index = (low < high)? (low+array_idx) : (low-array_idx); tmp << name << "<" << index << "." << gate_idx << ">"; perm_string inm = lex_strings.make(tmp.str()); cur[idx] = create_gate_for_output_(des, scope, inm, instance_width); if (cur[idx] == 0) return; for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1) cur[idx]->attribute(attrib_list[adx].key, attrib_list[adx].val); /* Set the delays and drive strength for all built in gates. */ cur[idx]->rise_time(rise_time); cur[idx]->fall_time(fall_time); cur[idx]->decay_time(decay_time); cur[idx]->pin(0).drive0(strength0()); cur[idx]->pin(0).drive1(strength1()); cur[idx]->set_line(*this); des->add_node(cur[idx]); } delete[]attrib_list; /* The gates have all been allocated, this loop runs through the parameters and attaches the ports of the objects. */ for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { PExpr*ex = pin(idx); if (ex == 0) { cerr << get_fileline() << ": error: Logic gate port " "expressions are not optional." << endl; des->errors += 1; return; } NetNet*sig = 0; if (idx < lval_count) { sig = lval_sigs[idx]; } else { // If this is an array, the port expression is required // to be the exact width required (this will be checked // later). But if this is a single instance, consensus // is that we just take the LSB of the port expression. NetExpr*tmp = elab_and_eval(des, scope, ex, msb_ ? -1 : 1); if (tmp == 0) continue; if (msb_ == 0 && tmp->expr_width() != 1) tmp = new NetESelect(tmp, make_const_0(1), 1, IVL_SEL_IDX_UP); sig = tmp->synthesize(des, scope, tmp); delete tmp; } if (sig == 0) continue; ivl_assert(*this, sig); if (array_count == 1) { /* Handle the case where there is one gate that carries the whole vector width. */ if (1 == sig->vector_width() && instance_width != 1) { assert(sig->vector_width() == 1); NetReplicate*rep = new NetReplicate(scope, scope->local_symbol(), instance_width, instance_width); rep->set_line(*this); des->add_node(rep); connect(rep->pin(1), sig->pin(0)); netvector_t*osig_vec = new netvector_t(IVL_VT_LOGIC, instance_width-1,0); sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, osig_vec); sig->set_line(*this); sig->local_flag(true); connect(rep->pin(0), sig->pin(0)); } if (instance_width != sig->vector_width()) { cerr << get_fileline() << ": error: " << "Expression width " << sig->vector_width() << " does not match width " << instance_width << " of logic gate array port " << idx+1 << "." << endl; des->errors += 1; } // There is only 1 instance, but there may be // multiple outputs to that gate. That would // potentially mean multiple actual gates. // Although in Verilog proper a multiple // output gate has only 1 input, this conditional // handles gates with N outputs and M inputs. if (idx < gate_count) { connect(cur[idx]->pin(0), sig->pin(0)); } else { for (unsigned dev = 0 ; dev < gate_count; dev += 1) connect(cur[dev]->pin(idx-gate_count+1), sig->pin(0)); } } else if (sig->vector_width() == 1) { /* Handle the case where a single bit is connected repetitively to all the instances. If idx is an output port, connect it to all array_count devices that have outputs at this position. Otherwise, idx is an input to all array_count*gate_count devices. */ if (idx < gate_count) { for (unsigned gdx = 0 ; gdx < array_count ; gdx += 1) { unsigned dev = gdx*gate_count; connect(cur[dev+idx]->pin(0), sig->pin(0)); } } else { unsigned use_idx = idx - gate_count + 1; for (unsigned gdx = 0 ; gdx < cur.size() ; gdx += 1) connect(cur[gdx]->pin(use_idx), sig->pin(0)); } } else if (sig->vector_width() == array_count) { /* Bi-directional switches should get collapsed into a single wide instance, so should never reach this point. Check this is so, as the following code doesn't handle bi-directional connections. */ ivl_assert(*this, lval_count == gate_count); /* Handle the general case that each bit of the value is connected to a different instance. In this case, the output is handled slightly different from the inputs. */ if (idx < gate_count) { NetConcat*cc = new NetConcat(scope, scope->local_symbol(), sig->vector_width(), array_count); cc->set_line(*this); des->add_node(cc); /* Connect the concat to the signal. */ connect(cc->pin(0), sig->pin(0)); /* Connect the outputs of the gates to the concat. */ for (unsigned gdx = 0 ; gdx < array_count; gdx += 1) { unsigned dev = gdx*gate_count; connect(cur[dev+idx]->pin(0), cc->pin(gdx+1)); netvector_t*tmp2_vec = new netvector_t(IVL_VT_LOGIC); NetNet*tmp2 = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp2_vec); tmp2->set_line(*this); tmp2->local_flag(true); connect(cc->pin(gdx+1), tmp2->pin(0)); } } else for (unsigned gdx = 0 ; gdx < array_count ; gdx += 1) { /* Use part selects to get the bits connected to the inputs of out gate. */ NetPartSelect*tmp1 = new NetPartSelect(sig, gdx, 1, NetPartSelect::VP); tmp1->set_line(*this); des->add_node(tmp1); connect(tmp1->pin(1), sig->pin(0)); netvector_t*tmp2_vec = new netvector_t(sig->data_type()); NetNet*tmp2 = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp2_vec); tmp2->set_line(*this); tmp2->local_flag(true); connect(tmp1->pin(0), tmp2->pin(0)); unsigned use_idx = idx - gate_count + 1; unsigned dev = gdx*gate_count; for (unsigned gdx2 = 0 ; gdx2 < gate_count ; gdx2 += 1) connect(cur[dev+gdx2]->pin(use_idx), tmp1->pin(0)); } } else { cerr << get_fileline() << ": error: Gate count of " << array_count << " does not match net width of " << sig->vector_width() << " at pin " << idx << "." << endl; des->errors += 1; } } } NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope, NetNet*sig, unsigned port_wid, NetNet::PortType dir, bool as_signed) const { ivl_assert(*this, dir != NetNet::NOT_A_PORT); ivl_assert(*this, dir != NetNet::PIMPLICIT); netvector_t*tmp_type = new netvector_t(IVL_VT_LOGIC, port_wid-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_type); tmp->local_flag(true); tmp->set_line(*this); // Handle the special case of a bi-directional part // select. Create a NetTran(VP) instead of a uni-directional // NetPartSelect node. if (dir == NetNet::PINOUT) { unsigned wida = sig->vector_width(); unsigned widb = tmp->vector_width(); bool part_b = widb < wida; // This needs to pad the value! // Also delete the inout specific warning when this is fixed. // It is located just before this routine is called. NetTran*node = new NetTran(scope, scope->local_symbol(), part_b? wida : widb, part_b? widb : wida, 0); if (part_b) { connect(node->pin(0), sig->pin(0)); connect(node->pin(1), tmp->pin(0)); } else { connect(node->pin(0), tmp->pin(0)); connect(node->pin(1), sig->pin(0)); } node->set_line(*this); des->add_node(node); return tmp; } unsigned pwidth = tmp->vector_width(); unsigned swidth = sig->vector_width(); switch (dir) { case NetNet::POUTPUT: if (pwidth > swidth) { NetPartSelect*node = new NetPartSelect(tmp, 0, swidth, NetPartSelect::VP); connect(node->pin(0), sig->pin(0)); des->add_node(node); } else { NetNet*osig; if (as_signed) { osig = pad_to_width_signed(des, tmp, swidth, *this); } else { osig = pad_to_width(des, tmp, swidth, *this); } connect(osig->pin(0), sig->pin(0)); } break; case NetNet::PINPUT: if (pwidth > swidth) { delete tmp; if (as_signed) { tmp = pad_to_width_signed(des, sig, pwidth, *this); } else { tmp = pad_to_width(des, sig, pwidth, *this); } } else { NetPartSelect*node = new NetPartSelect(sig, 0, pwidth, NetPartSelect::VP); connect(node->pin(0), tmp->pin(0)); des->add_node(node); } break; case NetNet::PINOUT: ivl_assert(*this, 0); break; case NetNet::PREF: ivl_assert(*this, 0); break; default: ivl_assert(*this, 0); } return tmp; } static bool need_bufz_for_input_port(const vector&prts) { if (prts[0]->port_type() != NetNet::PINPUT) return false; if (prts[0]->pin(0).nexus()->drivers_present()) return true; return false; } /* * Convert a wire or tri to a tri0 or tri1 as needed to make * an unconnected drive pull for floating inputs. */ static void convert_net(Design*des, const LineInfo *line, NetNet *net, NetNet::Type type) { // If the types already match just return. if (net->type() == type) return; // We can only covert a wire or tri to have a default pull. if (net->type() == NetNet::WIRE || net->type() == NetNet::TRI) { net->type(type); return; } // We may have to support this at some point in time! cerr << line->get_fileline() << ": sorry: Can not pull floating " "input type '" << net->type() << "'." << endl; des->errors += 1; } /* * Instantiate a module by recursively elaborating it. Set the path of * the recursive elaboration so that signal names get properly * set. Connect the ports of the instantiated module to the signals of * the parameters. This is done with BUFZ gates so that they look just * like continuous assignment connections. */ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const { assert(scope); if (debug_elaborate) { cerr << get_fileline() << ": debug: Instantiate module " << rmod->mod_name() << " with instance name " << get_name() << " in scope " << scope_path(scope) << endl; } // This is the array of pin expressions, shuffled to match the // order of the declaration. If the source instantiation uses // bind by order, this is the same as the source list. Otherwise, // the source list is rearranged by name binding into this list. vectorpins (rmod->port_count()); vectorpins_fromwc (rmod->port_count(), false); // If the instance has a pins_ member, then we know we are // binding by name. Therefore, make up a pins array that // reflects the positions of the named ports. if (pins_) { unsigned nexp = rmod->port_count(); // Scan the bindings, matching them with port names. for (unsigned idx = 0 ; idx < npins_ ; idx += 1) { // Handle wildcard named port if (pins_[idx].name[0] == '*') { for (unsigned j = 0 ; j < nexp ; j += 1) { if (!pins[j]) { pins_fromwc[j] = true; NetNet* net = 0; const NetExpr*par = 0; NetEvent* eve = 0; pform_name_t path_; path_.push_back(name_component_t(rmod->ports[j]->name)); symbol_search(this, des, scope, path_, net, par, eve); if (net != 0) { pins[j] = new PEIdent(rmod->ports[j]->name, true); pins[j]->set_lineno(get_lineno()); pins[j]->set_file(get_file()); } } } continue; } // Given a binding, look at the module port names // for the position that matches the binding name. unsigned pidx = rmod->find_port(pins_[idx].name); // If the port name doesn't exist, the find_port // method will return the port count. Detect that // as an error. if (pidx == nexp) { cerr << get_fileline() << ": error: port ``" << pins_[idx].name << "'' is not a port of " << get_name() << "." << endl; des->errors += 1; continue; } // If I am overriding a wildcard port, delete and // override it if (pins_fromwc[pidx]) { delete pins[pidx]; pins_fromwc[pidx] = false; // If I already explicitly bound something to // this port, then the pins array will already // have a pointer value where I want to place this // expression. } else if (pins[pidx]) { cerr << get_fileline() << ": error: port ``" << pins_[idx].name << "'' already bound." << endl; des->errors += 1; continue; } // OK, do the binding by placing the expression in // the right place. pins[pidx] = pins_[idx].parm; } } else if (pin_count() == 0) { /* Handle the special case that no ports are connected. It is possible that this is an empty connect-by-name list, so we'll allow it and assume that is the case. */ for (unsigned idx = 0 ; idx < rmod->port_count() ; idx += 1) pins[idx] = 0; } else { /* Otherwise, this is a positional list of port connections. In this case, the port count must be right. Check that is is, the get the pin list. */ if (pin_count() != rmod->port_count()) { cerr << get_fileline() << ": error: Wrong number " "of ports. Expecting " << rmod->port_count() << ", got " << pin_count() << "." << endl; des->errors += 1; return; } // No named bindings, just use the positional list I // already have. assert(pin_count() == rmod->port_count()); pins = get_pins(); } // Elaborate these instances of the module. The recursive // elaboration causes the module to generate a netlist with // the ports represented by NetNet objects. I will find them // later. NetScope::scope_vec_t&instance = scope->instance_arrays[get_name()]; if (debug_elaborate) cerr << get_fileline() << ": debug: start " "recursive elaboration of " << instance.size() << " instance(s) of " << get_name() << "..." << endl; for (unsigned inst = 0 ; inst < instance.size() ; inst += 1) { rmod->elaborate(des, instance[inst]); instance[inst]->set_num_ports( rmod->port_count() ); } if (debug_elaborate) cerr << get_fileline() << ": debug: ...done." << endl; // Now connect the ports of the newly elaborated designs to // the expressions that are the instantiation parameters. Scan // the pins, elaborate the expressions attached to them, and // bind them to the port of the elaborated module. // This can get rather complicated because the port can be // unconnected (meaning an empty parameter is passed) connected // to a concatenation, or connected to an internally // unconnected port. for (unsigned idx = 0 ; idx < pins.size() ; idx += 1) { bool unconnected_port = false; perm_string port_name = rmod->get_port_name(idx); // Skip unconnected module ports. This happens when a // null parameter is passed in. if (pins[idx] == 0) { if (pins_fromwc[idx]) { cerr << get_fileline() << ": error: Wildcard named " "port connection (.*) did not find a matching " "identifier for port " << (idx+1) << " (" << port_name << ")." << endl; des->errors += 1; return; } // We need this information to support the // unconnected_drive directive and for a // unconnected input warning when asked for. vector mport = rmod->get_port(idx); if (mport.empty()) continue; perm_string pname = peek_tail_name(mport[0]->path()); NetNet*tmp = instance[0]->find_signal(pname); // Handle the error case where there is no internal // signal connected to the port. if (!tmp) continue; assert(tmp); if (tmp->port_type() == NetNet::PINPUT) { // If we have an unconnected input convert it // as needed if an unconnected_drive directive // was given. This only works for tri or wire! switch (rmod->uc_drive) { case Module::UCD_PULL0: convert_net(des, this, tmp, NetNet::TRI0); break; case Module::UCD_PULL1: convert_net(des, this, tmp, NetNet::TRI1); break; case Module::UCD_NONE: break; } // Print a warning for an unconnected input. if (warn_portbinding) { cerr << get_fileline() << ": warning: " << "Instantiating module " << rmod->mod_name() << " with dangling input port " << (idx+1) << " (" << port_name; switch (rmod->uc_drive) { case Module::UCD_PULL0: cerr << ") pulled low." << endl; break; case Module::UCD_PULL1: cerr << ") pulled high." << endl; break; case Module::UCD_NONE: cerr << ") floating." << endl; break; } } } unconnected_port = true; } // Inside the module, the port connects zero or more signals // that were already elaborated. List all those signals // and the NetNet equivalents, for all the instances. vector mport = rmod->get_port(idx); vector prts (mport.size() * instance.size()); if (debug_elaborate) { cerr << get_fileline() << ": debug: " << get_name() << ": Port " << (idx+1) << " (" << port_name << ") has " << prts.size() << " sub-ports." << endl; } // Count the internal vector bits of the port. unsigned prts_vector_width = 0; for (unsigned inst = 0 ; inst < instance.size() ; inst += 1) { // Scan the instances from MSB to LSB. The port // will be assembled in that order as well. NetScope*inst_scope = instance[instance.size()-inst-1]; unsigned int prt_vector_width = 0; PortType::Enum ptype = PortType::PIMPLICIT; // Scan the module sub-ports for this instance... // (Sub-ports are concatenated ports that form the // single port for the instance. This is not a // commonly used feature.) for (unsigned ldx = 0 ; ldx < mport.size() ; ldx += 1) { unsigned lbase = inst * mport.size(); PEIdent*pport = mport[ldx]; ivl_assert(*this, pport); NetNet *netnet = pport->elaborate_subport(des, inst_scope); prts[lbase + ldx] = netnet; if (netnet == 0) continue; ivl_assert(*this, netnet); unsigned port_width = netnet->vector_width() * netnet->pin_count(); prts_vector_width += port_width; prt_vector_width += port_width; ptype = PortType::merged(netnet->port_type(), ptype); } inst_scope->add_module_port_info(idx, port_name, ptype, prt_vector_width ); } // If I find that the port is unconnected inside the // module, then there is nothing to connect. Skip the // argument. if ((prts_vector_width == 0) || unconnected_port) { continue; } // We know by design that each instance has the same // width port. Therefore, the prts_pin_count must be an // even multiple of the instance count. assert(prts_vector_width % instance.size() == 0); if (!prts.empty() && (prts[0]->port_type() == NetNet::PINPUT) && prts[0]->pin(0).nexus()->drivers_present() && pins[idx]->is_collapsible_net(des, scope)) { prts[0]->port_type(NetNet::PINOUT); cerr << pins[idx]->get_fileline() << ": warning: input port " << prts[0]->name() << " is coerced to inout." << endl; } // Elaborate the expression that connects to the // module[s] port. sig is the thing outside the module // that connects to the port. NetNet*sig = 0; if (prts.empty() || (prts[0]->port_type() == NetNet::PINPUT)) { // Special case: If the input port is an unpacked // array, then there should be no sub-ports and // the r-value expression is processed // differently. if (prts.size() >= 1 && prts[0]->pin_count()>1) { ivl_assert(*this, prts.size()==1); PEIdent*rval_pident = dynamic_cast (pins[idx]); ivl_assert(*this, rval_pident); NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope); ivl_assert(*this, rval_net->pin_count() == prts[0]->pin_count()); assign_unpacked_with_bufz(des, scope, this, prts[0], rval_net); continue; } /* Input to module. elaborate the expression to the desired width. If this in an instance array, then let the net determine its own width. We use that, then, to decide how to hook it up. NOTE that this also handles the case that the port is actually empty on the inside. We assume in that case that the port is input. */ NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], -1); if (tmp_expr == 0) { cerr << pins[idx]->get_fileline() << ": error: Failed to elaborate port expression." << endl; des->errors += 1; continue; } if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Elaborating INPUT port expression: " << *tmp_expr << endl; } sig = tmp_expr->synthesize(des, scope, tmp_expr); if (sig == 0) { cerr << pins[idx]->get_fileline() << ": internal error: Port expression " << "too complicated for elaboration." << endl; continue; } delete tmp_expr; if (!sig->get_lineno()) sig->set_line(*this); if (need_bufz_for_input_port(prts)) { NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(), sig->vector_width(), true); tmp->set_line(*this); des->add_node(tmp); connect(tmp->pin(1), sig->pin(0)); netvector_t*tmp2_vec = new netvector_t(sig->data_type(), sig->vector_width()-1,0); NetNet*tmp2 = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp2_vec); tmp2->local_flag(true); tmp2->set_line(*this); connect(tmp->pin(0), tmp2->pin(0)); sig = tmp2; } // If we have a real signal driving a bit/vector port // then we convert the real value using the appropriate // width cast. Since a real is only one bit the whole // thing needs to go to each instance when arrayed. if ((sig->data_type() == IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { sig = cast_to_int4(des, scope, sig, prts_vector_width/instance.size()); } // If we have a bit/vector signal driving a real port // then we convert the value to a real. if ((sig->data_type() != IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) { sig = cast_to_real(des, scope, sig); } // If we have a 4-state bit/vector signal driving a // 2-state port then we convert the value to 2-state. if ((sig->data_type() == IVL_VT_LOGIC ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_BOOL )) { sig = cast_to_int2(des, scope, sig, sig->vector_width()); } } else if (prts[0]->port_type() == NetNet::PINOUT) { // For now, do not support unpacked array outputs. ivl_assert(*this, prts[0]->unpacked_dimensions()==0); /* Inout to/from module. This is a more complicated case, where the expression must be an lnet, but also an r-value net. Normally, this winds up being the same as if we just elaborated as an lnet, as passing a simple identifier elaborates to the same NetNet in both cases so the extra elaboration has no effect. But if the expression passed to the inout port is a part select, a special part select must be created that can pass data in both directions. Use the elaborate_bi_net method to handle all the possible cases. */ sig = pins[idx]->elaborate_bi_net(des, scope); if (sig == 0) { cerr << pins[idx]->get_fileline() << ": error: " << "Inout port expression must support " << "continuous assignment." << endl; cerr << pins[idx]->get_fileline() << ": : Port " << (idx+1) << " (" << port_name << ") of " << rmod->mod_name() << " is connected to " << *pins[idx] << endl; des->errors += 1; continue; } // We do not support automatic bits to real conversion // for inout ports. if ((sig->data_type() == IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { cerr << pins[idx]->get_fileline() << ": error: " << "Cannot automatically connect bit based " "inout port " << (idx+1) << " (" << port_name << ") of module " << rmod->mod_name() << " to real signal " << sig->name() << "." << endl; des->errors += 1; continue; } // We do not support real inout ports at all. if (!prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) { cerr << pins[idx]->get_fileline() << ": error: " << "No support for connecting real inout ports (" "port " << (idx+1) << " (" << port_name << ") of module " << rmod->mod_name() << ")." << endl; des->errors += 1; continue; } } else { /* Port type must be OUTPUT here. */ ivl_assert(*this, prts[0]->port_type() == NetNet::POUTPUT); // Special case: If the output port is an unpacked // array, then there should be no sub-ports and // the passed port expression is processed // differently. Note that we are calling it the // "r-value" expression, but since this is an // output port, we assign to it from the internal object. if (prts[0]->pin_count() > 1) { ivl_assert(*this, prts.size()==1); PEIdent*rval_pident = dynamic_cast(pins[idx]); ivl_assert(*this, rval_pident); NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope); ivl_assert(*this, rval_net->pin_count() == prts[0]->pin_count()); assign_unpacked_with_bufz(des, scope, this, rval_net, prts[0]); continue; } // At this point, arrays are handled. ivl_assert(*this, prts[0]->unpacked_dimensions()==0); /* Output from module. Elaborate the port expression as the l-value of a continuous assignment, as the port will continuous assign into the port. */ sig = pins[idx]->elaborate_lnet(des, scope); if (sig == 0) { cerr << pins[idx]->get_fileline() << ": error: " << "Output port expression must support " << "continuous assignment." << endl; cerr << pins[idx]->get_fileline() << ": : Port " << (idx+1) << " (" << port_name << ") of " << rmod->mod_name() << " is connected to " << *pins[idx] << endl; des->errors += 1; continue; } // If we have a real port driving a bit/vector signal // then we convert the real value using the appropriate // width cast. Since a real is only one bit the whole // thing needs to go to each instance when arrayed. if ((sig->data_type() != IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) { if (sig->vector_width() % instance.size() != 0) { cerr << pins[idx]->get_fileline() << ": error: " "When automatically converting a real " "port of an arrayed instance to a bit " "signal" << endl; cerr << pins[idx]->get_fileline() << ": : " "the signal width (" << sig->vector_width() << ") must be an " "integer multiple of the instance count (" << instance.size() << ")." << endl; des->errors += 1; continue; } prts_vector_width = sig->vector_width(); for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { prts[pidx]->port_type(NetNet::NOT_A_PORT); prts[pidx] = cast_to_int4(des, scope, prts[pidx], prts_vector_width / instance.size()); prts[pidx]->port_type(NetNet::POUTPUT); } } // If we have a bit/vector port driving a single real // signal then we convert the value to a real. if ((sig->data_type() == IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { prts_vector_width -= prts[0]->vector_width() - 1; prts[0]->port_type(NetNet::NOT_A_PORT); prts[0] = cast_to_real(des, scope, prts[0]); prts[0]->port_type(NetNet::POUTPUT); // No support for multiple real drivers. if (instance.size() != 1) { cerr << pins[idx]->get_fileline() << ": error: " << "Cannot connect an arrayed instance of " "module " << rmod->mod_name() << " to " "real signal " << sig->name() << "." << endl; des->errors += 1; continue; } } // If we have a 4-state bit/vector port driving a // 2-state signal then we convert the value to 2-state. if ((sig->data_type() == IVL_VT_BOOL ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) { for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { prts[pidx]->port_type(NetNet::NOT_A_PORT); prts[pidx] = cast_to_int2(des, scope, prts[pidx], prts[pidx]->vector_width()); prts[pidx]->port_type(NetNet::POUTPUT); } } // A real to real connection is not allowed for arrayed // instances. You cannot have multiple real drivers. if ((sig->data_type() == IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_REAL ) && instance.size() != 1) { cerr << pins[idx]->get_fileline() << ": error: " << "An arrayed instance of " << rmod->mod_name() << " cannot have a real port (port " << (idx+1) << " : " << port_name << ") connected to a " "real signal (" << sig->name() << ")." << endl; des->errors += 1; continue; } } assert(sig); #ifndef NDEBUG if ((! prts.empty()) && (prts[0]->port_type() != NetNet::PINPUT)) { assert(sig->type() != NetNet::REG); } #endif /* If we are working with an instance array, then the signal width must match the port width exactly. */ if ((instance.size() != 1) && (sig->vector_width() != prts_vector_width) && (sig->vector_width() != prts_vector_width/instance.size())) { cerr << pins[idx]->get_fileline() << ": error: " << "Port expression width " << sig->vector_width() << " does not match expected width "<< prts_vector_width << " or " << (prts_vector_width/instance.size()) << "." << endl; des->errors += 1; continue; } if (debug_elaborate) { cerr << get_fileline() << ": debug: " << get_name() << ": Port " << (idx+1) << " (" << port_name << ") has vector width of " << prts_vector_width << "." << endl; } // Check that the parts have matching pin counts. If // not, they are different widths. Note that idx is 0 // based, but users count parameter positions from 1. if ((instance.size() == 1) && (prts_vector_width != sig->vector_width())) { bool as_signed = false; switch (prts[0]->port_type()) { case NetNet::POUTPUT: as_signed = prts[0]->get_signed(); break; case NetNet::PINPUT: as_signed = sig->get_signed(); break; case NetNet::PINOUT: /* This may not be correct! */ as_signed = prts[0]->get_signed() && sig->get_signed(); break; case NetNet::PREF: ivl_assert(*this, 0); break; default: ivl_assert(*this, 0); } cerr << get_fileline() << ": warning: Port " << (idx+1) << " (" << port_name << ") of " << type_ << " expects " << prts_vector_width << " bits, got " << sig->vector_width() << "." << endl; // Delete this when inout ports pad correctly. if (prts[0]->port_type() == NetNet::PINOUT) { if (prts_vector_width > sig->vector_width()) { cerr << get_fileline() << ": : Leaving " << (prts_vector_width-sig->vector_width()) << " high bits of the port unconnected." << endl; } else { cerr << get_fileline() << ": : Leaving " << (sig->vector_width()-prts_vector_width) << " high bits of the expression dangling." << endl; } // Keep the if, but delete the "} else" when fixed. } else if (prts_vector_width > sig->vector_width()) { cerr << get_fileline() << ": : Padding "; if (as_signed) cerr << "(signed) "; cerr << (prts_vector_width-sig->vector_width()) << " high bits of the port." << endl; } else { if (prts[0]->port_type() == NetNet::PINPUT) { cerr << get_fileline() << ": : Pruning "; } else { cerr << get_fileline() << ": : Padding "; } if (as_signed) cerr << "(signed) "; cerr << (sig->vector_width()-prts_vector_width) << " high bits of the expression." << endl; } sig = resize_net_to_port_(des, scope, sig, prts_vector_width, prts[0]->port_type(), as_signed); } // Connect the sig expression that is the context of the // module instance to the ports of the elaborated module. // The prts_pin_count variable is the total width of the // port and is the maximum number of connections to // make. sig is the elaborated expression that connects // to that port. If sig has too few pins, then reduce // the number of connections to make. // Connect this many of the port pins. If the expression // is too small, then reduce the number of connects. unsigned ccount = prts_vector_width; if (instance.size() == 1 && sig->vector_width() < ccount) ccount = sig->vector_width(); // Now scan the concatenation that makes up the port, // connecting pins until we run out of port pins or sig // pins. The sig object is the NetNet that is connected // to the port from the outside, and the prts object is // an array of signals to be connected to the sig. NetConcat*ctmp; if (prts.size() == 1) { // The simplest case, there are no // parts/concatenations on the inside of the // module, so the port and sig need simply be // connected directly. connect(prts[0]->pin(0), sig->pin(0)); } else if (sig->vector_width()==prts_vector_width/instance.size() && prts.size()/instance.size() == 1) { if (debug_elaborate){ cerr << get_fileline() << ": debug: " << get_name() << ": Replicating " << prts_vector_width << " bits across all " << prts_vector_width/instance.size() << " sub-ports." << endl; } // The signal width is exactly the width of a // single instance of the port. In this case, // connect the sig to all the ports identically. for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) connect(prts[ldx]->pin(0), sig->pin(0)); } else switch (prts[0]->port_type()) { case NetNet::POUTPUT: ctmp = new NetConcat(scope, scope->local_symbol(), prts_vector_width, prts.size()); ctmp->set_line(*this); des->add_node(ctmp); connect(ctmp->pin(0), sig->pin(0)); for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) { connect(ctmp->pin(ldx+1), prts[prts.size()-ldx-1]->pin(0)); } break; case NetNet::PINPUT: if (debug_elaborate){ cerr << get_fileline() << ": debug: " << get_name() << ": Dividing " << prts_vector_width << " bits across all " << prts_vector_width/instance.size() << " input sub-ports of port " << (idx+1) << "." << endl; } for (unsigned ldx = 0, spin = 0 ; ldx < prts.size() ; ldx += 1) { NetNet*sp = prts[prts.size()-ldx-1]; NetPartSelect*ptmp = new NetPartSelect(sig, spin, sp->vector_width(), NetPartSelect::VP); ptmp->set_line(*this); des->add_node(ptmp); connect(ptmp->pin(0), sp->pin(0)); spin += sp->vector_width(); } break; case NetNet::PINOUT: for (unsigned ldx = 0, spin = 0 ; ldx < prts.size() ; ldx += 1) { NetNet*sp = prts[prts.size()-ldx-1]; NetTran*ttmp = new NetTran(scope, scope->local_symbol(), sig->vector_width(), sp->vector_width(), spin); ttmp->set_line(*this); des->add_node(ttmp); connect(ttmp->pin(0), sig->pin(0)); connect(ttmp->pin(1), sp->pin(0)); spin += sp->vector_width(); } break; case NetNet::PREF: cerr << get_fileline() << ": sorry: " << "Reference ports not supported yet." << endl; des->errors += 1; break; case NetNet::PIMPLICIT: cerr << get_fileline() << ": internal error: " << "Unexpected IMPLICIT port" << endl; des->errors += 1; break; case NetNet::NOT_A_PORT: cerr << get_fileline() << ": internal error: " << "Unexpected NOT_A_PORT port." << endl; des->errors += 1; break; } } } unsigned PGModule::calculate_instance_count_(Design*des, NetScope*scope, long&high, long&low, perm_string name) const { unsigned count = 1; high = 0; low = 0; /* If the Verilog source has a range specification for the UDP, then * I am expected to make more than one gate. Figure out how many are * desired. */ if (msb_) { NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true); NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true); NetEConst*msb_con = dynamic_cast(msb_exp); NetEConst*lsb_con = dynamic_cast(lsb_exp); if (msb_con == 0) { cerr << get_fileline() << ": error: Unable to evaluate " "expression " << *msb_ << endl; des->errors += 1; return 0; } if (lsb_con == 0) { cerr << get_fileline() << ": error: Unable to evaluate " "expression " << *lsb_ << endl; des->errors += 1; return 0; } verinum msb = msb_con->value(); verinum lsb = lsb_con->value(); delete msb_exp; delete lsb_exp; if (msb.as_long() > lsb.as_long()) count = msb.as_long() - lsb.as_long() + 1; else count = lsb.as_long() - msb.as_long() + 1; low = lsb.as_long(); high = msb.as_long(); if (debug_elaborate) { cerr << get_fileline() << ": debug: PGModule: Make range " << "[" << high << ":" << low << "]" << " of " << count << " UDPs for " << name << endl; } } return count; } /* * From a UDP definition in the source, make a NetUDP * object. Elaborate the pin expressions as netlists, then connect * those networks to the pins. */ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const { NetExpr*rise_expr =0, *fall_expr =0, *decay_expr =0; perm_string my_name = get_name(); if (my_name == 0) my_name = scope->local_symbol(); /* When the parser notices delay expressions in front of a module or primitive, it interprets them as parameter overrides. Correct that misconception here. */ if (overrides_) { if (overrides_->size() > 2) { cerr << get_fileline() << ": error: UDPs take at most two " "delay arguments." << endl; des->errors += 1; } else { PDelays tmp_del; tmp_del.set_delays(overrides_, false); tmp_del.eval_delays(des, scope, rise_expr, fall_expr, decay_expr); } } long low = 0, high = 0; unsigned inst_count = calculate_instance_count_(des, scope, high, low, my_name); if (inst_count == 0) return; if (inst_count != 1) { cerr << get_fileline() << ": sorry: UDPs with a range (" << my_name << " [" << high << ":" << low << "]) are " << "not supported." << endl; des->errors += 1; return; } assert(udp); NetUDP*net = new NetUDP(scope, my_name, udp->ports.count(), udp); net->set_line(*this); net->rise_time(rise_expr); net->fall_time(fall_expr); net->decay_time(decay_expr); struct attrib_list_t*attrib_list; unsigned attrib_list_n = 0; attrib_list = evaluate_attributes(attributes, attrib_list_n, des, scope); for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1) net->attribute(attrib_list[adx].key, attrib_list[adx].val); delete[]attrib_list; // This is the array of pin expressions, shuffled to match the // order of the declaration. If the source instantiation uses // bind by order, this is the same as the source // list. Otherwise, the source list is rearranged by name // binding into this list. vectorpins; // Detect binding by name. If I am binding by name, then make // up a pins array that reflects the positions of the named // ports. If this is simply positional binding in the first // place, then get the binding from the base class. if (pins_) { unsigned nexp = udp->ports.count(); pins = vector(nexp); // Scan the bindings, matching them with port names. for (unsigned idx = 0 ; idx < npins_ ; idx += 1) { // Given a binding, look at the module port names // for the position that matches the binding name. unsigned pidx = udp->find_port(pins_[idx].name); // If the port name doesn't exist, the find_port // method will return the port count. Detect that // as an error. if (pidx == nexp) { cerr << get_fileline() << ": error: port ``" << pins_[idx].name << "'' is not a port of " << get_name() << "." << endl; des->errors += 1; continue; } // If I already bound something to this port, then // the (*exp) array will already have a pointer // value where I want to place this expression. if (pins[pidx]) { cerr << get_fileline() << ": error: port ``" << pins_[idx].name << "'' already bound." << endl; des->errors += 1; continue; } // OK, do the binding by placing the expression in // the right place. pins[pidx] = pins_[idx].parm; } } else { /* Otherwise, this is a positional list of port connections. In this case, the port count must be right. Check that is is, the get the pin list. */ if (pin_count() != udp->ports.count()) { cerr << get_fileline() << ": error: Wrong number " "of ports. Expecting " << udp->ports.count() << ", got " << pin_count() << "." << endl; des->errors += 1; return; } // No named bindings, just use the positional list I // already have. assert(pin_count() == udp->ports.count()); pins = get_pins(); } /* Handle the output port of the primitive special. It is an output port (the only output port) so must be passed an l-value net. */ if (pins[0] == 0) { cerr << get_fileline() << ": warning: output port unconnected." << endl; } else { NetNet*sig = pins[0]->elaborate_lnet(des, scope); if (sig == 0) { cerr << get_fileline() << ": error: " << "Output port expression is not valid." << endl; cerr << get_fileline() << ": : Output " << "port of " << udp->name_ << " is " << udp->ports[0] << "." << endl; des->errors += 1; } else { connect(sig->pin(0), net->pin(0)); } if (sig->vector_width() != 1) { cerr << get_fileline() << ": error: " << "Output port expression " << *pins[0] << " is too wide (" << sig->vector_width() << ") expected 1." << endl; des->errors += 1; } } /* Run through the pins, making netlists for the pin expressions and connecting them to the pin in question. All of this is independent of the nature of the UDP. */ for (unsigned idx = 1 ; idx < net->pin_count() ; idx += 1) { if (pins[idx] == 0) continue; NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1); if (expr_tmp == 0) { cerr << "internal error: Expression too complicated " "for elaboration:" << *pins[idx] << endl; continue; } NetNet*sig = expr_tmp->synthesize(des, scope, expr_tmp); ivl_assert(*this, sig); sig->set_line(*this); delete expr_tmp; connect(sig->pin(0), net->pin(idx)); if (sig->vector_width() != 1) { cerr << get_fileline() << ": error: " << "Input port expression " << *pins[idx] << " is too wide (" << sig->vector_width() << ") expected 1." << endl; des->errors += 1; } } // All done. Add the object to the design. des->add_node(net); } bool PGModule::elaborate_sig(Design*des, NetScope*scope) const { if (bound_type_) { return elaborate_sig_mod_(des, scope, bound_type_); } // Look for the module type map::const_iterator mod = pform_modules.find(type_); if (mod != pform_modules.end()) return elaborate_sig_mod_(des, scope, (*mod).second); // elaborate_sig_udp_ currently always returns true so skip all this // for now. #if 0 map::const_iterator udp = pform_primitives.find(type_); if (udp != pform_primitives.end()) return elaborate_sig_udp_(des, scope, (*udp).second); #endif return true; } void PGModule::elaborate(Design*des, NetScope*scope) const { if (bound_type_) { elaborate_mod_(des, bound_type_, scope); return; } // Look for the module type map::const_iterator mod = pform_modules.find(type_); if (mod != pform_modules.end()) { elaborate_mod_(des, (*mod).second, scope); return; } // Try a primitive type map::const_iterator udp = pform_primitives.find(type_); if (udp != pform_primitives.end()) { assert((*udp).second); elaborate_udp_(des, (*udp).second, scope); return; } cerr << get_fileline() << ": internal error: Unknown module type: " << type_ << endl; } void PGModule::elaborate_scope(Design*des, NetScope*sc) const { // If the module type is known by design, then go right to it. if (bound_type_) { elaborate_scope_mod_(des, bound_type_, sc); return; } // Look for the module type map::const_iterator mod = pform_modules.find(type_); if (mod != pform_modules.end()) { elaborate_scope_mod_(des, mod->second, sc); return; } // Try a primitive type map::const_iterator udp = pform_primitives.find(type_); if (udp != pform_primitives.end()) return; // Not a module or primitive that I know about yet, so try to // load a library module file (which parses some new Verilog // code) and try again. if (load_module(type_)) { // Try again to find the module type mod = pform_modules.find(type_); if (mod != pform_modules.end()) { elaborate_scope_mod_(des, mod->second, sc); return; } // Try again to find a primitive type udp = pform_primitives.find(type_); if (udp != pform_primitives.end()) return; } // Not a module or primitive that I know about or can find by // any means, so give up. cerr << get_fileline() << ": error: Unknown module type: " << type_ << endl; missing_modules[type_] += 1; des->errors += 1; } NetProc* Statement::elaborate(Design*des, NetScope*) const { cerr << get_fileline() << ": internal error: elaborate: " "What kind of statement? " << typeid(*this).name() << endl; NetProc*cur = new NetProc; des->errors += 1; return cur; } NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const { // A function called as a task does not have an L-value. if (! lval_) { // The R-value must be a simple function call. assert (dynamic_cast(rval_)); PExpr::width_mode_t mode = PExpr::SIZED; rval_->test_width(des, scope, mode); // Create a L-value that matches the function return type. NetNet*tmp; netvector_t*tmp_vec = new netvector_t(rval_->expr_type(), rval_->expr_width()-1, 0, rval_->has_sign()); if(rval_->expr_type() == IVL_VT_DARRAY) { netdarray_t*darray = new netdarray_t(tmp_vec); tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, darray); } else { tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, tmp_vec); } tmp->set_file(rval_->get_file()); tmp->set_lineno(rval_->get_lineno()); NetAssign_*lv = new NetAssign_(tmp); return lv; } if (debug_elaborate) { cerr << get_fileline() << ": PAssign_::elaborate_lval: " << "lval_ = " << *lval_ << endl; cerr << get_fileline() << ": PAssign_::elaborate_lval: " << "lval_ expr type = " << typeid(*lval_).name() << endl; } return lval_->elaborate_lval(des, scope, false, false); } NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, ivl_type_t net_type) const { ivl_assert(*this, rval_); NetExpr*rv = rval_->elaborate_expr(des, scope, net_type, 0); ivl_assert(*this, !is_constant_); return rv; } NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, unsigned lv_width) const { ivl_assert(*this, rval_); // Don't have a good value for the lv_net_type argument to // elaborate_rval_expr, so punt and pass nil. In the future we // should look into fixing calls to this method to pass a // net_type instead of the separate lv_width/lv_type values. NetExpr*rv = elaborate_rval_expr(des, scope, lv_net_type, lv_type, lv_width, rval(), is_constant_); if (!is_constant_ || !rv) return rv; if (dynamic_cast(rv)) return rv; if (dynamic_cast(rv)) return rv; cerr << get_fileline() << ": error: " "The RHS expression must be constant." << endl; cerr << get_fileline() << " : " "This expression violates the rule: " << *rv << endl; des->errors += 1; delete rv; return 0; } /* * This function elaborates delay expressions. This is a little * different from normal elaboration because the result may need to be * scaled. */ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) { NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based * delays in the design, since this is likely an error. */ if (scope->time_from_timescale()) dly_used_timescale = true; else dly_used_no_timescale = true; if (display_ts_dly_warning && dly_used_no_timescale && dly_used_timescale) { cerr << "warning: Found both default and " "`timescale based delays. Use" << endl; cerr << " -Wtimescale to find the " "module(s) with no `timescale." << endl; display_ts_dly_warning = false; } /* If the delay expression is a real constant or vector constant, then evaluate it, scale it to the local time units, and return an adjusted NetEConst. */ if (NetECReal*tmp = dynamic_cast(dex)) { uint64_t delay = get_scaled_time_from_real(des, scope, tmp); delete tmp; NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); tmp2->set_line(*expr); return tmp2; } if (NetEConst*tmp = dynamic_cast(dex)) { verinum fn = tmp->value(); uint64_t delay = des->scale_to_precision(fn.as_ulong64(), scope); delete tmp; NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); tmp2->set_line(*expr); return tmp2; } /* The expression is not constant, so generate an expanded expression that includes the necessary scale shifts, and return that expression. */ ivl_assert(*expr, dex); if (dex->expr_type() == IVL_VT_REAL) { // Scale the real value. int shift = scope->time_unit() - scope->time_precision(); assert(shift >= 0); double round = 1; for (int lp = 0; lp < shift; lp += 1) round *= 10.0; NetExpr*scal_val = new NetECReal(verireal(round)); scal_val->set_line(*expr); dex = new NetEBMult('*', dex, scal_val, 1, true); dex->set_line(*expr); // Cast this part of the expression to an integer. dex = new NetECast('v', dex, 64, false); dex->set_line(*expr); // Now scale the integer value. shift = scope->time_precision() - des->get_precision(); assert(shift >= 0); uint64_t scale = 1; for (int lp = 0; lp < shift; lp += 1) scale *= 10; scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } else { int shift = scope->time_unit() - des->get_precision(); assert(shift >= 0); uint64_t scale = 1; for (int lp = 0; lp < shift; lp += 1) scale *= 10; NetExpr*scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } return dex; } NetProc* PAssign::elaborate_compressed_(Design*des, NetScope*scope) const { ivl_assert(*this, ! delay_); ivl_assert(*this, ! count_); ivl_assert(*this, ! event_); NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; NetExpr*rv = elaborate_rval_(des, scope, 0, lv->expr_type(), count_lval_width(lv)); if (rv == 0) return 0; NetAssign*cur = new NetAssign(lv, op_, rv); cur->set_line(*this); return cur; } /* * Assignments within program blocks can only write to certain types * of variables. We can only write to: * - variables in a program block * - static properties of a class */ static bool lval_not_program_variable(const NetAssign_*lv) { while (lv) { NetScope*sig_scope = lv->scope(); if (! sig_scope->program_block() && sig_scope->type()!=NetScope::CLASS) return true; lv = lv->more; } return false; } NetProc* PAssign::elaborate(Design*des, NetScope*scope) const { assert(scope); /* If this is a compressed assignment, then handle the elaboration in a specialized function. */ if (op_ != 0) return elaborate_compressed_(des, scope); /* elaborate the lval. This detects any part selects and mux expressions that might exist. */ NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; if (scope->program_block() && lval_not_program_variable(lv)) { cerr << get_fileline() << ": error: Blocking assignments to " << "non-program variables are not allowed." << endl; des->errors += 1; } /* If there is an internal delay expression, elaborate it. */ NetExpr*delay = 0; if (delay_ != 0) delay = elaborate_delay_expr(delay_, des, scope); NetExpr*rv; const ivl_type_s*lv_net_type = lv->net_type(); if (debug_elaborate) { cerr << get_fileline() << ": PAssign::elaborate: "; if (lv_net_type) cerr << "lv_net_type=" << *lv_net_type << endl; else cerr << "lv_net_type=" << endl; } /* If the l-value is a compound type of some sort, then use the newer net_type form of the elaborate_rval_ method to handle the new types. */ if (dynamic_cast (lv_net_type)) { ivl_assert(*this, lv->more==0); rv = elaborate_rval_(des, scope, lv_net_type); } else if (const netdarray_t*dtype = dynamic_cast (lv_net_type)) { ivl_assert(*this, lv->more==0); if (debug_elaborate) { if (lv->word()) cerr << get_fileline() << ": PAssign::elaborate: " << "lv->word() = " << *lv->word() << endl; else cerr << get_fileline() << ": PAssign::elaborate: " << "lv->word() = " << endl; } ivl_type_t use_lv_type = lv_net_type; if (lv->word()) use_lv_type = dtype->element_type(); rv = elaborate_rval_(des, scope, use_lv_type); } else if (const netuarray_t*utype = dynamic_cast(lv_net_type)) { ivl_assert(*this, lv->more==0); if (debug_elaborate) { if (lv->word()) cerr << get_fileline() << ": PAssign::elaborate: " << "lv->word() = " << *lv->word() << endl; else cerr << get_fileline() << ": PAssign::elaborate: " << "lv->word() = " << endl; } ivl_type_t use_lv_type = lv_net_type; ivl_assert(*this, lv->word()); use_lv_type = utype->element_type(); ivl_assert(*this, use_lv_type); rv = elaborate_rval_(des, scope, use_lv_type); } else { /* Elaborate the r-value expression, then try to evaluate it. */ rv = elaborate_rval_(des, scope, lv_net_type, lv->expr_type(), count_lval_width(lv)); } if (rv == 0) return 0; assert(rv); if (count_) assert(event_); /* Rewrite delayed assignments as assignments that are delayed. For example, a = # b; becomes: begin tmp = b; # a = tmp; end If the delay is an event delay, then the transform is similar, with the event delay replacing the time delay. It is an event delay if the event_ member has a value. This rewriting of the expression allows me to not bother to actually and literally represent the delayed assign in the netlist. The compound statement is exactly equivalent. */ if (delay || event_) { unsigned wid = count_lval_width(lv); netvector_t*tmp2_vec = new netvector_t(rv->expr_type(),wid-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, tmp2_vec); tmp->local_flag(true); tmp->set_line(*this); NetESignal*sig = new NetESignal(tmp); /* Generate an assignment of the l-value to the temporary... */ NetAssign_*lvt = new NetAssign_(tmp); NetAssign*a1 = new NetAssign(lvt, rv); a1->set_line(*this); /* Generate an assignment of the temporary to the r-value... */ NetAssign*a2 = new NetAssign(lv, sig); a2->set_line(*this); /* Generate the delay statement with the final assignment attached to it. If this is an event delay, elaborate the PEventStatement. Otherwise, create the right NetPDelay object. For a repeat event control repeat the event and then do the final assignment. */ NetProc*st; if (event_) { if (count_) { NetExpr*count = elab_and_eval(des, scope, count_, -1); if (count == 0) { cerr << get_fileline() << ": Unable to " "elaborate repeat expression." << endl; des->errors += 1; return 0; } st = event_->elaborate(des, scope); if (st == 0) { cerr << event_->get_fileline() << ": error: " "unable to elaborate event expression." << endl; des->errors += 1; return 0; } st->set_line(*this); // If the expression is a constant, handle // certain special iteration counts. if (NetEConst*ce = dynamic_cast(count)) { long val = ce->value().as_long(); // We only need the real statement. if (val <= 0) { delete count; delete st; st = 0; // We don't need the repeat statement. } else if (val == 1) { delete count; // We need a repeat statement. } else { st = new NetRepeat(count, st); st->set_line(*this); } } else { st = new NetRepeat(count, st); st->set_line(*this); } } else { st = event_->elaborate_st(des, scope, a2); if (st == 0) { cerr << event_->get_fileline() << ": error: " "unable to elaborate event expression." << endl; des->errors += 1; return 0; } st->set_line(*this); } } else { NetPDelay*de = new NetPDelay(delay, a2); de->set_line(*this); st = de; } /* And build up the complex statement. */ NetBlock*bl = new NetBlock(NetBlock::SEQU, 0); bl->append(a1); if (st) bl->append(st); if (count_) bl->append(a2); bl->set_line(*this); return bl; } if (lv->enumeration() && ! lv->enumeration()->matches(rv->enumeration())) { cerr << get_fileline() << ": error: " << "Enumeration type mismatch in assignment." << endl; des->errors += 1; } NetAssign*cur = new NetAssign(lv, rv); cur->set_line(*this); return cur; } /* * Return true if any lvalue parts are in a program block scope. */ static bool lval_is_program_variable(const NetAssign_*lv) { while (lv) { NetScope*sig_scope = lv->sig()->scope(); if (sig_scope->program_block()) return true; lv = lv->more; } return false; } /* * Elaborate non-blocking assignments. The statement is of the general * form: * * <= # ; */ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const { assert(scope); if (scope->in_func()) { cerr << get_fileline() << ": error: functions cannot have non " "blocking assignment statements." << endl; des->errors += 1; return 0; } if (scope->in_final()) { cerr << get_fileline() << ": error: final procedures cannot have " "non blocking assignment statements." << endl; des->errors += 1; return 0; } if (scope->is_auto() && lval()->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be assigned values using non-blocking " "assignments." << endl; des->errors += 1; return 0; } /* Elaborate the l-value. */ NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; if (scope->program_block() && lval_is_program_variable(lv)) { cerr << get_fileline() << ": error: Non-blocking assignments to " << "program variables are not allowed." << endl; des->errors += 1; // This is an error, but we can let elaboration continue // because it would necessarily trigger other errors. } NetExpr*rv = elaborate_rval_(des, scope, 0, lv->expr_type(), count_lval_width(lv)); if (rv == 0) return 0; NetExpr*delay = 0; if (delay_ != 0) { assert(count_ == 0 && event_ == 0); delay = elaborate_delay_expr(delay_, des, scope); } NetExpr*count = 0; NetEvWait*event = 0; if (count_ != 0 || event_ != 0) { if (count_ != 0) { if (scope->is_auto() && count_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically " "allocated variables may not be referenced " "in intra-assignment event controls of " "non-blocking assignments." << endl; des->errors += 1; return 0; } assert(event_ != 0); count = elab_and_eval(des, scope, count_, -1); if (count == 0) { cerr << get_fileline() << ": Unable to elaborate " "repeat expression." << endl; des->errors += 1; return 0; } } if (scope->is_auto() && event_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically " "allocated variables may not be referenced " "in intra-assignment event controls of " "non-blocking assignments." << endl; des->errors += 1; return 0; } NetProc*st = event_->elaborate(des, scope); if (st == 0) { cerr << get_fileline() << ": unable to elaborate " "event expression." << endl; des->errors += 1; return 0; } event = dynamic_cast(st) ; assert(event); // Some constant values are special. if (NetEConst*ce = dynamic_cast(count)) { long val = ce->value().as_long(); // We only need the assignment statement. if (val <= 0) { delete count; delete event; count = 0; event = 0; // We only need the event. } else if (val == 1) { delete count; count = 0; } } } /* All done with this node. Mark its line number and check it in. */ NetAssignNB*cur = new NetAssignNB(lv, rv, event, count); cur->set_delay(delay); cur->set_line(*this); return cur; } /* * This is the elaboration method for a begin-end block. Try to * elaborate the entire block, even if it fails somewhere. This way I * get all the error messages out of it. Then, if I detected a failure * then pass the failure up. */ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const { assert(scope); NetBlock::Type type; switch (bl_type_) { case PBlock::BL_SEQ: type = NetBlock::SEQU; break; case PBlock::BL_PAR: type = NetBlock::PARA; break; case PBlock::BL_JOIN_NONE: type = NetBlock::PARA_JOIN_NONE; break; case PBlock::BL_JOIN_ANY: type = NetBlock::PARA_JOIN_ANY; break; // Added to remove a "type" uninitialized compiler warning. // This should never be reached since all the PBlock enumeration // cases are handled above. default: type = NetBlock::SEQU; assert(0); } NetScope*nscope = 0; if (pscope_name() != 0) { nscope = scope->child(hname_t(pscope_name())); if (nscope == 0) { cerr << get_fileline() << ": internal error: " "unable to find block scope " << scope_path(scope) << "." << pscope_name() << endl; des->errors += 1; return 0; } assert(nscope); elaborate_behaviors_(des, nscope); } NetBlock*cur = new NetBlock(type, nscope); if (nscope == 0) nscope = scope; // Handle the special case that the block contains only one // statement. There is no need to keep the block node. Also, // don't elide named blocks, because they might be referenced // elsewhere. if ((list_.size() == 1) && (pscope_name() == 0)) { assert(list_[0]); NetProc*tmp = list_[0]->elaborate(des, nscope); return tmp; } for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) { assert(list_[idx]); NetProc*tmp = list_[idx]->elaborate(des, nscope); // If the statement fails to elaborate, then simply // ignore it. Presumably, the elaborate for the // statement already generated an error message and // marked the error count in the design so no need to // do any of that here. if (tmp == 0) { continue; } // If the result turns out to be a noop, then skip it. if (NetBlock*tbl = dynamic_cast(tmp)) if (tbl->proc_first() == 0) { delete tbl; continue; } cur->append(tmp); } // Update flags in parent scope. if (!nscope->is_const_func()) scope->is_const_func(false); if (nscope->calls_sys_task()) scope->calls_sys_task(true); cur->set_line(*this); return cur; } static int test_case_width(Design*des, NetScope*scope, PExpr*pe, PExpr::width_mode_t&mode) { unsigned expr_width = pe->test_width(des, scope, mode); if (debug_elaborate) { cerr << pe->get_fileline() << ": debug: test_width " << "of case expression " << *pe << endl; cerr << pe->get_fileline() << ": " << "returns type=" << pe->expr_type() << ", width=" << expr_width << ", signed=" << pe->has_sign() << ", mode=" << PExpr::width_mode_name(mode) << endl; } return expr_width; } static NetExpr*elab_and_eval_case(Design*des, NetScope*scope, PExpr*pe, bool context_is_real, bool context_unsigned, unsigned context_width) { if (context_unsigned) pe->cast_signed(false); unsigned width = context_is_real ? pe->expr_width() : context_width; NetExpr*expr = pe->elaborate_expr(des, scope, width, PExpr::NO_FLAGS); if (expr == 0) return 0; if (context_is_real) expr = cast_to_real(expr); eval_expr(expr, context_width); return expr; } /* * Elaborate a case statement. */ NetProc* PCase::elaborate(Design*des, NetScope*scope) const { ivl_assert(*this, scope); /* The type of the case expression and case item expressions is determined according to the following rules: - if any of the expressions is real, all the expressions are evaluated as real (non-real expressions will be treated as self-determined, then converted to real) - otherwise if any of the expressions is unsigned, all the expressions are evaluated as unsigned - otherwise all the expressions are evaluated as signed If the type is not real, the bit width is determined by the largest self-determined width of any of the expressions. */ PExpr::width_mode_t context_mode = PExpr::SIZED; unsigned context_width = test_case_width(des, scope, expr_, context_mode); bool context_is_real = (expr_->expr_type() == IVL_VT_REAL); bool context_unsigned = !expr_->has_sign(); for (unsigned idx = 0; idx < items_->count(); idx += 1) { PCase::Item*cur = (*items_)[idx]; for (list::iterator idx_expr = cur->expr.begin() ; idx_expr != cur->expr.end() ; ++idx_expr) { PExpr*cur_expr = *idx_expr; ivl_assert(*this, cur_expr); PExpr::width_mode_t cur_mode = PExpr::SIZED; unsigned cur_width = test_case_width(des, scope, cur_expr, cur_mode); if (cur_mode > context_mode) context_mode = cur_mode; if (cur_width > context_width) context_width = cur_width; if (cur_expr->expr_type() == IVL_VT_REAL) context_is_real = true; if (!cur_expr->has_sign()) context_unsigned = true; } } if (context_is_real) { context_width = 1; context_unsigned = false; } else if (context_mode >= PExpr::LOSSLESS) { /* Expressions may choose a different size if they are in a lossless context, so we need to run through the process again to get the final expression width. */ context_width = test_case_width(des, scope, expr_, context_mode); for (unsigned idx = 0; idx < items_->count(); idx += 1) { PCase::Item*cur = (*items_)[idx]; for (list::iterator idx_expr = cur->expr.begin() ; idx_expr != cur->expr.end() ; ++idx_expr) { PExpr*cur_expr = *idx_expr; ivl_assert(*this, cur_expr); unsigned cur_width = test_case_width(des, scope, cur_expr, context_mode); if (cur_width > context_width) context_width = cur_width; } } if (context_width < integer_width) context_width += 1; } if (debug_elaborate) { cerr << get_fileline() << ": debug: case context is "; if (context_is_real) { cerr << "real" << endl; } else { cerr << (context_unsigned ? "unsigned" : "signed") << " vector, width=" << context_width << endl; } } NetExpr*expr = elab_and_eval_case(des, scope, expr_, context_is_real, context_unsigned, context_width); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate this case" " expression." << endl; return 0; } /* Count the items in the case statement. Note that there may be some cases that have multiple guards. Count each as a separate item. */ unsigned icount = 0; for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { PCase::Item*cur = (*items_)[idx]; if (cur->expr.empty()) icount += 1; else icount += cur->expr.size(); } NetCase*res = new NetCase(type_, expr, icount); res->set_line(*this); /* Iterate over all the case items (guard/statement pairs) elaborating them. If the guard has no expression, then this is a "default" case. Otherwise, the guard has one or more expressions, and each guard is a case. */ unsigned inum = 0; for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { ivl_assert(*this, inum < icount); PCase::Item*cur = (*items_)[idx]; if (cur->expr.empty()) { /* If there are no expressions, then this is the default case. */ NetProc*st = 0; if (cur->stat) st = cur->stat->elaborate(des, scope); res->set_case(inum, 0, st); inum += 1; } else for (list::iterator idx_expr = cur->expr.begin() ; idx_expr != cur->expr.end() ; ++idx_expr) { /* If there are one or more expressions, then iterate over the guard expressions, elaborating a separate case for each. (Yes, the statement will be elaborated again for each.) */ PExpr*cur_expr = *idx_expr; ivl_assert(*this, cur_expr); NetExpr*gu = elab_and_eval_case(des, scope, cur_expr, context_is_real, context_unsigned, context_width); NetProc*st = 0; if (cur->stat) st = cur->stat->elaborate(des, scope); res->set_case(inum, gu, st); inum += 1; } } res->prune(); return res; } NetProc* PChainConstructor::elaborate(Design*des, NetScope*scope) const { assert(scope); if (debug_elaborate) { cerr << get_fileline() << ": PChainConstructor::elaborate: " << "Elaborate constructor chain in scope=" << scope_path(scope) << endl; } // The scope is the .new function, so scope->parent() // is the class. Use that to get the class type that we are // constructing. NetScope*scope_class = scope->parent(); const netclass_t*class_this = scope_class->class_def(); ivl_assert(*this, class_this); // We also need the super-class. const netclass_t*class_super = class_this->get_super(); if (class_super == 0) { cerr << get_fileline() << ": error: " << "Class " << class_this->get_name() << " has no parent class for super.new constructor chaining." << endl; des->errors += 1; NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0); tmp->set_line(*this); return tmp; } // Need the "this" variable for the current constructor. We're // going to pass this to the chained constructor. NetNet*var_this = scope->find_signal(perm_string::literal("@")); // If super.new is an implicit constructor, then there are no // arguments (other than "this" to worry about, so make a // NetEUFunc and there we go. if (NetScope*new_scope = class_super->method_from_name(perm_string::literal("new@"))) { NetESignal*eres = new NetESignal(var_this); vector parms(1); parms[0] = eres; NetEUFunc*tmp = new NetEUFunc(scope, new_scope, eres, parms, true); tmp->set_line(*this); NetAssign_*lval_this = new NetAssign_(var_this); NetAssign*stmt = new NetAssign(lval_this, tmp); stmt->set_line(*this); return stmt; } // If super.new(...) is a user defined constructor, then call // it. This is a bit more complicated because there may be arguments. if (NetScope*new_scope = class_super->method_from_name(perm_string::literal("new"))) { int missing_parms = 0; NetFuncDef*def = new_scope->func_def(); ivl_assert(*this, def); NetESignal*eres = new NetESignal(var_this); vector parms (def->port_count()); parms[0] = eres; for (size_t idx = 1 ; idx < parms.size() ; idx += 1) { if (idx <= parms_.size() && parms_[idx-1]) { PExpr*tmp = parms_[idx-1]; parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->net_type(), def->port(idx)->data_type(), def->port(idx)->vector_width(), tmp, false); continue; } if (NetExpr*tmp = def->port_defe(idx)) { parms[idx] = tmp; continue; } missing_parms += 1; parms[idx] = 0; } if (missing_parms) { cerr << get_fileline() << ": error: " << "Missing " << missing_parms << " arguments to constructor " << scope_path(new_scope) << "." << endl; des->errors += 1; } NetEUFunc*tmp = new NetEUFunc(scope, new_scope, eres, parms, true); tmp->set_line(*this); NetAssign_*lval_this = new NetAssign_(var_this); NetAssign*stmt = new NetAssign(lval_this, tmp); stmt->set_line(*this); return stmt; } // There is no constructor at all in the parent, so skip it. NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0); tmp->set_line(*this); return tmp; } NetProc* PCondit::elaborate(Design*des, NetScope*scope) const { assert(scope); if (debug_elaborate) cerr << get_fileline() << ": debug: Elaborate condition statement" << " with conditional: " << *expr_ << endl; // Elaborate and try to evaluate the conditional expression. NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " condition expression." << endl; des->errors += 1; return 0; } // If the condition of the conditional statement is constant, // then look at the value and elaborate either the if statement // or the else statement. I don't need both. If there is no // else_ statement, then use an empty block as a noop. if (NetEConst*ce = dynamic_cast(expr)) { verinum val = ce->value(); if (debug_elaborate) { cerr << get_fileline() << ": debug: Condition expression " << "is a constant " << val << "." << endl; } verinum::V reduced = verinum::V0; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) reduced = reduced | val[idx]; delete expr; if (reduced == verinum::V1) if (if_) { return if_->elaborate(des, scope); } else { NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0); tmp->set_line(*this); return tmp; } else if (else_) return else_->elaborate(des, scope); else return new NetBlock(NetBlock::SEQU, 0); } // If the condition expression is more than 1 bits, then // generate a comparison operator to get the result down to // one bit. Turn into != 0; if (expr->expr_width() < 1) { cerr << get_fileline() << ": internal error: " "incomprehensible expression width (0)." << endl; return 0; } // Make sure the condition expression evaluates to a condition. expr = condition_reduce(expr); // Well, I actually need to generate code to handle the // conditional, so elaborate. NetProc*i = if_? if_->elaborate(des, scope) : 0; NetProc*e = else_? else_->elaborate(des, scope) : 0; // Detect the special cases that the if or else statements are // empty blocks. If this is the case, remove the blocks as // null statements. if (NetBlock*tmp = dynamic_cast(i)) { if (tmp->proc_first() == 0) { delete i; i = 0; } } if (NetBlock*tmp = dynamic_cast(e)) { if (tmp->proc_first() == 0) { delete e; e = 0; } } NetCondit*res = new NetCondit(expr, i, e); res->set_line(*this); return res; } NetProc* PCallTask::elaborate(Design*des, NetScope*scope) const { if (peek_tail_name(path_)[0] == '$') return elaborate_sys(des, scope); else return elaborate_usr(des, scope); } /* * A call to a system task involves elaborating all the parameters, * then passing the list to the NetSTask object. *XXXX * There is a single special case in the call to a system * task. Normally, an expression cannot take an unindexed * memory. However, it is possible to take a system task parameter a * memory if the expression is trivial. */ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const { assert(scope); if (path_.size() > 1) { cerr << get_fileline() << ": error: Hierarchical system task names" << " make no sense: " << path_ << endl; des->errors += 1; } unsigned parm_count = parms_.size(); /* Catch the special case that the system task has no parameters. The "()" string will be parsed as a single empty parameter, when we really mean no parameters at all. */ if ((parm_count== 1) && (parms_[0] == 0)) parm_count = 0; vectoreparms (parm_count); perm_string name = peek_tail_name(path_); for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { PExpr*ex = parms_[idx]; if (ex != 0) { eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); } else { eparms[idx] = 0; } } // Special case: Specify blocks are turned off, and this is an // $sdf_annotate system task. There will be nothing for $sdf // to annotate, and the user is intending to turn the behavior // off anyhow, so replace the system task invocation with a no-op. if (gn_specify_blocks_flag == false && name == "$sdf_annotate") { cerr << get_fileline() << ": warning: Omitting $sdf_annotate() " << "since specify blocks are being omitted." << endl; NetBlock*noop = new NetBlock(NetBlock::SEQU, scope); noop->set_line(*this); return noop; } scope->calls_sys_task(true); NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms); cur->set_line(*this); return cur; } /* * A call to a user defined task is different from a call to a system * task because a user task in a netlist has no parameters: the * assignments are done by the calling thread. For example: * * task foo; * input a; * output b; * [...] * endtask; * * [...] foo(x, y); * * is really: * * task foo; * reg a; * reg b; * [...] * endtask; * * [...] * begin * a = x; * foo; * y = b; * end */ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const { assert(scope); NetScope*pscope = scope; if (package_) { pscope = des->find_package(package_->pscope_name()); ivl_assert(*this, pscope); } NetScope*task = des->find_task(pscope, path_); if (task == 0) { // For SystemVerilog this may be a few other things. if (gn_system_verilog()) { NetProc *tmp; // This could be a method attached to a signal? tmp = elaborate_method_(des, scope); if (tmp) return tmp; // Or it could be a function call ignoring the return? tmp = elaborate_function_(des, scope); if (tmp) return tmp; } cerr << get_fileline() << ": error: Enable of unknown task " << "``" << path_ << "''." << endl; des->errors += 1; return 0; } assert(task); assert(task->type() == NetScope::TASK); NetTaskDef*def = task->task_def(); if (def == 0) { cerr << get_fileline() << ": internal error: task " << path_ << " doesn't have a definition in " << scope_path(scope) << "." << endl; des->errors += 1; return 0; } assert(def); /* In SystemVerilog a method calling another method in the * current class needs to be elaborated as a method with an * implicit this added. */ if (gn_system_verilog() && (path_.size() == 1)) { const NetScope *c_scope = scope->get_class_scope(); if (c_scope && (c_scope == task->get_class_scope())) { NetProc *tmp = elaborate_method_(des, scope, true); assert(tmp); return tmp; } } unsigned parm_count = def->port_count(); /* Handle non-automatic tasks with no parameters specially. There is no need to make a sequential block to hold the generated code. */ if ((parm_count == 0) && !task->is_auto()) { NetUTask*cur = new NetUTask(task); cur->set_line(*this); return cur; } return elaborate_build_call_(des, scope, task, 0); } /* * This private method is called to elaborate built-in methods. The * method_name is the detected name of the built-in method, and the * sys_task_name is the internal system-task name to use. */ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, const char*sys_task_name) const { NetESignal*sig = new NetESignal(net); sig->set_line(*this); /* If there is a single NULL argument then ignore it since it is * left over from the parser and is not needed by the method. */ unsigned nparms = parms_.size(); if ((nparms == 1) && (parms_[0] == 0)) nparms = 0; vectorargv (1 + nparms); argv[0] = sig; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*ex = parms_[idx]; if (ex != 0) { argv[idx+1] = elab_sys_task_arg(des, scope, method_name, idx, ex); } else { argv[idx+1] = 0; } } NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv); sys->set_line(*this); return sys; } NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, bool add_this_flag) const { pform_name_t use_path = path_; perm_string method_name = peek_tail_name(use_path); use_path.pop_back(); NetNet *net; const NetExpr *par; NetEvent *eve; const NetExpr *ex1, *ex2; /* Add the implicit this reference when requested. */ if (add_this_flag) { assert(use_path.empty()); use_path.push_front(name_component_t(perm_string::literal("@"))); } // There is no signal to search for so this cannot be a method. if (use_path.empty()) return 0; // Search for an object using the use_path. This should // resolve to a class object. Note that the "this" symbol // (internally represented as "@") is handled by there being a // "this" object in the instance scope. symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); if (net == 0) return 0; // Is this a delete method for dynamic arrays? if (net->darray_type() && method_name=="delete") { return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_darray_method$delete"); } if (net->queue_type()) { if (method_name=="push_back") return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_queue_method$push_back"); if (method_name=="push_front") return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_queue_method$push_front"); } if (const netclass_t*class_type = net->class_type()) { NetScope*task = class_type->method_from_name(method_name); if (task == 0) { cerr << get_fileline() << ": internal error: " << "Can't find task " << method_name << " in class " << class_type->get_name() << endl; des->errors += 1; return 0; } if (debug_elaborate) { cerr << get_fileline() << ": PCallTask::elaborate_method_: " << "Elaborate " << class_type->get_name() << " method " << task->basename() << endl; } NetESignal*use_this = new NetESignal(net); use_this->set_line(*this); return elaborate_build_call_(des, scope, task, use_this); } return 0; } /* * If during elaboration we determine (for sure) that we are calling a * task (and not just a void function) then this method tests if that * task call is allowed in the current context. If so, return true. If * not, print and error message and return false; */ bool PCallTask::test_task_calls_ok_(Design*des, NetScope*scope) const { if (scope->in_func()) { cerr << get_fileline() << ": error: Functions cannot enable/call " "tasks." << endl; des->errors += 1; return false; } if (scope->in_final()) { cerr << get_fileline() << ": error: final procedures cannot " "enable/call tasks." << endl; des->errors += 1; return false; } return true; } NetProc* PCallTask::elaborate_function_(Design*des, NetScope*scope) const { NetFuncDef*func = des->find_function(scope, path_); // This is not a function, so this task call cannot be a function // call with a missing return assignment. if (! func) return 0; // Generate a function call version of this task call. PExpr*rval = new PECallFunction(package_, path_, parms_); rval->set_file(get_file()); rval->set_lineno(get_lineno()); // Generate an assign to nothing. PAssign*tmp = new PAssign(0, rval); tmp->set_file(get_file()); tmp->set_lineno(get_lineno()); cerr << get_fileline() << ": warning: User function '" << peek_tail_name(path_) << "' is being called as a task." << endl; // Elaborate the assignment to a dummy variable. return tmp->elaborate(des, scope); } NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, NetScope*task, NetExpr*use_this) const { NetBaseDef*def = 0; if (task->type() == NetScope::TASK) { def = task->task_def(); // OK, this is certainly a TASK that I'm calling. Make // sure that is OK where I am. Even if this test fails, // continue with the elaboration as if it were OK so // that we can catch more errors. test_task_calls_ok_(des, scope); } else if (task->type() == NetScope::FUNC) { NetFuncDef*tmp = task->func_def(); if (tmp->return_sig() != 0) { cerr << get_fileline() << ": error: " << "Calling a non-void function as a task." << endl; des->errors += 1; } def = tmp; } /* The caller has checked the parms_ size to make sure it matches the task definition, so we can just use the task definition to get the parm_count. */ unsigned parm_count = def->port_count(); if (parms_.size() > parm_count) { cerr << get_fileline() << ": error: " << "Too many arguments (" << parms_.size() << ", expecting " << parm_count << ")" << " in call to task." << endl; des->errors += 1; } NetBlock*block = new NetBlock(NetBlock::SEQU, 0); block->set_line(*this); /* Detect the case where the definition of the task is known empty. In this case, we need not bother with calls to the task, all the assignments, etc. Just return a no-op. */ if (const NetBlock*tp = dynamic_cast(def->proc())) { if (tp->proc_first() == 0) return block; } /* If this is an automatic task, generate a statement to allocate the local storage. */ if (task->is_auto()) { NetAlloc*ap = new NetAlloc(task); ap->set_line(*this); block->append(ap); } /* If this is a method call, then the use_this pointer will have an expression for the "this" argument. The "this" argument is the first argument of any method, so emit it here. */ if (use_this) { ivl_assert(*this, def->port_count() >= 1); NetNet*port = def->port(0); ivl_assert(*this, port->port_type()==NetNet::PINPUT); NetAssign_*lv = new NetAssign_(port); NetAssign*pr = new NetAssign(lv, use_this); pr->set_line(*this); block->append(pr); } /* Generate assignment statement statements for the input and INOUT ports of the task. These are managed by writing assignments with the task port the l-value and the passed expression the r-value. We know by definition that the port is a reg type, so this elaboration is pretty obvious. */ for (unsigned idx = use_this?1:0 ; idx < parm_count ; idx += 1) { size_t parms_idx = use_this? idx-1 : idx; NetNet*port = def->port(idx); assert(port->port_type() != NetNet::NOT_A_PORT); if (port->port_type() == NetNet::POUTPUT) continue; NetAssign_*lv = new NetAssign_(port); unsigned wid = count_lval_width(lv); ivl_variable_type_t lv_type = lv->expr_type(); NetExpr*rv = 0; if (parms_idx < parms_.size() && parms_[parms_idx]) { rv = elaborate_rval_expr(des, scope, port->net_type(), lv_type, wid, parms_ [parms_idx]); if (NetEEvent*evt = dynamic_cast (rv)) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " "task argument." << endl; des->errors += 1; continue; } } else if (def->port_defe(idx)) { if (! gn_system_verilog()) { cerr << get_fileline() << ": internal error: " << "Found (and using) default task expression " "requires SystemVerilog." << endl; des->errors += 1; } rv = def->port_defe(idx); if (lv_type==IVL_VT_BOOL||lv_type==IVL_VT_LOGIC) rv = pad_to_width(rv->dup_expr(), wid, *this); } else { cerr << get_fileline() << ": error: " << "Missing argument " << (idx+1) << " of call to task." << endl; des->errors += 1; continue; } NetAssign*pr = new NetAssign(lv, rv); pr->set_line(*this); block->append(pr); } /* Generate the task call proper... */ NetUTask*cur = new NetUTask(task); cur->set_line(*this); block->append(cur); /* Generate assignment statements for the output and INOUT ports of the task. The l-value in this case is the expression passed as a parameter, and the r-value is the port to be copied out. We know by definition that the r-value of this copy-out is the port, which is a reg. The l-value, however, may be any expression that can be a target to a procedural assignment, including a memory word. */ for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { NetNet*port = def->port(idx); /* Skip input ports. */ assert(port->port_type() != NetNet::NOT_A_PORT); if (port->port_type() == NetNet::PINPUT) continue; /* Elaborate an l-value version of the port expression for output and inout ports. If the expression does not exist or is not a valid l-value print an error message. Note that the elaborate_lval method already printed a detailed message for the latter case. */ NetAssign_*lv = 0; if (idx < parms_.size() && parms_[idx]) { lv = parms_[idx]->elaborate_lval(des, scope, false, false); if (lv == 0) { cerr << parms_[idx]->get_fileline() << ": error: " << "I give up on task port " << (idx+1) << " expression: " << *parms_[idx] << endl; } } else if (port->port_type() == NetNet::POUTPUT) { // Output ports were skipped earlier, so // report the error now. cerr << get_fileline() << ": error: " << "Missing argument " << (idx+1) << " of call to task." << endl; des->errors += 1; } if (lv == 0) continue; NetExpr*rv = new NetESignal(port); /* Handle any implicit cast. */ unsigned lv_width = count_lval_width(lv); if (lv->expr_type() != rv->expr_type()) { switch (lv->expr_type()) { case IVL_VT_REAL: rv = cast_to_real(rv); break; case IVL_VT_BOOL: rv = cast_to_int2(rv, lv_width); break; case IVL_VT_LOGIC: rv = cast_to_int4(rv, lv_width); break; default: /* Don't yet know how to handle this. */ ivl_assert(*this, 0); break; } } rv = pad_to_width(rv, lv_width, *this); /* Generate the assignment statement. */ NetAssign*ass = new NetAssign(lv, rv); ass->set_line(*this); block->append(ass); } /* If this is an automatic task, generate a statement to free the local storage. */ if (task->is_auto()) { NetFree*fp = new NetFree(task); fp->set_line(*this); block->append(fp); } return block; } /* * Elaborate a procedural continuous assign. This really looks very * much like other procedural assignments, at this point, but there * is no delay to worry about. The code generator will take care of * the differences between continuous assign and normal assignments. */ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const { NetCAssign*dev = 0; assert(scope); if (scope->is_auto() && lval_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be assigned values using procedural " "continuous assignments." << endl; des->errors += 1; return 0; } if (scope->is_auto() && expr_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be referenced in procedural " "continuous assignments." << endl; des->errors += 1; return 0; } NetAssign_*lval = lval_->elaborate_lval(des, scope, true, false); if (lval == 0) return 0; unsigned lwid = count_lval_width(lval); ivl_variable_type_t ltype = lval->expr_type(); // Need to figure out a better thing to do about the // lv_net_type argument to elaborate_rval_expr here. This // would entail getting the NetAssign_ to give us an // ivl_type_t as needed. NetExpr*rexp = elaborate_rval_expr(des, scope, 0, ltype, lwid, expr_); if (rexp == 0) return 0; dev = new NetCAssign(lval, rexp); if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate cassign," << " lval width=" << lwid << " rval width=" << rexp->expr_width() << " rval=" << *rexp << endl; } dev->set_line(*this); return dev; } NetDeassign* PDeassign::elaborate(Design*des, NetScope*scope) const { assert(scope); if (scope->is_auto() && lval_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be assigned values using procedural " "continuous assignments." << endl; des->errors += 1; return 0; } NetAssign_*lval = lval_->elaborate_lval(des, scope, true, false); if (lval == 0) return 0; NetDeassign*dev = new NetDeassign(lval); dev->set_line( *this ); return dev; } /* * Elaborate the delay statement (of the form # ) as a * NetPDelay object. If the expression is constant, evaluate it now * and make a constant delay. If not, then pass an elaborated * expression to the constructor of NetPDelay so that the code * generator knows to evaluate the expression at run time. */ NetProc* PDelayStatement::elaborate(Design*des, NetScope*scope) const { assert(scope); if (scope->in_func()) { cerr << get_fileline() << ": error: functions cannot have " "delay statements." << endl; des->errors += 1; return 0; } if (scope->in_final()) { cerr << get_fileline() << ": error: final procedures cannot " "have delay statements." << endl; des->errors += 1; return 0; } /* This call evaluates the delay expression to a NetEConst, if possible. This includes transforming NetECReal values to integers, and applying the proper scaling. */ NetExpr*dex = elaborate_delay_expr(delay_, des, scope); NetPDelay *obj; if (NetEConst*tmp = dynamic_cast(dex)) { if (statement_) obj = new NetPDelay(tmp->value().as_ulong64(), statement_->elaborate(des, scope)); else obj = new NetPDelay(tmp->value().as_ulong64(), 0); delete dex; } else { if (statement_) obj = new NetPDelay(dex, statement_->elaborate(des, scope)); else obj = new NetPDelay(dex, 0); } obj->set_line(*this); return obj; } /* * The disable statement is not yet supported. */ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const { assert(scope); /* If the disable scope_ is empty then this is a SystemVerilog * disable fork statement. */ if (scope_.empty()) { if (gn_system_verilog()) { NetDisable*obj = new NetDisable(0); obj->set_line(*this); return obj; } else { cerr << get_fileline() << ": error: 'disable fork' requires SystemVerilog." << endl; des->errors += 1; return 0; } } list spath = eval_scope_path(des, scope, scope_); NetScope*target = des->find_scope(scope, spath); if (target == 0) { cerr << get_fileline() << ": error: Cannot find scope " << scope_ << " in " << scope_path(scope) << endl; des->errors += 1; return 0; } switch (target->type()) { case NetScope::FUNC: cerr << get_fileline() << ": error: Cannot disable functions." << endl; des->errors += 1; return 0; case NetScope::MODULE: cerr << get_fileline() << ": error: Cannot disable modules." << endl; des->errors += 1; return 0; default: break; } NetDisable*obj = new NetDisable(target); obj->set_line(*this); return obj; } /* * The do/while loop is fairly directly represented in the netlist. */ NetProc* PDoWhile::elaborate(Design*des, NetScope*scope) const { NetExpr*ce = elab_and_eval(des, scope, cond_, -1); NetProc*sub; if (statement_) sub = statement_->elaborate(des, scope); else sub = new NetBlock(NetBlock::SEQU, 0); if (ce == 0 || sub == 0) { delete ce; delete sub; return 0; } NetDoWhile*loop = new NetDoWhile(ce, sub); loop->set_line(*this); return loop; } /* * An event statement is an event delay of some sort, attached to a * statement. Some Verilog examples are: * * @(posedge CLK) $display("clock rise"); * @event_1 $display("event triggered."); * @(data or negedge clk) $display("data or clock fall."); * * The elaborated netlist uses the NetEvent, NetEvWait and NetEvProbe * classes. The NetEvWait class represents the part of the netlist * that is executed by behavioral code. The process starts waiting on * the NetEvent when it executes the NetEvWait step. Net NetEvProbe * and NetEvTrig are structural and behavioral equivalents that * trigger the event, and awakens any processes blocking in the * associated wait. * * The basic data structure is: * * NetEvWait ---/---> NetEvent <----\---- NetEvProbe * ... | | ... * NetEvWait ---+ +---- NetEvProbe * | ... * +---- NetEvTrig * * That is, many NetEvWait statements may wait on a single NetEvent * object, and Many NetEvProbe objects may trigger the NetEvent * object. The many NetEvWait objects pointing to the NetEvent object * reflects the possibility of different places in the code blocking * on the same named event, like so: * * event foo; * [...] * always begin @foo ; @foo end * * This tends to not happen with signal edges. The multiple probes * pointing to the same event reflect the possibility of many * expressions in the same blocking statement, like so: * * wire reset, clk; * [...] * always @(reset or posedge clk) ; * * Conjunctions like this cause a NetEvent object be created to * represent the overall conjunction, and NetEvProbe objects for each * event expression. * * If the NetEvent object represents a named event from the source, * then there are NetEvTrig objects that represent the trigger * statements instead of the NetEvProbe objects representing signals. * For example: * * event foo; * always @foo ; * initial begin * [...] * -> foo; * [...] * -> foo; * [...] * end * * Each trigger statement in the source generates a separate NetEvTrig * object in the netlist. Those trigger objects are elaborated * elsewhere. * * Additional complications arise when named events show up in * conjunctions. An example of such a case is: * * event foo; * wire bar; * always @(foo or posedge bar) ; * * Since there is by definition a NetEvent object for the foo object, * this is handled by allowing the NetEvWait object to point to * multiple NetEvent objects. All the NetEvProbe based objects are * collected and pointed as the synthetic NetEvent object, and all the * named events are added into the list of NetEvent object that the * NetEvWait object can refer to. */ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, NetProc*enet) const { assert(scope); if (scope->in_func()) { cerr << get_fileline() << ": error: functions cannot have " "event statements." << endl; des->errors += 1; return 0; } if (scope->in_final()) { cerr << get_fileline() << ": error: final procedures cannot " "have event statements." << endl; des->errors += 1; return 0; } /* Create a single NetEvent and NetEvWait. Then, create a NetEvProbe for each conjunctive event in the event list. The NetEvProbe objects all refer back to the NetEvent object. */ NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*this); unsigned expr_count = 0; NetEvWait*wa = new NetEvWait(enet); wa->set_line(*this); /* If there are no expressions, this is a signal that it is an @* statement. Generate an expression to use. */ if (expr_.count() == 0) { assert(enet); /* For synthesis we want just the inputs, but for the rest we * want inputs and outputs that may cause a value to change. */ extern bool synthesis; /* Synthesis flag from main.cc */ bool rem_out = false; if (synthesis) { rem_out = true; } NexusSet*nset = enet->nex_input(rem_out); if (nset == 0) { cerr << get_fileline() << ": error: Unable to elaborate:" << endl; enet->dump(cerr, 6); des->errors += 1; return enet; } if (nset->size() == 0) { cerr << get_fileline() << ": warning: @* found no " "sensitivities so it will never trigger." << endl; /* Add the currently unreferenced event to the scope. */ scope->add_event(ev); /* Delete the current wait, create a new one with no * statement and add the event to it. This creates a * perpetual wait since nothing will ever trigger the * unreferenced event. */ delete wa; wa = new NetEvWait(0); wa->set_line(*this); wa->add_event(ev); return wa; } NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, nset->size()); for (unsigned idx = 0 ; idx < nset->size() ; idx += 1) connect(nset->at(idx).lnk, pr->pin(idx)); delete nset; des->add_node(pr); expr_count = 1; } else for (unsigned idx = 0 ; idx < expr_.count() ; idx += 1) { assert(expr_[idx]->expr()); /* If the expression is an identifier that matches a named event, then handle this case all at once and skip the rest of the expression handling. */ if (PEIdent*id = dynamic_cast(expr_[idx]->expr())) { NetNet* sig = 0; const NetExpr*par = 0; NetEvent* eve = 0; NetScope*found_in = symbol_search(this, des, scope, id->path(), sig, par, eve); if (found_in && eve) { wa->add_event(eve); /* You can not look for the posedge or negedge of * an event. */ if (expr_[idx]->type() != PEEvent::ANYEDGE) { cerr << get_fileline() << ": error: "; switch (expr_[idx]->type()) { case PEEvent::POSEDGE: cerr << "posedge"; break; case PEEvent::NEGEDGE: cerr << "negedge"; break; default: cerr << "unknown edge type!"; assert(0); } cerr << " can not be used with a named event (" << eve->name() << ")." << endl; des->errors += 1; } continue; } } /* So now we have a normal event expression. Elaborate the sub-expression as a net and decide how to handle the edge. */ if (scope->is_auto()) { if (! dynamic_cast(expr_[idx]->expr())) { cerr << get_fileline() << ": sorry, complex event " "expressions are not yet supported in " "automatic tasks." << endl; des->errors += 1; return 0; } } NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), -1); if (tmp == 0) { expr_[idx]->dump(cerr); cerr << endl; des->errors += 1; continue; } NetNet*expr = tmp->synthesize(des, scope, tmp); if (expr == 0) { expr_[idx]->dump(cerr); cerr << endl; des->errors += 1; continue; } assert(expr); delete tmp; unsigned pins = (expr_[idx]->type() == PEEvent::ANYEDGE) ? expr->pin_count() : 1; NetEvProbe*pr; switch (expr_[idx]->type()) { case PEEvent::POSEDGE: pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::POSEDGE, pins); break; case PEEvent::NEGEDGE: pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::NEGEDGE, pins); break; case PEEvent::ANYEDGE: pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, pins); break; default: pr = NULL; assert(0); } for (unsigned p = 0 ; p < pr->pin_count() ; p += 1) connect(pr->pin(p), expr->pin(p)); des->add_node(pr); expr_count += 1; } /* If there was at least one conjunction that was an expression (and not a named event) then add this event. Otherwise, we didn't use it so delete it. */ if (expr_count > 0) { scope->add_event(ev); wa->add_event(ev); /* NOTE: This event that I am adding to the wait may be a duplicate of another event somewhere else. However, I don't know that until all the modules are hooked up, so it is best to leave find_similar_event to after elaboration. */ } else { delete ev; } return wa; } /* * This is the special case of the event statement, the wait * statement. This is elaborated into a slightly more complicated * statement that uses non-wait statements: * * wait () * * becomes * * begin * while (1 !== ) * @() ; * ; * end */ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, NetProc*enet) const { assert(scope); assert(expr_.count() == 1); if (scope->in_func()) { cerr << get_fileline() << ": error: functions cannot have " "wait statements." << endl; des->errors += 1; return 0; } if (scope->in_final()) { cerr << get_fileline() << ": error: final procedures cannot " "have wait statements." << endl; des->errors += 1; return 0; } PExpr *pe = expr_[0]->expr(); /* Elaborate wait expression. Don't eval yet, we will do that shortly, after we apply a reduction or. */ PExpr::width_mode_t mode = PExpr::SIZED; pe->test_width(des, scope, mode); NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), PExpr::NO_FLAGS); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " wait condition expression." << endl; des->errors += 1; return 0; } // If the condition expression is more than 1 bits, then // generate a reduction operator to get the result down to // one bit. In other words, Turn into |; if (expr->expr_width() < 1) { cerr << get_fileline() << ": internal error: " "incomprehensible wait expression width (0)." << endl; return 0; } if (expr->expr_width() > 1) { assert(expr->expr_width() > 1); NetEUReduce*cmp = new NetEUReduce('|', expr); cmp->set_line(*pe); expr = cmp; } /* precalculate as much as possible of the wait expression. */ eval_expr(expr); /* Detect the unusual case that the wait expression is constant. Constant true is OK (it becomes transparent) but constant false is almost certainly not what is intended. */ assert(expr->expr_width() == 1); if (NetEConst*ce = dynamic_cast(expr)) { verinum val = ce->value(); assert(val.len() == 1); /* Constant true -- wait(1) reduces to . */ if (val[0] == verinum::V1) { delete expr; assert(enet); return enet; } /* Otherwise, false. wait(0) blocks permanently. */ cerr << get_fileline() << ": warning: wait expression is " << "constant false." << endl; cerr << get_fileline() << ": : The statement will " << "block permanently." << endl; /* Create an event wait and an otherwise unreferenced event variable to force a perpetual wait. */ NetEvent*wait_event = new NetEvent(scope->local_symbol()); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0); wait->add_event(wait_event); wait->set_line(*this); delete expr; delete enet; return wait; } /* Invert the sense of the test with an exclusive NOR. In other words, if this adjusted expression returns TRUE, then wait. */ assert(expr->expr_width() == 1); expr = new NetEBComp('N', expr, new NetEConst(verinum(verinum::V1))); expr->set_line(*pe); eval_expr(expr); NetEvent*wait_event = new NetEvent(scope->local_symbol()); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0 /* noop */); wait->add_event(wait_event); wait->set_line(*this); NexusSet*wait_set = expr->nex_input(); if (wait_set == 0) { cerr << get_fileline() << ": internal error: No NexusSet" << " from wait expression." << endl; des->errors += 1; return 0; } if (wait_set->size() == 0) { cerr << get_fileline() << ": internal error: Empty NexusSet" << " from wait expression." << endl; des->errors += 1; return 0; } NetEvProbe*wait_pr = new NetEvProbe(scope, scope->local_symbol(), wait_event, NetEvProbe::ANYEDGE, wait_set->size()); for (unsigned idx = 0; idx < wait_set->size() ; idx += 1) connect(wait_set->at(idx).lnk, wait_pr->pin(idx)); delete wait_set; des->add_node(wait_pr); NetWhile*loop = new NetWhile(expr, wait); loop->set_line(*this); /* If there is no real substatement (i.e., "wait (foo) ;") then we are done. */ if (enet == 0) return loop; /* Create a sequential block to combine the wait loop and the delayed statement. */ NetBlock*block = new NetBlock(NetBlock::SEQU, 0); block->append(loop); block->append(enet); block->set_line(*this); return block; } /* * This is a special case of the event statement, the wait fork * statement. This is elaborated into a simplified statement. * * wait fork; * * becomes * * @(0) ; */ NetProc* PEventStatement::elaborate_wait_fork(Design*des, NetScope*scope) const { assert(scope); assert(expr_.count() == 1); assert(expr_[0] == 0); assert(! statement_); if (scope->in_func()) { cerr << get_fileline() << ": error: functions cannot have " "wait fork statements." << endl; des->errors += 1; return 0; } if (scope->in_final()) { cerr << get_fileline() << ": error: final procedures cannot " "have wait fork statements." << endl; des->errors += 1; return 0; } if (gn_system_verilog()) { NetEvWait*wait = new NetEvWait(0 /* noop */); wait->add_event(0); wait->set_line(*this); return wait; } else { cerr << get_fileline() << ": error: 'wait fork' requires SystemVerilog." << endl; des->errors += 1; return 0; } } NetProc* PEventStatement::elaborate(Design*des, NetScope*scope) const { /* Check to see if this is a wait fork statement. */ if ((expr_.count() == 1) && (expr_[0] == 0)) return elaborate_wait_fork(des, scope); NetProc*enet = 0; if (statement_) { enet = statement_->elaborate(des, scope); if (enet == 0) return 0; } else { enet = new NetBlock(NetBlock::SEQU, 0); enet->set_line(*this); } if ((expr_.count() == 1) && (expr_[0]->type() == PEEvent::POSITIVE)) return elaborate_wait(des, scope, enet); return elaborate_st(des, scope, enet); } /* * Forever statements are represented directly in the netlist. It is * theoretically possible to use a while structure with a constant * expression to represent the loop, but why complicate the code * generators so? */ NetProc* PForever::elaborate(Design*des, NetScope*scope) const { NetProc*stat; if (statement_) stat = statement_->elaborate(des, scope); else stat = new NetBlock(NetBlock::SEQU, 0); if (stat == 0) return 0; NetForever*proc = new NetForever(stat); proc->set_line(*this); return proc; } /* * Force is like a procedural assignment, most notably procedural * continuous assignment: * * force = * * The can be anything that a normal behavioral assignment can * take, plus net signals. This is a little bit more lax than the * other procedural assignments. */ NetForce* PForce::elaborate(Design*des, NetScope*scope) const { NetForce*dev = 0; assert(scope); if (scope->is_auto() && lval_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be assigned values using procedural " "force statements." << endl; des->errors += 1; return 0; } if (scope->is_auto() && expr_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be referenced in procedural force " "statements." << endl; des->errors += 1; return 0; } NetAssign_*lval = lval_->elaborate_lval(des, scope, false, true); if (lval == 0) return 0; unsigned lwid = count_lval_width(lval); ivl_variable_type_t ltype = lval->expr_type(); // Like a variety of other assigns, we need to figure out a // better way to get a reasonable lv_net_type value, and that // probably will involve NetAssign_ having a method for // synthesizing one as needed. NetExpr*rexp = elaborate_rval_expr(des, scope, 0, ltype, lwid, expr_); if (rexp == 0) return 0; dev = new NetForce(lval, rexp); if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate force," << " lval width=" << lval->lwidth() << " rval width=" << rexp->expr_width() << " rval=" << *rexp << endl; } dev->set_line(*this); return dev; } static void find_property_in_class(const LineInfo&loc, const NetScope*scope, perm_string name, const netclass_t*&found_in, int&property) { found_in = find_class_containing_scope(loc, scope); property = -1; if (found_in==0) return; property = found_in->property_idx_from_name(name); if (property < 0) { found_in = 0; return; } } /* * The foreach statement can be written as a for statement like so: * * for ( = $low() ; <= $high() ; += 1) * * * The variable is already known to be in the containing named * block scope, which was created by the parser. */ NetProc* PForeach::elaborate(Design*des, NetScope*scope) const { // Locate the signal for the array variable pform_name_t array_name; array_name.push_back(name_component_t(array_var_)); NetNet*array_sig = des->find_signal(scope, array_name); // And if necessary, look for the class property that is // referenced. const netclass_t*class_scope = 0; int class_property = -1; if (array_sig == 0) find_property_in_class(*this, scope, array_var_, class_scope, class_property); if (debug_elaborate && array_sig) { cerr << get_fileline() << ": PForeach::elaborate: " << "Found array_sig in " << scope_path(array_sig->scope()) << "." << endl; } if (debug_elaborate && class_scope) { cerr << get_fileline() << ": PForeach::elaborate: " << "Found array_sig property (" << class_property << ") in class " << class_scope->get_name() << " as " << *class_scope->get_prop_type(class_property) << "." << endl; } if (class_scope!=0 && class_property >= 0) { ivl_type_t ptype = class_scope->get_prop_type(class_property); const netsarray_t*atype = dynamic_cast (ptype); if (atype == 0) { cerr << get_fileline() << ": error: " << "I can't handle the type of " << array_var_ << " as a foreach loop." << endl; des->errors += 1; return 0; } const std::vector&dims = atype->static_dimensions(); if (dims.size() < index_vars_.size()) { cerr << get_fileline() << ": error: " << "class " << class_scope->get_name() << " property " << array_var_ << " has too few dimensions for foreach dimension list." << endl; des->errors += 1; return 0; } return elaborate_static_array_(des, scope, dims); } if (array_sig == 0) { cerr << get_fileline() << ": error:" << " Unable to find foreach array " << array_name << " in scope " << scope_path(scope) << "." << endl; des->errors += 1; return 0; } ivl_assert(*this, array_sig); if (debug_elaborate) { cerr << get_fileline() << ": PForeach::elaborate: " << "Scan array " << array_sig->name() << " of " << array_sig->data_type() << " with " << array_sig->unpacked_dimensions() << " unpacked" << " and " << array_sig->packed_dimensions() << " packed dimensions." << endl; } // Classic arrays are processed this way. if (array_sig->data_type()==IVL_VT_BOOL) return elaborate_static_array_(des, scope, array_sig->unpacked_dims()); if (array_sig->data_type()==IVL_VT_LOGIC) return elaborate_static_array_(des, scope, array_sig->unpacked_dims()); if (array_sig->unpacked_dimensions() >= index_vars_.size()) return elaborate_static_array_(des, scope, array_sig->unpacked_dims()); // At this point, we know that the array is dynamic so we // handle that slightly differently, using run-time tests. if (index_vars_.size() != 1) { cerr << get_fileline() << ": sorry: " << "Multi-index foreach loops not supported." << endl; des->errors += 1; } // Get the signal for the index variable. pform_name_t index_name; index_name.push_back(name_component_t(index_vars_[0])); NetNet*idx_sig = des->find_signal(scope, index_name); ivl_assert(*this, idx_sig); NetESignal*array_exp = new NetESignal(array_sig); array_exp->set_line(*this); NetESignal*idx_exp = new NetESignal(idx_sig); idx_exp->set_line(*this); // Make an initialization expression for the index. NetESFunc*init_expr = new NetESFunc("$low", IVL_VT_BOOL, 32, 1); init_expr->set_line(*this); init_expr->parm(0, array_exp); // Make a condition expression: idx <= $high(array) NetESFunc*high_exp = new NetESFunc("$high", IVL_VT_BOOL, 32, 1); high_exp->set_line(*this); high_exp->parm(0, array_exp); NetEBComp*cond_expr = new NetEBComp('L', idx_exp, high_exp); cond_expr->set_line(*this); /* Elaborate the statement that is contained in the foreach loop. */ NetProc*sub; if (statement_) sub = statement_->elaborate(des, scope); else sub = new NetBlock(NetBlock::SEQU, 0); /* Make a step statement: idx += 1 */ NetAssign_*idx_lv = new NetAssign_(idx_sig); NetEConst*step_val = make_const_val(1); NetAssign*step = new NetAssign(idx_lv, '+', step_val); step->set_line(*this); NetForLoop*stmt = new NetForLoop(idx_sig, init_expr, cond_expr, sub, step); stmt->set_line(*this); stmt->wrap_up(); return stmt; } /* * This is a variant of the PForeach::elaborate() method that handles * the case that the array has static dimensions. We can use constants * and possibly do some optimizations. */ NetProc* PForeach::elaborate_static_array_(Design*des, NetScope*scope, const vector&dims) const { if (debug_elaborate) { cerr << get_fileline() << ": PForeach::elaborate_static_array_: " << "Handle as array with static dimensions." << endl; } ivl_assert(*this, index_vars_.size() > 0); ivl_assert(*this, dims.size() == index_vars_.size()); NetProc*sub; if (statement_) sub = statement_->elaborate(des, scope); else sub = new NetBlock(NetBlock::SEQU, 0); NetForLoop*stmt = 0; for (int idx_idx = index_vars_.size()-1 ; idx_idx >= 0 ; idx_idx -= 1) { const netrange_t&idx_range = dims[idx_idx]; // Get the $high and $low constant values for this slice // of the array. NetEConst*hig_expr = make_const_val_s(idx_range.get_msb()); NetEConst*low_expr = make_const_val_s(idx_range.get_lsb()); if (idx_range.get_msb() < idx_range.get_lsb()) { NetEConst*tmp = hig_expr; hig_expr = low_expr; low_expr = tmp; } hig_expr->set_line(*this); low_expr->set_line(*this); pform_name_t idx_name; idx_name.push_back(name_component_t(index_vars_[idx_idx])); NetNet*idx_sig = des->find_signal(scope, idx_name); ivl_assert(*this, idx_sig); // Make the condition expression <= $high(slice) NetESignal*idx_expr = new NetESignal(idx_sig); idx_expr->set_line(*this); NetEBComp*cond_expr = new NetEBComp('L', idx_expr, hig_expr); cond_expr->set_line(*this); // Make the step statement: += 1 NetAssign_*idx_lv = new NetAssign_(idx_sig); NetEConst*step_val = make_const_val_s(1); NetAssign*step = new NetAssign(idx_lv, '+', step_val); step->set_line(*this); stmt = new NetForLoop(idx_sig, low_expr, cond_expr, sub, step); stmt->set_line(*this); stmt->wrap_up(); sub = stmt; } return stmt? stmt : sub; } /* * elaborate the for loop as the equivalent while loop. This eases the * task for the target code generator. The structure is: * * begin : top * name1_ = expr1_; * while (cond_) begin : body * statement_; * name2_ = expr2_; * end * end */ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const { NetExpr*initial_expr; assert(scope); const PEIdent*id1 = dynamic_cast(name1_); assert(id1); /* make the expression, and later the initial assignment to the condition variable. The statement in the for loop is very specifically an assignment. */ NetNet*sig = des->find_signal(scope, id1->path()); if (sig == 0) { cerr << id1->get_fileline() << ": register ``" << id1->path() << "'' unknown in " << scope_path(scope) << "." << endl; des->errors += 1; return 0; } assert(sig); /* Make the r-value of the initial assignment, and size it properly. Then use it to build the assignment statement. */ initial_expr = elaborate_rval_expr(des, scope, sig->net_type(), sig->data_type(), sig->vector_width(), expr1_); if (debug_elaborate && initial_expr) { cerr << get_fileline() << ": debug: FOR initial assign: " << sig->name() << " = " << *initial_expr << endl; } /* Elaborate the statement that is contained in the for loop. If there is an error, this will return 0 and I should skip the append. No need to worry, the error has been reported so it's OK that the netlist is bogus. */ NetProc*sub; if (statement_) sub = statement_->elaborate(des, scope); else sub = new NetBlock(NetBlock::SEQU, 0); /* Now elaborate the for_step statement. I really should do some error checking here to make sure the step statement really does step the variable. */ NetProc*step = step_->elaborate(des, scope); /* Elaborate the condition expression. Try to evaluate it too, in case it is a constant. This is an interesting case worthy of a warning. */ NetExpr*ce = elab_and_eval(des, scope, cond_, -1); if (dynamic_cast(ce)) { cerr << get_fileline() << ": warning: condition expression " "of for-loop is constant." << endl; } /* Error recovery - if we failed to elaborate any of the loop expressions, give up now. */ if (initial_expr == 0 || ce == 0 || step == 0 || sub == 0) { delete initial_expr; delete ce; delete step; delete sub; return 0; } /* All done, build up the loop. */ NetForLoop*loop = new NetForLoop(sig, initial_expr, ce, sub, step); loop->set_line(*this); loop->wrap_up(); return loop; } /* * (See the PTask::elaborate methods for basic common stuff.) * * The return value of a function is represented as a reg variable * within the scope of the function that has the name of the * function. So for example with the function: * * function [7:0] incr; * input [7:0] in1; * incr = in1 + 1; * endfunction * * The scope of the function is .incr and there is a reg * variable .incr.incr. The elaborate_1 method is called with * the scope of the function, so the return reg is easily located. * * The function parameters are all inputs, except for the synthetic * output parameter that is the return value. The return value goes * into port 0, and the parameters are all the remaining ports. */ void PFunction::elaborate(Design*des, NetScope*scope) const { if (scope->elab_stage() > 2) return; scope->set_elab_stage(3); NetFuncDef*def = scope->func_def(); if (def == 0) { cerr << get_fileline() << ": internal error: " << "No function definition for function " << scope_path(scope) << endl; des->errors += 1; return; } assert(def); ivl_assert(*this, statement_); NetProc*st = statement_->elaborate(des, scope); if (st == 0) { cerr << statement_->get_fileline() << ": error: Unable to elaborate " "statement in function " << scope->basename() << "." << endl; scope->is_const_func(true); // error recovery des->errors += 1; return; } def->set_proc(st); } NetProc* PRelease::elaborate(Design*des, NetScope*scope) const { assert(scope); if (scope->is_auto() && lval_->has_aa_term(des, scope)) { cerr << get_fileline() << ": error: automatically allocated " "variables may not be assigned values using procedural " "force statements." << endl; des->errors += 1; return 0; } NetAssign_*lval = lval_->elaborate_lval(des, scope, false, true); if (lval == 0) return 0; NetRelease*dev = new NetRelease(lval); dev->set_line( *this ); return dev; } NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const { assert(scope); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": Unable to elaborate" " repeat expression." << endl; des->errors += 1; return 0; } // If the expression is real, convert to an integer. 64 bits // should be more enough for any real use case. if (expr->expr_type() == IVL_VT_REAL) expr = cast_to_int4(expr, 64); NetProc*stat; if (statement_) stat = statement_->elaborate(des, scope); else stat = new NetBlock(NetBlock::SEQU, 0); if (stat == 0) return 0; // If the expression is a constant, handle certain special // iteration counts. if (NetEConst*ce = dynamic_cast(expr)) { long val = ce->value().as_long(); if (val <= 0) { delete expr; delete stat; return new NetBlock(NetBlock::SEQU, 0); } else if (val == 1) { delete expr; return stat; } } NetRepeat*proc = new NetRepeat(expr, stat); proc->set_line( *this ); return proc; } NetProc* PReturn::elaborate(Design*des, NetScope*scope) const { NetScope*target = scope; for (;;) { if (target == 0) { cerr << get_fileline() << ": error: " << "Return statement is not in a function." << endl; des->errors += 1; return 0; } if (target->type() == NetScope::FUNC) break; if (target->type() == NetScope::TASK) { cerr << get_fileline() << ": error: " << "Cannot \"return\" from tasks." << endl; des->errors += 1; return 0; } if (target->type()==NetScope::BEGIN_END) { target = target->parent(); continue; } cerr << get_fileline() << ": error: " << "Cannot \"return\" from this scope: " << scope_path(target) << endl; des->errors += 1; return 0; } // We don't yet support void functions, so require an // expression for the return statement. if (expr_ == 0) { cerr << get_fileline() << ": error: " << "Return from " << scope_path(target) << " requires a return value expression." << endl; des->errors += 1; return 0; } NetNet*res = target->find_signal(target->basename()); ivl_variable_type_t lv_type = res->data_type(); unsigned long wid = res->vector_width(); NetAssign_*lv = new NetAssign_(res); NetExpr*val = elaborate_rval_expr(des, scope, res->net_type(), lv_type, wid, expr_); NetBlock*proc = new NetBlock(NetBlock::SEQU, 0); proc->set_line( *this ); NetAssign*assn = new NetAssign(lv, val); assn->set_line( *this ); proc->append(assn); NetDisable*disa = new NetDisable(target); disa->set_line( *this ); proc->append( disa ); return proc; } /* * A task definition is elaborated by elaborating the statement that * it contains, and connecting its ports to NetNet objects. The * netlist doesn't really need the array of parameters once elaboration * is complete, but this is the best place to store them. * * The first elaboration pass finds the reg objects that match the * port names, and creates the NetTaskDef object. The port names are * in the form task.port. * * task foo; * output blah; * begin end * endtask * * So in the foo example, the PWire objects that represent the ports * of the task will include a foo.blah for the blah port. This port is * bound to a NetNet object by looking up the name. All of this is * handled by the PTask::elaborate_sig method and the results stashed * in the created NetTaskDef attached to the scope. * * Elaboration pass 2 for the task definition causes the statement of * the task to be elaborated and attached to the NetTaskDef object * created in pass 1. * * NOTE: I am not sure why I bothered to prepend the task name to the * port name when making the port list. It is not really useful, but * that is what I did in pform_make_task_ports, so there it is. */ void PTask::elaborate(Design*des, NetScope*task) const { // Elaborate any processes that are part of this scope that // aren't the definition itself. This can happen, for example, // with variable initialization statements in this scope. elaborate_behaviors_(des, task); NetTaskDef*def = task->task_def(); assert(def); NetProc*st; if (statement_ == 0) { st = new NetBlock(NetBlock::SEQU, 0); } else { st = statement_->elaborate(des, task); if (st == 0) { cerr << statement_->get_fileline() << ": Unable to elaborate " "statement in task " << scope_path(task) << " at " << get_fileline() << "." << endl; return; } } def->set_proc(st); } NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const { assert(scope); NetNet* sig = 0; const NetExpr*par = 0; NetEvent* eve = 0; NetScope*found_in = symbol_search(this, des, scope, event_, sig, par, eve); if (found_in == 0) { cerr << get_fileline() << ": error: event <" << event_ << ">" << " not found." << endl; des->errors += 1; return 0; } if (eve == 0) { cerr << get_fileline() << ": error: <" << event_ << ">" << " is not a named event." << endl; des->errors += 1; return 0; } NetEvTrig*trig = new NetEvTrig(eve); trig->set_line(*this); return trig; } /* * The while loop is fairly directly represented in the netlist. */ NetProc* PWhile::elaborate(Design*des, NetScope*scope) const { NetExpr*ce = elab_and_eval(des, scope, cond_, -1); NetProc*sub; if (statement_) sub = statement_->elaborate(des, scope); else sub = new NetBlock(NetBlock::SEQU, 0); if (ce == 0 || sub == 0) { delete ce; delete sub; return 0; } NetWhile*loop = new NetWhile(ce, sub); loop->set_line(*this); return loop; } bool PProcess::elaborate(Design*des, NetScope*scope) const { scope->in_final(type() == IVL_PR_FINAL); NetProc*cur = statement_->elaborate(des, scope); scope->in_final(false); if (cur == 0) { return false; } NetProcTop*top=new NetProcTop(scope, type(), cur); ivl_assert(*this, top); // Evaluate the attributes for this process, if there // are any. These attributes are to be attached to the // NetProcTop object. struct attrib_list_t*attrib_list; unsigned attrib_list_n = 0; attrib_list = evaluate_attributes(attributes, attrib_list_n, des, scope); for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1) top->attribute(attrib_list[adx].key, attrib_list[adx].val); delete[]attrib_list; top->set_line(*this); des->add_process(top); /* Detect the special case that this is a combinational always block. We want to attach an _ivl_schedule_push attribute to this process so that it starts up and gets into its wait statement before non-combinational code is executed. */ do { if (top->type() != IVL_PR_ALWAYS) break; NetEvWait*st = dynamic_cast(top->statement()); if (st == 0) break; if (st->nevents() != 1) break; NetEvent*ev = st->event(0); if (ev->nprobe() == 0) break; bool anyedge_test = true; for (unsigned idx = 0 ; anyedge_test && (idxnprobe()) ; idx += 1) { const NetEvProbe*pr = ev->probe(idx); if (pr->edge() != NetEvProbe::ANYEDGE) anyedge_test = false; } if (! anyedge_test) break; top->attribute(perm_string::literal("_ivl_schedule_push"), verinum(1)); } while (0); return true; } void PSpecPath::elaborate(Design*des, NetScope*scope) const { uint64_t delay_value[12]; unsigned ndelays = 0; /* Do not elaborate specify delay paths if this feature is turned off. */ if (!gn_specify_blocks_flag) return; ivl_assert(*this, conditional || (condition==0)); ndelays = delays.size(); if (ndelays > 12) ndelays = 12; /* Print a warning if we find default and `timescale based * delays in the design, since this is likely an error. */ if (scope->time_from_timescale()) dly_used_timescale = true; else dly_used_no_timescale = true; if (display_ts_dly_warning && dly_used_no_timescale && dly_used_timescale) { cerr << "warning: Found both default and " "`timescale based delays. Use" << endl; cerr << " -Wtimescale to find the " "module(s) with no `timescale." << endl; display_ts_dly_warning = false; } /* Elaborate the delay values themselves. Remember to scale them for the timescale/precision of the scope. */ for (unsigned idx = 0 ; idx < ndelays ; idx += 1) { PExpr*exp = delays[idx]; NetExpr*cur = elab_and_eval(des, scope, exp, -1); if (NetEConst*con = dynamic_cast (cur)) { verinum fn = con->value(); delay_value[idx] = des->scale_to_precision(fn.as_ulong64(), scope); } else if (NetECReal*rcon = dynamic_cast(cur)) { delay_value[idx] = get_scaled_time_from_real(des, scope, rcon); } else { cerr << get_fileline() << ": error: Path delay value " << "must be constant (" << *cur << ")." << endl; delay_value[idx] = 0; des->errors += 1; } delete cur; } switch (delays.size()) { case 1: case 2: case 3: case 6: case 12: break; default: cerr << get_fileline() << ": error: Incorrect delay configuration." << " Given " << delays.size() << " delay expressions." << endl; ndelays = 1; des->errors += 1; break; } NetNet*condit_sig = 0; if (conditional && condition) { NetExpr*tmp = elab_and_eval(des, scope, condition, -1); ivl_assert(*condition, tmp); // FIXME: Look for constant expressions here? // Get a net form. condit_sig = tmp->synthesize(des, scope, tmp); ivl_assert(*condition, condit_sig); } /* A parallel connection does not support more than a one to one connection (source/destination). */ if (! full_flag_ && ((src.size() != 1) || (dst.size() != 1))) { /* To be compatible with NC-Verilog we allow a parallel connection * with multiple sources/destinations if all the paths are only a * single bit wide (a scalar or a one bit vector). */ bool all_single = true; typedef std::vector::const_iterator str_vec_iter; for (str_vec_iter cur = src.begin(); ( cur != src.end() && all_single); ++ cur) { NetNet *psig = scope->find_signal(*cur); /* We will report a missing signal as invalid later. For * now assume it's a single bit. */ if (psig == 0) continue; if (psig->vector_width() != 1) all_single = false; } for (str_vec_iter cur = dst.begin(); ( cur != dst.end() && all_single); ++ cur) { NetNet *psig = scope->find_signal(*cur); /* The same as above for source paths. */ if (psig == 0) continue; if (psig->vector_width() != 1) all_single = false; } if (! all_single) { cerr << get_fileline() << ": error: Parallel connections " "only support one source/destination path found (" << src.size() << "/" << dst.size() << ")." << endl; des->errors += 1; } } /* Create all the various paths from the path specifier. */ typedef std::vector::const_iterator str_vector_iter; for (str_vector_iter cur = dst.begin() ; cur != dst.end() ; ++ cur ) { if (debug_elaborate) { cerr << get_fileline() << ": debug: Path to " << (*cur); if (condit_sig) cerr << " if " << condit_sig->name(); else if (conditional) cerr << " ifnone"; cerr << " from "; } NetNet*dst_sig = scope->find_signal(*cur); if (dst_sig == 0) { cerr << get_fileline() << ": error: No wire '" << *cur << "' in this module." << endl; des->errors += 1; continue; } unsigned long dst_wid = dst_sig->vector_width(); if (dst_sig->port_type() != NetNet::POUTPUT && dst_sig->port_type() != NetNet::PINOUT) { cerr << get_fileline() << ": error: Path destination " << *cur << " must be an output or inout port." << endl; des->errors += 1; } NetDelaySrc*path = new NetDelaySrc(scope, scope->local_symbol(), src.size(), condit_sig, conditional); path->set_line(*this); // The presence of the data_source_expression indicates // that this is an edge sensitive path. If so, then set // the edges. Note that edge==0 is BOTH edges. if (data_source_expression) { if (edge >= 0) path->set_posedge(); if (edge <= 0) path->set_negedge(); } switch (ndelays) { case 12: path->set_delays(delay_value[0], delay_value[1], delay_value[2], delay_value[3], delay_value[4], delay_value[5], delay_value[6], delay_value[7], delay_value[8], delay_value[9], delay_value[10], delay_value[11]); break; case 6: path->set_delays(delay_value[0], delay_value[1], delay_value[2], delay_value[3], delay_value[4], delay_value[5]); break; case 3: path->set_delays(delay_value[0], delay_value[1], delay_value[2]); break; case 2: path->set_delays(delay_value[0], delay_value[1]); break; case 1: path->set_delays(delay_value[0]); break; } unsigned idx = 0; for (str_vector_iter cur_src = src.begin() ; cur_src != src.end() ; ++ cur_src ) { NetNet*src_sig = scope->find_signal(*cur_src); if (src_sig == 0) { cerr << get_fileline() << ": error: No wire '" << *cur_src << "' in this module." << endl; des->errors += 1; continue; } if (debug_elaborate) { if (cur_src != src.begin()) cerr << " and "; cerr << src_sig->name(); } if ( (src_sig->port_type() != NetNet::PINPUT) && (src_sig->port_type() != NetNet::PINOUT) ) { cerr << get_fileline() << ": error: Path source " << *cur_src << " must be an input or inout port." << endl; des->errors += 1; } // For a parallel connection the source and destination // must be the same width. if (! full_flag_) { unsigned long src_wid = src_sig->vector_width(); if (src_wid != dst_wid) { cerr << get_fileline() << ": error: For a " "parallel connection the " "source/destination width must match " "found (" << src_wid << "/" << dst_wid << ")." << endl; des->errors += 1; } } connect(src_sig->pin(0), path->pin(idx)); idx += 1; } if (debug_elaborate) { cerr << endl; } if (condit_sig) connect(condit_sig->pin(0), path->pin(idx)); dst_sig->add_delay_path(path); } } static void elaborate_functions(Design*des, NetScope*scope, const map&funcs) { typedef map::const_iterator mfunc_it_t; for (mfunc_it_t cur = funcs.begin() ; cur != funcs.end() ; ++ cur ) { hname_t use_name ( (*cur).first ); NetScope*fscope = scope->child(use_name); assert(fscope); (*cur).second->elaborate(des, fscope); } } static void elaborate_tasks(Design*des, NetScope*scope, const map&tasks) { typedef map::const_iterator mtask_it_t; for (mtask_it_t cur = tasks.begin() ; cur != tasks.end() ; ++ cur ) { hname_t use_name ( (*cur).first ); NetScope*tscope = scope->child(use_name); assert(tscope); (*cur).second->elaborate(des, tscope); } } static void elaborate_classes(Design*des, NetScope*scope, const map&classes) { for (map::const_iterator cur = classes.begin() ; cur != classes.end() ; ++ cur) { netclass_t*use_class = scope->find_class(cur->second->pscope_name()); use_class->elaborate(des, cur->second); if (use_class->test_for_missing_initializers()) { cerr << cur->second->get_fileline() << ": error: " << "Const properties of class " << use_class->get_name() << " are missing initialization." << endl; des->errors += 1; } } } bool PPackage::elaborate(Design*des, NetScope*scope) const { bool result_flag = true; // Elaborate function methods, and... elaborate_functions(des, scope, funcs); // Elaborate task methods. elaborate_tasks(des, scope, tasks); // Elaborate class definitions. elaborate_classes(des, scope, classes); return result_flag; } /* * When a module is instantiated, it creates the scope then uses this * method to elaborate the contents of the module. */ bool Module::elaborate(Design*des, NetScope*scope) const { bool result_flag = true; // Elaborate within the generate blocks. typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur)->elaborate(des, scope); } // Elaborate functions. elaborate_functions(des, scope, funcs); // Elaborate the task definitions. This is done before the // behaviors so that task calls may reference these, and after // the signals so that the tasks can reference them. elaborate_tasks(des, scope, tasks); // Elaborate class definitions. elaborate_classes(des, scope, classes); // Get all the gates of the module and elaborate them by // connecting them to the signals. The gate may be simple or // complex. const list&gl = get_gates(); for (list::const_iterator gt = gl.begin() ; gt != gl.end() ; ++ gt ) { (*gt)->elaborate(des, scope); } // Elaborate the behaviors, making processes out of them. This // involves scanning the PProcess* list, creating a NetProcTop // for each process. result_flag &= elaborate_behaviors_(des, scope); // Elaborate the specify paths of the module. for (list::const_iterator sp = specify_paths.begin() ; sp != specify_paths.end() ; ++ sp ) { (*sp)->elaborate(des, scope); } return result_flag; } /* * Elaborating a netclass_t means elaborating the PFunction and PTask * objects that it contains. The scopes and signals have already been * elaborated in the class of the netclass_t scope, so we can get the * child scope for each definition and use that for the context of the * function. */ void netclass_t::elaborate(Design*des, PClass*pclass) { if (! pclass->type->initialize_static.empty()) { std::vector&stmt_list = pclass->type->initialize_static; NetBlock*stmt = new NetBlock(NetBlock::SEQU, 0); for (size_t idx = 0 ; idx < stmt_list.size() ; idx += 1) { NetProc*tmp = stmt_list[idx]->elaborate(des, class_scope_); if (tmp == 0) continue; stmt->append(tmp); } NetProcTop*top = new NetProcTop(class_scope_, IVL_PR_INITIAL, stmt); top->set_line(*pclass); des->add_process(top); } for (map::iterator cur = pclass->funcs.begin() ; cur != pclass->funcs.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": netclass_t::elaborate: " << "Elaborate class " << scope_path(class_scope_) << " function method " << cur->first << endl; } NetScope*scope = class_scope_->child( hname_t(cur->first) ); ivl_assert(*cur->second, scope); cur->second->elaborate(des, scope); } for (map::iterator cur = pclass->tasks.begin() ; cur != pclass->tasks.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": netclass_t::elaborate: " << "Elaborate class " << scope_path(class_scope_) << " task method " << cur->first << endl; } NetScope*scope = class_scope_->child( hname_t(cur->first) ); ivl_assert(*cur->second, scope); cur->second->elaborate(des, scope); } } bool PGenerate::elaborate(Design*des, NetScope*container) const { if (direct_nested_) return elaborate_direct_(des, container); bool flag = true; if (debug_elaborate) { cerr << get_fileline() << ": PGenerate::elaborate: " "generate " << scheme_type << " elaborating in scope " << scope_path(container) << "." << endl; cerr << get_fileline() << ": PGenerate::elaborate: " "generate scope_name=" << scope_name << ", id_number=" << id_number << endl; } // Handle the special case that this is a CASE scheme. In this // case the PGenerate itself does not have the generated // item. Look instead for the case ITEM that has a scope // generated for it. if (scheme_type == PGenerate::GS_CASE) { typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*item = *cur; if (item->direct_nested_ || !item->scope_list_.empty()) { flag &= item->elaborate(des, container); } } return flag; } typedef list::const_iterator scope_list_it_t; for (scope_list_it_t cur = scope_list_.begin() ; cur != scope_list_.end() ; ++ cur ) { NetScope*scope = *cur; // Check that this scope is one that is contained in the // container that the caller passed in. if (scope->parent() != container) continue; // If this was an unnamed generate block, replace its // temporary name with a name generated using the naming // scheme defined in the Verilog-2005 standard. const char*name = scope_name.str(); if (name[0] == '$') { if (!scope->auto_name("genblk", '0', name + 4)) { cerr << get_fileline() << ": warning: Couldn't build" << " unique name for unnamed generate block" << " - using internal name " << name << endl; } } if (debug_elaborate) cerr << get_fileline() << ": debug: Elaborate in " << "scope " << scope_path(scope) << endl; flag = elaborate_(des, scope) & flag; } return flag; } bool PGenerate::elaborate_direct_(Design*des, NetScope*container) const { bool flag = true; if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Direct nesting elaborate in scope " << scope_path(container) << ", scheme_type=" << scheme_type << endl; } // Elaborate for a direct nested generated scheme knows // that there are only sub_schemes to be elaborated. There // should be exactly 1 active generate scheme, search for it // using this loop. typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*item = *cur; if (debug_elaborate) { cerr << get_fileline() << ": PGenerate::elaborate_direct_: " << "item->scope_name=" << item->scope_name << ", item->scheme_type=" << item->scheme_type << ", item->direct_nested_=" << item->direct_nested_ << ", item->scope_list_.size()=" << item->scope_list_.size() << "." << endl; } // Special case: If this is a case generate scheme, then // the PGenerate object (item) does not actually // contain anything. Instead scan the case items, which // are listed as sub-schemes of the item. if (item->scheme_type == PGenerate::GS_CASE) { for (generate_it_t icur = item->generate_schemes.begin() ; icur != item->generate_schemes.end() ; ++ icur ) { PGenerate*case_item = *icur; if (case_item->direct_nested_ || !case_item->scope_list_.empty()) { flag &= case_item->elaborate(des, container); } } } else { if (item->direct_nested_ || !item->scope_list_.empty()) { // Found the item, and it is direct nested. flag &= item->elaborate(des, container); } } } return flag; } bool PGenerate::elaborate_(Design*des, NetScope*scope) const { elaborate_functions(des, scope, funcs); elaborate_tasks(des, scope, tasks); typedef list::const_iterator gates_it_t; for (gates_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur ) (*cur)->elaborate(des, scope); typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin(); cur != behaviors.end(); ++ cur ) (*cur)->elaborate(des, scope); typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur)->elaborate(des, scope); } return true; } bool PScope::elaborate_behaviors_(Design*des, NetScope*scope) const { bool result_flag = true; // Elaborate the behaviors, making processes out of them. This // involves scanning the PProcess* list, creating a NetProcTop // for each process. for (list::const_iterator st = behaviors.begin() ; st != behaviors.end() ; ++ st ) { result_flag &= (*st)->elaborate(des, scope); } for (list::const_iterator st = analog_behaviors.begin() ; st != analog_behaviors.end() ; ++ st ) { result_flag &= (*st)->elaborate(des, scope); } return result_flag; } class elaborate_package_t : public elaborator_work_item_t { public: elaborate_package_t(Design*d, NetScope*scope, PPackage*p) : elaborator_work_item_t(d), scope_(scope), package_(p) { } ~elaborate_package_t() { } virtual void elaborate_runrun() { if (! package_->elaborate_scope(des, scope_)) des->errors += 1; } private: NetScope*scope_; PPackage*package_; }; class elaborate_root_scope_t : public elaborator_work_item_t { public: elaborate_root_scope_t(Design*des__, NetScope*scope, Module*rmod) : elaborator_work_item_t(des__), scope_(scope), rmod_(rmod) { } ~elaborate_root_scope_t() { } virtual void elaborate_runrun() { Module::replace_t root_repl; for (list::iterator cur = Module::user_defparms.begin() ; cur != Module::user_defparms.end() ; ++ cur ) { pform_name_t tmp_name = cur->first; if (peek_head_name(tmp_name) != scope_->basename()) continue; tmp_name.pop_front(); if (tmp_name.size() != 1) continue; root_repl[peek_head_name(tmp_name)] = cur->second; } if (! rmod_->elaborate_scope(des, scope_, root_repl)) des->errors += 1; } private: NetScope*scope_; Module*rmod_; }; class top_defparams : public elaborator_work_item_t { public: top_defparams(Design*des__) : elaborator_work_item_t(des__) { } ~top_defparams() { } virtual void elaborate_runrun() { if (debug_scopes) { cerr << "debug: top_defparams::elaborate_runrun()" << endl; } // This method recurses through the scopes, looking for // defparam assignments to apply to the parameters in the // various scopes. This needs to be done after all the scopes // and basic parameters are taken care of because the defparam // can assign to a parameter declared *after* it. des->run_defparams(); // At this point, all parameter overrides are done. Scan the // scopes and evaluate the parameters all the way down to // constants. des->evaluate_parameters(); if (debug_scopes) { cerr << "debug: top_defparams::elaborate_runrun() done" << endl; } } }; class later_defparams : public elaborator_work_item_t { public: later_defparams(Design*des__) : elaborator_work_item_t(des__) { } ~later_defparams() { } virtual void elaborate_runrun() { if (debug_scopes) { cerr << "debug: later_defparams::elaborate_runrun()" << endl; } listtmp_list; for (set::iterator cur = des->defparams_later.begin() ; cur != des->defparams_later.end() ; ++ cur ) tmp_list.push_back(*cur); des->defparams_later.clear(); while (! tmp_list.empty()) { NetScope*cur = tmp_list.front(); tmp_list.pop_front(); cur->run_defparams_later(des); } // The overridden parameters will be evaluated later in // a top_defparams work item. if (debug_scopes) { cerr << "debuf: later_defparams::elaborate_runrun() done" << endl; } } }; bool Design::check_proc_delay() const { bool result_flag = true; for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { /* If this is an always block and we have no or zero delay then * a runtime infinite loop will happen. If we possible have some * delay then print a warning that an infinite loop is possible. */ if (pr->type() == IVL_PR_ALWAYS) { DelayType dly_type = pr->statement()->delay_type(); if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) { cerr << pr->get_fileline() << ": error: always" << " statement does not have any delay." << endl; cerr << pr->get_fileline() << ": : A runtime" << " infinite loop will occur." << endl; result_flag = false; } else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) { cerr << pr->get_fileline() << ": warning: always" << " statement may not have any delay." << endl; cerr << pr->get_fileline() << ": : A runtime" << " infinite loop may be possible." << endl; } } /* If this is a final block it must not have a delay, but this should have been caught by the statement elaboration, so maybe this should be an internal error? */ if (pr->type() == IVL_PR_FINAL) { DelayType dly_type = pr->statement()->delay_type(); if (dly_type != NO_DELAY && dly_type != ZERO_DELAY) { cerr << pr->get_fileline() << ": error: final" << " statement contains a delay." << endl; result_flag = false; } } } return result_flag; } void Design::root_elaborate(void) { for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++ cur) { netclass_t*cur_class = cur->second; PClass*cur_pclass = class_to_pclass_[cur_class]; cur_class->elaborate(this, cur_pclass); } for (map::iterator cur = root_tasks_.begin() ; cur != root_tasks_.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": Design::root_elaborate: " << "Elaborate for root task/func " << scope_path(cur->first) << endl; } cur->second->elaborate(this, cur->first); } } /* * This function is the root of all elaboration. The input is the list * of root module names. The function locates the Module definitions * for each root, does the whole elaboration sequence, and fills in * the resulting Design. */ struct pack_elem { PPackage*pack; NetScope*scope; }; struct root_elem { Module *mod; NetScope *scope; }; Design* elaborate(listroots) { vector root_elems(roots.size()); vector pack_elems(pform_packages.size()); bool rc = true; unsigned i = 0; // This is the output design. I fill it in as I scan the root // module and elaborate what I find. Design*des = new Design; // Elaborate enum sets in $root scope. elaborate_rootscope_enumerations(des); // Elaborate tasks and functions in $root scope. elaborate_rootscope_tasks(des); // Elaborate classes in $root scope. elaborate_rootscope_classes(des); // Elaborate the packages. Package elaboration is simpler // because there are fewer sub-scopes involved. i = 0; for (map::iterator pac = pform_packages.begin() ; pac != pform_packages.end() ; ++ pac) { ivl_assert(*pac->second, pac->first == pac->second->pscope_name()); NetScope*scope = des->make_package_scope(pac->first); scope->set_line(pac->second); elaborator_work_item_t*es = new elaborate_package_t(des, scope, pac->second); des->elaboration_work_list.push_back(es); pack_elems[i].pack = pac->second; pack_elems[i].scope = scope; i += 1; } // Scan the root modules by name, and elaborate their scopes. i = 0; for (list::const_iterator root = roots.begin() ; root != roots.end() ; ++ root ) { // Look for the root module in the list. map::const_iterator mod = pform_modules.find(*root); if (mod == pform_modules.end()) { cerr << "error: Unable to find the root module \"" << (*root) << "\" in the Verilog source." << endl; cerr << " : Perhaps ``-s " << (*root) << "'' is incorrect?" << endl; des->errors++; continue; } // Get the module definition for this root instance. Module *rmod = (*mod).second; // Make the root scope. This makes a NetScope object and // pushes it into the list of root scopes in the Design. NetScope*scope = des->make_root_scope(*root, rmod->program_block, rmod->is_interface); // Collect some basic properties of this scope from the // Module definition. scope->set_line(rmod); scope->time_unit(rmod->time_unit); scope->time_precision(rmod->time_precision); scope->time_from_timescale(rmod->time_from_timescale); des->set_precision(rmod->time_precision); // Save this scope, along with its definition, in the // "root_elems" list for later passes. root_elems[i].mod = rmod; root_elems[i].scope = scope; i += 1; // Arrange for these scopes to be elaborated as root // scopes. Create an "elaborate_root_scope" object to // contain the work item, and append it to the scope // elaborations work list. elaborator_work_item_t*es = new elaborate_root_scope_t(des, scope, rmod); des->elaboration_work_list.push_back(es); } // Run the work list of scope elaborations until the list is // empty. This list is initially populated above where the // initial root scopes are primed. while (! des->elaboration_work_list.empty()) { // Push a work item to process the defparams of any scopes // that are elaborated during this pass. For the first pass // this will be all the root scopes. For subsequent passes // it will be any scopes created during the previous pass // by a generate construct or instance array. des->elaboration_work_list.push_back(new top_defparams(des)); // Transfer the queue to a temporary queue. list cur_queue; while (! des->elaboration_work_list.empty()) { cur_queue.push_back(des->elaboration_work_list.front()); des->elaboration_work_list.pop_front(); } // Run from the temporary queue. If the temporary queue // items create new work queue items, they will show up // in the elaboration_work_list and then we get to run // through them in the next pass. while (! cur_queue.empty()) { elaborator_work_item_t*tmp = cur_queue.front(); cur_queue.pop_front(); tmp->elaborate_runrun(); delete tmp; } if (! des->elaboration_work_list.empty()) { des->elaboration_work_list.push_back(new later_defparams(des)); } } if (debug_elaborate) { cerr << ": elaborate: " << "elaboration work list done. Start processing residual defparams." << endl; } // Look for residual defparams (that point to a non-existent // scope) and clean them out. des->residual_defparams(); // Errors already? Probably missing root modules. Just give up // now and return nothing. if (des->errors > 0) return des; if (debug_elaborate) { cerr << ": elaborate: " << "Start calling Package elaborate_sig methods." << endl; } // With the parameters evaluated down to constants, we have // what we need to elaborate signals and memories. This pass // creates all the NetNet and NetMemory objects for declared // objects. for (i = 0; i < pack_elems.size(); i += 1) { PPackage*pack = pack_elems[i].pack; NetScope*scope= pack_elems[i].scope; if (! pack->elaborate_sig(des, scope)) { if (debug_elaborate) { cerr << "" << ": debug: " << pack->pscope_name() << ": elaborate_sig failed!!!" << endl; } delete des; return 0; } } if (debug_elaborate) { cerr << ": elaborate: " << "Start calling $root elaborate_sig methods." << endl; } des->root_elaborate_sig(); if (debug_elaborate) { cerr << ": elaborate: " << "Start calling root module elaborate_sig methods." << endl; } for (i = 0; i < root_elems.size(); i++) { Module *rmod = root_elems[i].mod; NetScope *scope = root_elems[i].scope; scope->set_num_ports( rmod->port_count() ); if (debug_elaborate) { cerr << "" << ": debug: " << rmod->mod_name() << ": port elaboration root " << rmod->port_count() << " ports" << endl; } if (! rmod->elaborate_sig(des, scope)) { if (debug_elaborate) { cerr << "" << ": debug: " << rmod->mod_name() << ": elaborate_sig failed!!!" << endl; } delete des; return 0; } // Some of the generators need to have the ports correctly // defined for the root modules. This code does that. for (unsigned idx = 0; idx < rmod->port_count(); idx += 1) { vector mport = rmod->get_port(idx); unsigned int prt_vector_width = 0; PortType::Enum ptype = PortType::PIMPLICIT; for (unsigned pin = 0; pin < mport.size(); pin += 1) { // This really does more than we need and adds extra // stuff to the design that should be cleaned later. NetNet *netnet = mport[pin]->elaborate_subport(des, scope); if (netnet != 0) { // Elaboration may actually fail with // erroneous input source ivl_assert(*mport[pin], netnet->pin_count()==1); prt_vector_width += netnet->vector_width(); ptype = PortType::merged(netnet->port_type(), ptype); } } if (debug_elaborate) { cerr << "" << ": debug: " << rmod->mod_name() << ": adding module port " << rmod->get_port_name(idx) << endl; } scope->add_module_port_info(idx, rmod->get_port_name(idx), ptype, prt_vector_width ); } } // Now that the structure and parameters are taken care of, // run through the pform again and generate the full netlist. for (i = 0; i < pack_elems.size(); i += 1) { PPackage*pkg = pack_elems[i].pack; NetScope*scope = pack_elems[i].scope; rc &= pkg->elaborate(des, scope); } des->root_elaborate(); for (i = 0; i < root_elems.size(); i++) { Module *rmod = root_elems[i].mod; NetScope *scope = root_elems[i].scope; rc &= rmod->elaborate(des, scope); } if (rc == false) { delete des; return 0; } // Now that everything is fully elaborated verify that we do // not have an always block with no delay (an infinite loop), // or a final block with a delay. if (des->check_proc_delay() == false) { delete des; des = 0; } if (debug_elaborate) { cerr << "" << ": debug: " << " finishing with " << des->find_root_scopes().size() << " root scopes " << endl; } return des; } iverilog-10_1/elaborate_analog.cc000066400000000000000000000044241265551621300171340ustar00rootroot00000000000000/* * Copyright (c) 2008-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "AStatement.h" # include "netlist.h" # include "netmisc.h" # include "util.h" # include NetProc* AContrib::elaborate(Design*des, NetScope*scope) const { NetExpr*lval = elab_and_eval(des, scope, lval_, -1); NetExpr*rval = elab_and_eval(des, scope, rval_, -1); NetEAccess*lacc = dynamic_cast (lval); if (lacc == 0) { cerr << get_fileline() << ": error: The l-value of a contribution" << " statement must be a branch probe access function." << endl; des->errors += 1; return 0; } NetContribution*st = new NetContribution(lacc, rval); st->set_line(*this); return st; } bool AProcess::elaborate(Design*des, NetScope*scope) const { NetProc*estatement = statement_->elaborate(des, scope); if (estatement == 0) return false; NetAnalogTop*top = new NetAnalogTop(scope, type_, estatement); // Evaluate the attributes for this process, if there // are any. These attributes are to be attached to the // NetProcTop object. struct attrib_list_t*attrib_list; unsigned attrib_list_n = 0; attrib_list = evaluate_attributes(attributes, attrib_list_n, des, scope); for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1) top->attribute(attrib_list[adx].key, attrib_list[adx].val); delete[]attrib_list; top->set_line(*this); des->add_process(top); return true; } iverilog-10_1/emit.cc000066400000000000000000000362611265551621300146170ustar00rootroot00000000000000/* * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include /* * The emit function is called to generate the output required of the * target. */ # include "target.h" # include "netclass.h" # include "netlist.h" # include "compiler.h" # include # include # include bool NetNode::emit_node(struct target_t*) const { cerr << "EMIT: Gate type? " << typeid(*this).name() << endl; return false; } bool NetLogic::emit_node(struct target_t*tgt) const { tgt->logic(this); return true; } bool NetUDP::emit_node(struct target_t*tgt) const { tgt->udp(this); return true; } bool NetAbs::emit_node(struct target_t*tgt) const { tgt->lpm_abs(this); return true; } bool NetAddSub::emit_node(struct target_t*tgt) const { tgt->lpm_add_sub(this); return true; } bool NetArrayDq::emit_node(struct target_t*tgt) const { return tgt->lpm_array_dq(this); } bool NetCaseCmp::emit_node(struct target_t*tgt) const { tgt->net_case_cmp(this); return true; } bool NetCastInt2::emit_node(struct target_t*tgt) const { return tgt->lpm_cast_int2(this); } bool NetCastInt4::emit_node(struct target_t*tgt) const { return tgt->lpm_cast_int4(this); } bool NetCastReal::emit_node(struct target_t*tgt) const { return tgt->lpm_cast_real(this); } bool NetCLShift::emit_node(struct target_t*tgt) const { tgt->lpm_clshift(this); return true; } bool NetCompare::emit_node(struct target_t*tgt) const { tgt->lpm_compare(this); return true; } bool NetConcat::emit_node(struct target_t*tgt) const { return tgt->concat(this); } bool NetConst::emit_node(struct target_t*tgt) const { return tgt->net_const(this); } bool NetDivide::emit_node(struct target_t*tgt) const { tgt->lpm_divide(this); return true; } bool NetFF::emit_node(struct target_t*tgt) const { tgt->lpm_ff(this); return true; } bool NetLiteral::emit_node(struct target_t*tgt) const { return tgt->net_literal(this); } bool NetModulo::emit_node(struct target_t*tgt) const { tgt->lpm_modulo(this); return true; } bool NetMult::emit_node(struct target_t*tgt) const { tgt->lpm_mult(this); return true; } bool NetMux::emit_node(struct target_t*tgt) const { tgt->lpm_mux(this); return true; } bool NetPartSelect::emit_node(struct target_t*tgt) const { return tgt->part_select(this); } bool NetPow::emit_node(struct target_t*tgt) const { tgt->lpm_pow(this); return true; } bool NetReplicate::emit_node(struct target_t*tgt) const { return tgt->replicate(this); } bool NetSignExtend::emit_node(struct target_t*tgt) const { return tgt->sign_extend(this); } bool NetSubstitute::emit_node(struct target_t*tgt) const { return tgt->substitute(this); } bool NetUReduce::emit_node(struct target_t*tgt) const { return tgt->ureduce(this); } bool NetSysFunc::emit_node(struct target_t*tgt) const { return tgt->net_sysfunction(this); } bool NetUserFunc::emit_node(struct target_t*tgt) const { return tgt->net_function(this); } bool NetTran::emit_node(struct target_t*tgt) const { return tgt->tran(this); } bool NetBUFZ::emit_node(struct target_t*tgt) const { return tgt->bufz(this); } bool NetProcTop::emit(struct target_t*tgt) const { return tgt->process(this); } bool NetAnalogTop::emit(struct target_t*tgt) const { return tgt->process(this); } bool NetProc::emit_proc(struct target_t*) const { cerr << "EMIT: Proc type? " << typeid(*this).name() << endl; return false; } bool NetAlloc::emit_proc(struct target_t*tgt) const { tgt->proc_alloc(this); return true; } bool NetAssign::emit_proc(struct target_t*tgt) const { return tgt->proc_assign(this); } bool NetAssignNB::emit_proc(struct target_t*tgt) const { tgt->proc_assign_nb(this); return true; } bool NetBlock::emit_proc(struct target_t*tgt) const { return tgt->proc_block(this); } bool NetCase::emit_proc(struct target_t*tgt) const { tgt->proc_case(this); return true; } bool NetCAssign::emit_proc(struct target_t*tgt) const { return tgt->proc_cassign(this); } bool NetCondit::emit_proc(struct target_t*tgt) const { return tgt->proc_condit(this); } bool NetContribution::emit_proc(struct target_t*tgt) const { return tgt->proc_contribution(this); } bool NetDeassign::emit_proc(struct target_t*tgt) const { return tgt->proc_deassign(this); } bool NetDisable::emit_proc(struct target_t*tgt) const { return tgt->proc_disable(this); } bool NetDoWhile::emit_proc(struct target_t*tgt) const { tgt->proc_do_while(this); return true; } void NetDoWhile::emit_proc_recurse(struct target_t*tgt) const { proc_->emit_proc(tgt); } bool NetForce::emit_proc(struct target_t*tgt) const { return tgt->proc_force(this); } bool NetForever::emit_proc(struct target_t*tgt) const { tgt->proc_forever(this); return true; } bool NetForLoop::emit_proc(struct target_t*tgt) const { return tgt->proc_block(as_block_); } bool NetFree::emit_proc(struct target_t*tgt) const { tgt->proc_free(this); return true; } bool NetPDelay::emit_proc(struct target_t*tgt) const { return tgt->proc_delay(this); } bool NetPDelay::emit_proc_recurse(struct target_t*tgt) const { if (statement_) return statement_->emit_proc(tgt); return true; } bool NetRelease::emit_proc(struct target_t*tgt) const { return tgt->proc_release(this); } bool NetRepeat::emit_proc(struct target_t*tgt) const { tgt->proc_repeat(this); return true; } bool NetSTask::emit_proc(struct target_t*tgt) const { tgt->proc_stask(this); return true; } bool NetUTask::emit_proc(struct target_t*tgt) const { tgt->proc_utask(this); return true; } bool NetWhile::emit_proc(struct target_t*tgt) const { tgt->proc_while(this); return true; } void NetWhile::emit_proc_recurse(struct target_t*tgt) const { proc_->emit_proc(tgt); } void NetBlock::emit_recurse(struct target_t*tgt) const { if (last_ == 0) return; NetProc*cur = last_; do { cur = cur->next_; cur->emit_proc(tgt); } while (cur != last_); } bool NetCondit::emit_recurse_if(struct target_t*tgt) const { if (if_) return if_->emit_proc(tgt); else return true; } bool NetCondit::emit_recurse_else(struct target_t*tgt) const { if (else_) return else_->emit_proc(tgt); else return true; } bool NetEvProbe::emit_node(struct target_t*tgt) const { tgt->net_probe(this); return true; } bool NetEvTrig::emit_proc(struct target_t*tgt) const { return tgt->proc_trigger(this); } bool NetEvWait::emit_proc(struct target_t*tgt) const { return tgt->proc_wait(this); } bool NetEvWait::emit_recurse(struct target_t*tgt) const { if (!statement_) return true; return statement_->emit_proc(tgt); } void NetForever::emit_recurse(struct target_t*tgt) const { if (statement_) statement_->emit_proc(tgt); } void NetRepeat::emit_recurse(struct target_t*tgt) const { if (statement_) statement_->emit_proc(tgt); } void netclass_t::emit_scope(struct target_t*tgt) const { class_scope_->emit_scope(tgt); } void NetScope::emit_scope(struct target_t*tgt) const { if (debug_emit) { cerr << "NetScope::emit_scope: " << "Emit scope " << scope_path(this) << endl; } tgt->scope(this); for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) tgt->event(cur); for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++cur) { cur->second->emit_scope(tgt); tgt->class_type(this, cur->second); } for (map::const_iterator cur = enum_sets_.begin() ; cur != enum_sets_.end() ; ++cur) tgt->enumeration(this, cur->second); for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) cur->second->emit_scope(tgt); for (signals_map_iter_t cur = signals_map_.begin() ; cur != signals_map_.end() ; ++ cur ) { tgt->signal(cur->second); } // Run the signals again, but this time to connect the // delay paths. This is done as a second pass because // the paths reference other signals that may be later // in the list. We can do it here because delay paths are // always connected within the scope. for (signals_map_iter_t cur = signals_map_.begin() ; cur != signals_map_.end() ; ++ cur) { tgt->signal_paths(cur->second); } if (type_ == MODULE) tgt->convert_module_ports(this); } bool NetScope::emit_defs(struct target_t*tgt) const { bool flag = true; if (debug_emit) { cerr << "NetScope::emit_defs: " << "Emit definitions for " << scope_path(this) << endl; } switch (type_) { case PACKAGE: case MODULE: for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) flag &= cur->second->emit_defs(tgt); for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++ cur) flag &= cur->second->emit_defs(tgt); break; case FUNC: flag &= tgt->func_def(this); break; case TASK: tgt->task_def(this); break; default: /* BEGIN_END and FORK_JOIN, GENERATE... */ for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) flag &= cur->second->emit_defs(tgt); break; } return flag; } bool netclass_t::emit_defs(struct target_t*tgt) const { return class_scope_->emit_defs(tgt); } int Design::emit(struct target_t*tgt) const { int rc = 0; if (tgt->start_design(this) == false) return -2; for (map::const_iterator scope = root_tasks_.begin() ; scope != root_tasks_.end() ; ++ scope) { scope->first->emit_scope(tgt); scope->first->emit_defs(tgt); } // enumerate package scopes for (map::const_iterator scope = packages_.begin() ; scope != packages_.end() ; ++ scope) { scope->second->emit_scope(tgt); } for (map::const_iterator cur = classes_.begin() ; cur != classes_.end() ; ++cur) { const NetScope*use_scope = cur->second->class_scope(); cur->second->emit_scope(tgt); tgt->class_type(use_scope, cur->second); cur->second->emit_defs(tgt); } // enumerate root scopes for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end(); ++ scope ) { (*scope)->emit_scope(tgt); } // emit nodes bool nodes_rc = true; if (nodes_) { NetNode*cur = nodes_->node_next_; do { nodes_rc = nodes_rc && cur->emit_node(tgt); cur = cur->node_next_; } while (cur != nodes_->node_next_); } bool branches_rc = true; for (NetBranch*cur = branches_ ; cur ; cur = cur->next_) { branches_rc = tgt->branch(cur) && branches_rc; } // emit task and function definitions bool tasks_rc = true; for (map::const_iterator scope = packages_.begin() ; scope != packages_.end() ; ++ scope ) tasks_rc &= scope->second->emit_defs(tgt); for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end(); ++ scope ) tasks_rc &= (*scope)->emit_defs(tgt); // emit the processes bool proc_rc = true; for (const NetProcTop*idx = procs_ ; idx ; idx = idx->next_) proc_rc &= idx->emit(tgt); for (const NetAnalogTop*idx = aprocs_ ; idx ; idx = idx->next_) proc_rc &= idx->emit(tgt); if (nodes_rc == false) tgt->errors += 1; if (tasks_rc == false) tgt->errors += 1; if (proc_rc == false) tgt->errors += 1; if (branches_rc == false) tgt->errors += 1; rc = tgt->end_design(this); if (nodes_rc == false) return -1; if (tasks_rc == false) return -2; if (proc_rc == false) return -3; if (branches_rc == false) return -4; return rc; } void NetEAccess::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_access_func(this); } void NetEArrayPattern::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_array_pattern(this); } void NetEBinary::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_binary(this); } void NetEConcat::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_concat(this); } void NetEConst::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_const(this); } void NetEConstEnum::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_const(this); } void NetEConstParam::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_param(this); } void NetECReal::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_creal(this); } void NetECRealParam::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_rparam(this); } void NetEEvent::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_event(this); } void NetELast::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_last(this); } void NetENetenum::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_netenum(this); } void NetENew::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_new(this); } void NetENull::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_null(this); } void NetEProperty::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_property(this); } void NetEScope::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_scope(this); } void NetESelect::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_select(this); } void NetESFunc::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_sfunc(this); } void NetEShallowCopy::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_scopy(this); } void NetEShallowCopy::expr_scan_oper1(struct expr_scan_t*tgt) const { arg1_->expr_scan(tgt); } void NetEShallowCopy::expr_scan_oper2(struct expr_scan_t*tgt) const { arg2_->expr_scan(tgt); } void NetEUFunc::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_ufunc(this); } void NetESignal::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_signal(this); } void NetETernary::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_ternary(this); } void NetEUnary::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_unary(this); } iverilog-10_1/eval.cc000066400000000000000000000152601265551621300146040ustar00rootroot00000000000000/* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include "PExpr.h" # include "netlist.h" # include "netmisc.h" # include "compiler.h" verinum* PExpr::eval_const(Design*, NetScope*) const { return 0; } verinum* PEBinary::eval_const(Design*des, NetScope*scope) const { verinum*l = left_->eval_const(des, scope); if (l == 0) return 0; verinum*r = right_->eval_const(des, scope); if (r == 0) { delete l; return 0; } verinum*res; switch (op_) { case 'p': { if (l->is_defined() && r->is_defined()) { res = new verinum(pow(*l, *r)); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '+': { if (l->is_defined() && r->is_defined()) { res = new verinum(*l + *r); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '-': { if (l->is_defined() && r->is_defined()) { res = new verinum(*l - *r); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '*': { if (l->is_defined() && r->is_defined()) { res = new verinum(*l * *r); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '/': { if (l->is_defined() && r->is_defined()) { long lv = l->as_long(); long rv = r->as_long(); res = new verinum(lv / rv, l->len()); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '%': { if (l->is_defined() && r->is_defined()) { long lv = l->as_long(); long rv = r->as_long(); res = new verinum(lv % rv, l->len()); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '>': { if (l->is_defined() && r->is_defined()) { long lv = l->as_long(); long rv = r->as_long(); res = new verinum(lv > rv, l->len()); } else { res = new verinum(verinum::Vx, l->len()); } break; } case '<': { if (l->is_defined() && r->is_defined()) { long lv = l->as_long(); long rv = r->as_long(); res = new verinum(lv < rv, l->len()); } else { res = new verinum(verinum::Vx, l->len()); } break; } case 'l': { // left shift (<<) assert(r->is_defined()); unsigned long rv = r->as_ulong(); res = new verinum(verinum::V0, l->len()); if (rv < res->len()) { unsigned cnt = res->len() - rv; for (unsigned idx = 0 ; idx < cnt ; idx += 1) res->set(idx+rv, l->get(idx)); } break; } case 'r': { // right shift (>>) assert(r->is_defined()); unsigned long rv = r->as_ulong(); res = new verinum(verinum::V0, l->len()); if (rv < res->len()) { unsigned cnt = res->len() - rv; for (unsigned idx = 0 ; idx < cnt ; idx += 1) res->set(idx, l->get(idx+rv)); } break; } default: delete l; delete r; return 0; } delete l; delete r; return res; } verinum* PEConcat::eval_const(Design*des, NetScope*scope) const { verinum*accum = parms_[0]->eval_const(des, scope); if (accum == 0) return 0; for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { verinum*tmp = parms_[idx]->eval_const(des, scope); if (tmp == 0) { delete accum; return 0; } assert(tmp); *accum = concat(*accum, *tmp); delete tmp; } return accum; } /* * Evaluate an identifier as a constant expression. This is only * possible if the identifier is that of a parameter. */ verinum* PEIdent::eval_const(Design*des, NetScope*scope) const { assert(scope); NetNet*net; NetEvent*eve; const NetExpr*expr; const name_component_t&name_tail = path_.back(); // Handle the special case that this ident is a genvar // variable name. In that case, the genvar meaning preempts // everything and we just return that value immediately. if (scope->genvar_tmp && strcmp(name_tail.name,scope->genvar_tmp) == 0) { return new verinum(scope->genvar_tmp_val); } symbol_search(this, des, scope, path_, net, expr, eve); if (expr == 0) return 0; const NetEConst*eval = dynamic_cast(expr); if (eval == 0) { cerr << get_fileline() << ": internal error: Unable to evaluate " << "constant expression (parameter=" << path_ << "): " << *expr << endl; return 0; } assert(eval); if (!name_tail.index.empty()) return 0; return new verinum(eval->value()); } verinum* PEFNumber::eval_const(Design*, NetScope*) const { long val = value_->as_long(); return new verinum(val); } verinum* PENumber::eval_const(Design*, NetScope*) const { return new verinum(value()); } verinum* PEString::eval_const(Design*, NetScope*) const { return new verinum(string(text_)); } verinum* PETernary::eval_const(Design*des, NetScope*scope) const { verinum*test = expr_->eval_const(des, scope); if (test == 0) return 0; verinum::V bit = test->get(0); delete test; switch (bit) { case verinum::V0: return fal_->eval_const(des, scope); case verinum::V1: return tru_->eval_const(des, scope); default: return 0; // XXXX It is possible to handle this case if both fal_ // and tru_ are constant. Someday... } } verinum* PEUnary::eval_const(Design*des, NetScope*scope) const { verinum*val = expr_->eval_const(des, scope); if (val == 0) return 0; switch (op_) { case '+': return val; case '-': { /* We need to expand the value a bit if we are taking the 2's complement so that we are guaranteed to not overflow. */ verinum tmp ((uint64_t)0, val->len()+1); for (unsigned idx = 0 ; idx < val->len() ; idx += 1) tmp.set(idx, val->get(idx)); *val = -tmp; val->has_sign(true); return val; } } delete val; return 0; } iverilog-10_1/eval_attrib.cc000066400000000000000000000043041265551621300161460ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "util.h" # include "PExpr.h" # include "netlist.h" # include # include /* * The evaluate_attributes function evaluates the attribute * expressions from the map, and returns a table in a form suitable * for passing to netlist devices. */ attrib_list_t* evaluate_attributes(const map&att, unsigned&natt, Design*des, NetScope*scope) { natt = att.size(); if (natt == 0) return 0; attrib_list_t*table = new attrib_list_t [natt]; unsigned idx = 0; typedef map::const_iterator iter_t; for (iter_t cur = att.begin() ; cur != att.end() ; ++ cur , idx += 1) { table[idx].key = (*cur).first; PExpr*exp = (*cur).second; /* If the attribute value is given in the source, then evaluate it as a constant. If the value is not given, then assume the value is 1. */ verinum*tmp = 0; if (exp) { tmp = exp->eval_const(des, scope); if (tmp == 0) { cerr << exp->get_fileline() << ": error: ``" << *exp << "'' is not a constant expression." << endl; des->errors += 1; } } if (tmp == 0) tmp = new verinum(1); assert(tmp); table[idx].val = *tmp; delete tmp; } assert(idx == natt); return table; } iverilog-10_1/eval_tree.cc000066400000000000000000001715721265551621300156340ustar00rootroot00000000000000/* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include # include # include # include # include "netlist.h" # include "ivl_assert.h" # include "netmisc.h" NetExpr* NetExpr::eval_tree() { return 0; } static void eval_debug(const NetExpr*expr, NetExpr*res, bool is_real) { if (res != 0) { res->set_line(*expr); if (debug_eval_tree) { cerr << expr->get_fileline() << ": debug: Evaluated"; if (is_real) cerr << " (real)"; cerr << ": " << *expr << " --> " << *res << endl; } } } static bool get_real_arg_(const NetExpr*expr, verireal&val) { switch (expr->expr_type()) { case IVL_VT_REAL: { const NetECReal*c = dynamic_cast (expr); if (c == 0) return false; val = c->value(); break; } case IVL_VT_BOOL: case IVL_VT_LOGIC: { const NetEConst*c = dynamic_cast(expr); if (c == 0) return false; verinum tmp = c->value(); val = verireal(tmp.as_double()); break; } case IVL_VT_DARRAY: return false; default: assert(0); } return true; } static bool get_real_arguments(const NetExpr*le, const NetExpr*re, double&lval, double&rval) { verireal val; if (!get_real_arg_(le, val)) return false; lval = val.as_double(); if (!get_real_arg_(re, val)) return false; rval = val.as_double(); return true; } NetExpr* NetEBinary::eval_tree() { eval_expr(left_); eval_expr(right_); return eval_arguments_(left_, right_); } NetExpr* NetEBinary::eval_arguments_(const NetExpr*, const NetExpr*) const { // this method should be overridden in all sub-classes ivl_assert(*this, 0); return 0; } NetECReal* NetEBAdd::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { double lval; double rval; bool flag = get_real_arguments(l, r, lval, rval); if (!flag) return 0; double res_val; switch (op()) { case '+': res_val = lval + rval; break; case '-': res_val = lval - rval; break; default: ivl_assert(*this, 0); } NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); eval_debug(this, res, true); return res; } NetExpr* NetEBAdd::eval_tree() { eval_expr(left_); eval_expr(right_); // First try to elaborate the expression completely. NetExpr*res = eval_arguments_(left_,right_); if (res != 0) return res; // If the expression type is real, then do not attempt the // following alternative processing. if (expr_type() == IVL_VT_REAL) return 0; // The expression has not evaluated to a constant. Let's still // try to optimize by trying to combine a right constant value // with the right constant value of a sub-expression add. For // example, the expression (a + 2) - 1 can be rewritten as a + 1. NetEBAdd*se = dynamic_cast(left_); NetEConst*lc = se? dynamic_cast(se->right_) : 0; NetEConst*rc = dynamic_cast(right_); if (lc != 0 && rc != 0) { ivl_assert(*this, se != 0); if (debug_eval_tree) { cerr << get_fileline() << ": debug: " << "Partially evaluate " << *this << " using (a+2)-1 --> (a+1) transform." << endl; } verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); ivl_assert(*this, rval.len() == wid); verinum val; if (op_ == se->op_) { /* (a + lval) + rval --> a + (rval+lval) */ /* (a - lval) - rval --> a - (rval+lval) */ val = cast_to_width(rval + lval, wid); } else { /* (a - lval) + rval --> a + (rval-lval) */ /* (a + lval) - rval --> a - (rval-lval) */ val = cast_to_width(rval - lval, wid); } NetEConst*tmp = new NetEConst(val); left_ = se->left_->dup_expr(); delete se; tmp->set_line(*right_); delete right_; right_ = tmp; } // We may have changed the subexpression, but the result is // still not constant, so return nil here anyhow. return 0; } NetExpr* NetEBAdd::eval_arguments_(const NetExpr*l, const NetExpr*r) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); /* If both operands are constant, then replace the entire expression with a constant value. */ if (lc != 0 && rc != 0) { verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); ivl_assert(*this, rval.len() == wid); verinum val; switch (op_) { case '+': val = cast_to_width(lval + rval, wid); break; case '-': val = cast_to_width(lval - rval, wid); break; default: return 0; } NetEConst *res = new NetEConst(val); ivl_assert(*this, res); eval_debug(this, res, false); return res; } /* Nothing more to be done, the value is not constant. */ return 0; } NetEConst* NetEBBits::eval_arguments_(const NetExpr*l, const NetExpr*r) const { const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; /* Notice the special case where one of the operands is 0 and this is a bitwise &. If this happens, then the result is known to be 0. */ if ((op() == '&') && (lc->value() == verinum(0))) { verinum res (verinum::V0, expr_width()); res.has_sign(has_sign()); NetEConst*tmp = new NetEConst(res); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } if ((op() == '&') && (rc->value() == verinum(0))) { verinum res (verinum::V0, expr_width()); res.has_sign(has_sign()); NetEConst*tmp = new NetEConst(res); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); ivl_assert(*this, rval.len() == wid); verinum res (verinum::V0, wid); switch (op()) { case '|': { for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) | rval.get(idx)); break; } case '&': { for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) & rval.get(idx)); break; } case 'X': { for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, ~(lval.get(idx) ^ rval.get(idx))); break; } case '^': { for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) ^ rval.get(idx)); break; } default: return 0; } res.has_sign(has_sign()); NetEConst*tmp = new NetEConst(res); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } NetEConst* NetEBComp::eval_less_(const NetExpr*le, const NetExpr*re) const { if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) return eval_leeq_real_(le, re, false); const NetEConst*rc = dynamic_cast(re); if (rc == 0) return 0; verinum rv = rc->value(); if (! rv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (NetEConst*tmp = must_be_leeq_(le, rv, false)) { return tmp; } /* Now go on to the normal test of the values. */ const NetEConst*lc = dynamic_cast(le); if (lc == 0) return 0; verinum lv = lc->value(); if (! lv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (lv < rv) { NetEConst*res = new NetEConst(verinum(verinum::V1, 1)); ivl_assert(*this, res); return res; } else { NetEConst*res = new NetEConst(verinum(verinum::V0, 1)); ivl_assert(*this, res); return res; } } NetEConst* NetEBComp::must_be_leeq_(const NetExpr*le, const verinum&rv, bool eq_flag) const { // The following optimization is not valid if le can contain 'x' // or 'z' values. if (le->expr_type() == IVL_VT_LOGIC) return 0; assert(le->expr_width() > 0); verinum lv (verinum::V1, le->expr_width()); if (le->has_sign() && rv.has_sign()) { // If the expression is signed, then the largest // possible value for the left_ needs to have a 0 in the // sign position. lv.set(lv.len()-1, verinum::V0); lv.has_sign(true); } if (lv < rv || (eq_flag && (lv == rv))) { NetEConst*res = new NetEConst(verinum(verinum::V1, 1)); ivl_assert(*this, res); return res; } return 0; } NetEConst* NetEBComp::eval_leeq_real_(const NetExpr*le, const NetExpr*re, bool eq_flag) const { double lval; double rval; bool flag = get_real_arguments(le, re, lval, rval); if (! flag) return 0; bool tmp = false; if (lval < rval) tmp = true; if (tmp == false && eq_flag && lval == rval) tmp = true; verinum result(tmp ? verinum::V1 : verinum::V0, 1); NetEConst*res = new NetEConst(result); ivl_assert(*this, res); return res; } NetEConst* NetEBComp::eval_leeq_(const NetExpr*le, const NetExpr*re) const { if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) return eval_leeq_real_(le, re, true); // assert(expr_type() == IVL_VT_LOGIC); const NetEConst*r = dynamic_cast(re); if (r == 0) return 0; verinum rv = r->value(); if (! rv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (le->expr_width() == 0) { cerr << get_fileline() << ": internal error: Something wrong " << "with the left side width of <= ?" << endl; cerr << get_fileline() << ": : " << *this << endl; } if (NetEConst*tmp = must_be_leeq_(le, rv, true)) { return tmp; } /* Now go on to the normal test of the values. */ const NetEConst*l = dynamic_cast(le); if (l == 0) return 0; verinum lv = l->value(); if (! lv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (lv <= rv) { NetEConst*res = new NetEConst(verinum(verinum::V1, 1)); ivl_assert(*this, res); return res; } else { NetEConst*res = new NetEConst(verinum(verinum::V0, 1)); ivl_assert(*this, res); return res; } } NetEConst* NetEBComp::eval_gt_(const NetExpr*le, const NetExpr*re) const { if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) return eval_leeq_real_(re, le, false); const NetEConst*l = dynamic_cast(le); if (l == 0) return 0; verinum lv = l->value(); if (! lv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (NetEConst*tmp = must_be_leeq_(re, lv, false)) { return tmp; } /* Now go on to the normal test of the values. */ const NetEConst*r = dynamic_cast(re); if (r == 0) return 0; verinum rv = r->value(); if (! rv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (lv > rv) { NetEConst*res = new NetEConst(verinum(verinum::V1, 1)); ivl_assert(*this, res); return res; } else { NetEConst*res = new NetEConst(verinum(verinum::V0, 1)); ivl_assert(*this, res); return res; } } NetEConst* NetEBComp::eval_gteq_(const NetExpr*le, const NetExpr*re) const { if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) return eval_leeq_real_(re, le, true); const NetEConst*l = dynamic_cast(le); if (l == 0) return 0; verinum lv = l->value(); if (! lv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (NetEConst*tmp = must_be_leeq_(re, lv, true)) { return tmp; } /* Now go on to the normal test of the values. */ const NetEConst*r = dynamic_cast(re); if (r == 0) return 0; verinum rv = r->value(); if (! rv.is_defined()) { NetEConst*res = new NetEConst(verinum(verinum::Vx, 1)); ivl_assert(*this, res); return res; } if (lv >= rv) { NetEConst*res = new NetEConst(verinum(verinum::V1, 1)); ivl_assert(*this, res); return res; } else { NetEConst*res = new NetEConst(verinum(verinum::V0, 1)); ivl_assert(*this, res); return res; } } /* * Evaluate == or !=. The equality operator checks all the * bits and returns true(false) if there are any bits in the vector * that are defined (0 or 1) and different. If all the defined bits * are equal, but there are are x/z bits, then the situation is * ambiguous so the result is x. */ NetEConst* NetEBComp::eval_eqeq_real_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { double lval; double rval; bool flag = get_real_arguments(le, re, lval, rval); if (! flag) return 0; verinum result(((lval == rval) ^ ne_flag) ? verinum::V1 : verinum::V0, 1); NetEConst*res = new NetEConst(result); ivl_assert(*this, res); return res; } NetEConst* NetEBComp::eval_eqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) return eval_eqeq_real_(ne_flag, le, re); const NetEConst*lc = dynamic_cast(le); const NetEConst*rc = dynamic_cast(re); if (lc == 0 || rc == 0) return 0; const verinum&lv = lc->value(); const verinum&rv = rc->value(); const verinum::V eq_res = ne_flag? verinum::V0 : verinum::V1; const verinum::V ne_res = ne_flag? verinum::V1 : verinum::V0; verinum::V res = eq_res; unsigned top = lv.len(); if (rv.len() < top) top = rv.len(); for (unsigned idx = 0 ; idx < top ; idx += 1) { bool x_bit_present = false; switch (lv.get(idx)) { case verinum::Vx: case verinum::Vz: res = verinum::Vx; x_bit_present = true; break; default: break; } switch (rv.get(idx)) { case verinum::Vx: case verinum::Vz: res = verinum::Vx; x_bit_present = true; break; default: break; } if (x_bit_present) continue; if (rv.get(idx) != lv.get(idx)) { res = ne_res; break; } } if (res != verinum::Vx) { verinum::V lpad = verinum::V0; verinum::V rpad = verinum::V0; if (lv.has_sign() && lv.get(lv.len()-1) == verinum::V1) lpad = verinum::V1; if (rv.has_sign() && rv.get(rv.len()-1) == verinum::V1) rpad = verinum::V1; for (unsigned idx = top ; idx < lv.len() ; idx += 1) switch (lv.get(idx)) { case verinum::Vx: case verinum::Vz: res = verinum::Vx; break; case verinum::V0: if (res != verinum::Vx && rpad != verinum::V0) res = ne_res; break; case verinum::V1: if (res != verinum::Vx && rpad != verinum::V1) res = ne_res; break; default: break; } for (unsigned idx = top ; idx < rv.len() ; idx += 1) switch (rv.get(idx)) { case verinum::Vx: case verinum::Vz: res = verinum::Vx; break; case verinum::V0: if (res != verinum::Vx && lpad != verinum::V0) res = ne_res; break; case verinum::V1: if (res != verinum::Vx && lpad != verinum::V1) res = ne_res; break; default: break; } } NetEConst*result = new NetEConst(verinum(res, 1)); ivl_assert(*this, result); return result; } NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { const NetEConst*lc = dynamic_cast(le); const NetEConst*rc = dynamic_cast(re); if (lc == 0 || rc == 0) return 0; const verinum&lv = lc->value(); const verinum&rv = rc->value(); verinum::V res = verinum::V1; // Find the smallest argument length. unsigned cnt = lv.len(); if (cnt > rv.len()) cnt = rv.len(); // Check the common bits. for (unsigned idx = 0 ; idx < cnt ; idx += 1) if (lv.get(idx) != rv.get(idx)) { res = verinum::V0; break; } bool is_signed = lv.has_sign() && rv.has_sign(); // If the left value is longer check it against the pad bit. if (res == verinum::V1) { verinum::V pad = verinum::V0; if (is_signed) pad = rv.get(rv.len()-1); for (unsigned idx = cnt ; idx < lv.len() ; idx += 1) if (lv.get(idx) != pad) { res = verinum::V0; break; } } // If the right value is longer check it against the pad bit. if (res == verinum::V1) { verinum::V pad = verinum::V0; if (is_signed) pad = lv.get(lv.len()-1); for (unsigned idx = cnt ; idx < rv.len() ; idx += 1) { if (rv.get(idx) != pad) res = verinum::V0; break; } } if (ne_flag) { if (res == verinum::V0) res = verinum::V1; else res = verinum::V0; } NetEConst*result = new NetEConst(verinum(res, 1)); ivl_assert(*this, result); return result; } NetEConst* NetEBComp::eval_arguments_(const NetExpr*l, const NetExpr*r) const { NetEConst*res = 0; switch (op_) { case 'E': // Case equality (===) res = eval_eqeqeq_(false, l, r); break; case 'e': // Equality (==) res = eval_eqeq_(false, l, r); break; case 'G': // >= res = eval_gteq_(l, r); break; case 'L': // <= res = eval_leeq_(l, r); break; case 'N': // Case inequality (!==) res = eval_eqeqeq_(true, l, r); break; case 'n': // not-equal (!=) res = eval_eqeq_(true, l, r); break; case '<': // Less than res = eval_less_(l, r); break; case '>': // Greater than res = eval_gt_(l, r); break; } eval_debug(this, res, l->expr_type() == IVL_VT_REAL || r->expr_type() == IVL_VT_REAL); return res; } NetExpr* NetEBDiv::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { double lval; double rval; bool flag = get_real_arguments(l, r, lval, rval); if (! flag) return 0; double res_val = 0.0; switch (op_) { case '/': res_val = lval / rval; break; case '%': // Since this could/may be called early we don't want to // leak functionality. if (!gn_icarus_misc_flag) return 0; res_val = fmod(lval, rval); break; } NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); eval_debug(this, res, true); return res; } NetExpr* NetEBDiv::eval_arguments_(const NetExpr*l, const NetExpr*r) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); assert(expr_type() == IVL_VT_LOGIC); const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); ivl_assert(*this, rval.len() == wid); verinum val; switch (op_) { case '/': val = cast_to_width(lval / rval, wid); break; case '%': val = cast_to_width(lval % rval, wid); break; default: return 0; } NetExpr*tmp = new NetEConst(val); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } NetEConst* NetEBLogic::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { double lval; double rval; bool flag = get_real_arguments(l, r, lval, rval); if (! flag) return 0; verinum::V res; switch (op_) { case 'a': // Logical AND (&&) if ((lval != 0.0) && (rval != 0.0)) res = verinum::V1; else res = verinum::V0; break; case 'o': // Logical OR (||) if ((lval != 0.0) || (rval != 0.0)) res = verinum::V1; else res = verinum::V0; break; default: return 0; } NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); eval_debug(this, tmp, true); return tmp; } NetEConst* NetEBLogic::eval_arguments_(const NetExpr*l, const NetExpr*r) const { if (l->expr_type() == IVL_VT_REAL || r->expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); assert(expr_type() == IVL_VT_LOGIC); const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; verinum::V lv = verinum::V0; verinum::V rv = verinum::V0; verinum v = lc->value(); for (unsigned idx = 0 ; idx < v.len() ; idx += 1) if (v.get(idx) == verinum::V1) { lv = verinum::V1; break; } if (lv == verinum::V0 && ! v.is_defined()) lv = verinum::Vx; v = rc->value(); for (unsigned idx = 0 ; idx < v.len() ; idx += 1) if (v.get(idx) == verinum::V1) { rv = verinum::V1; break; } if (rv == verinum::V0 && ! v.is_defined()) rv = verinum::Vx; verinum::V res; switch (op_) { case 'a': // Logical AND (&&) if ((lv == verinum::V0) || (rv == verinum::V0)) res = verinum::V0; else if ((lv == verinum::V1) && (rv == verinum::V1)) res = verinum::V1; else res = verinum::Vx; break; case 'o': // Logical OR (||) if ((lv == verinum::V1) || (rv == verinum::V1)) res = verinum::V1; else if ((lv == verinum::V0) && (rv == verinum::V0)) res = verinum::V0; else res = verinum::Vx; break; default: return 0; } NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } NetExpr* NetEBMinMax::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { double lval; double rval; bool flag = get_real_arguments(l, r, lval, rval); if (! flag) return 0; double res_val; switch (op()) { case 'm': res_val = lval < rval ? lval : rval; break; case 'M': res_val = lval > rval ? lval : rval; break; default: ivl_assert(*this, 0); } NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); eval_debug(this, res, true); return res; } NetExpr* NetEBMinMax::eval_arguments_(const NetExpr*l, const NetExpr*r) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); assert(expr_type() == IVL_VT_LOGIC); const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); ivl_assert(*this, rval.len() == wid); verinum res_val; if (lval.is_defined() && rval.is_defined()) { switch (op()) { case 'm': res_val = lval < rval ? lval : rval; break; case 'M': res_val = lval > rval ? lval : rval; break; default: ivl_assert(*this, 0); } } else { res_val = verinum(verinum::Vx, wid); } res_val.has_sign(has_sign()); NetEConst*res = new NetEConst(res_val); ivl_assert(*this, res); eval_debug(this, res, false); return res; } NetExpr* NetEBMult::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { double lval; double rval; bool flag = get_real_arguments(l, r, lval, rval); if (! flag) return 0; NetECReal*res = new NetECReal( verireal(lval * rval) ); ivl_assert(*this, res); eval_debug(this, res, true); return res; } NetExpr* NetEBMult::eval_arguments_(const NetExpr*l, const NetExpr*r) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); assert(expr_type() == IVL_VT_LOGIC); const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); ivl_assert(*this, rval.len() == wid); verinum val = cast_to_width(lval * rval, wid); NetEConst*tmp = new NetEConst(val); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } NetExpr* NetEBPow::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { double lval; double rval; bool flag = get_real_arguments(l, r, lval, rval); if (! flag) return 0; NetECReal*res = new NetECReal( verireal( pow(lval,rval) ) ); ivl_assert(*this, res); eval_debug(this, res, true); return res; } NetExpr* NetEBPow::eval_arguments_(const NetExpr*l, const NetExpr*r) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); assert(expr_type() == IVL_VT_LOGIC); const NetEConst*lc = dynamic_cast(l); const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); verinum rval = rc->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lval.len() == wid); verinum val = cast_to_width(pow(lval, rval), wid); NetEConst*res = new NetEConst(val); ivl_assert(*this, res); eval_debug(this, res, false); return res; } NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const { const NetEConst*le = dynamic_cast(l); const NetEConst*re = dynamic_cast(r); if (le == 0 || re == 0) return 0; NetEConst*res; verinum lv = le->value(); verinum rv = re->value(); unsigned wid = expr_width(); ivl_assert(*this, wid > 0); ivl_assert(*this, lv.len() == wid); verinum val; if (rv.is_defined()) { unsigned shift = rv.as_unsigned(); switch (op_) { case 'l': val = cast_to_width(lv << shift, wid); break; case 'r': lv.has_sign(false); case 'R': val = cast_to_width(lv >> shift, wid); break; default: return 0; } } else { val = verinum(verinum::Vx, wid); } val.has_sign(has_sign()); res = new NetEConst(val); ivl_assert(*this, res); eval_debug(this, res, false); return res; } NetEConst* NetEConcat::eval_tree() { unsigned local_errors = 0; unsigned gap = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { // Parameter not here? This is an error, but presumably // already caught and we are here just to catch more. if (parms_[idx] == 0) continue; // If this parameter is already a constant, all is well // so go on. if (dynamic_cast(parms_[idx])) { gap += parms_[idx]->expr_width(); continue; } // Finally, try to evaluate the parameter expression // that is here. If I succeed, reset the parameter to // the evaluated value. assert(parms_[idx]); NetExpr*expr = parms_[idx]->eval_tree(); if (expr) { expr->set_line(*parms_[idx]); delete parms_[idx]; parms_[idx] = expr; if (! expr->has_width()) { cerr << get_fileline() << ": error: concatenation " << "operand has indefinite width: " << *parms_[idx] << endl; local_errors += 1; } else if (expr->expr_width() == 0) { cerr << expr->get_fileline() << ": internal error: " << "Operand of concatenation has no width: " << *expr << endl; local_errors += 1; } gap += expr->expr_width(); } } if (local_errors > 0) return 0; return eval_arguments_(parms_, gap); } NetEConst* NetEConcat::eval_arguments_(const vector&vals, unsigned gap) const { unsigned repeat_val = repeat(); // At this point, the "gap" is the width of a single repeat of // the concatenation. The total width of the result is the gap // times the repeat count. verinum val (verinum::Vx, repeat_val * gap); // build up the result from least significant to most. unsigned cur = 0; bool is_string_flag = true; for (unsigned idx = vals.size() ; idx > 0 ; idx -= 1) { const NetEConst*expr = dynamic_cast(vals[idx-1]); if (expr == 0) return 0; verinum tmp = expr->value(); for (unsigned bit = 0; bit < tmp.len(); bit += 1, cur += 1) for (unsigned rep = 0 ; rep < repeat_val ; rep += 1) val.set(rep*gap+cur, tmp[bit]); is_string_flag = is_string_flag && tmp.is_string(); } /* If all the values were strings, then re-stringify this constant. This might be useful information in the code generator or other optimizer steps. */ if (is_string_flag) { val = verinum(val.as_string()); } // Normally, concatenations are unsigned. However, the // $signed() function works by marking the expression as // signed, so we really have to check. val.has_sign( this->has_sign() ); NetEConst*res = new NetEConst(val); ivl_assert(*this, res); eval_debug(this, res, false); return res; } NetEConst* NetESelect::eval_tree() { eval_expr(expr_); NetEConst*expr = dynamic_cast(expr_); long bval = 0; if (base_) { eval_expr(base_); NetEConst*base = dynamic_cast(base_); if (base == 0) return 0; bval = base->value().as_long(); } if (expr == 0) return 0; verinum eval = expr->value(); verinum oval (verinum::V0, expr_width(), true); verinum::V pad_bit = verinum::Vx; if (base_ == 0) { /* If the base is NULL (different from 0) then this select is here for zero or sign extension. So calculate a proper pad bit. */ if (has_sign()) pad_bit = eval.get(expr->expr_width()-1); else pad_bit = verinum::V0; } for (unsigned long idx = 0 ; idx < expr_width() ; idx += 1) { if ((bval >= 0) && ((unsigned long) bval < eval.len())) oval.set(idx, eval.get(bval)); else oval.set(idx, pad_bit); bval += 1; } oval.has_sign(has_sign()); NetEConst*res = new NetEConst(oval); eval_debug(this, res, false); return res; } static void print_ternary_cond(NetExpr*expr) { if (NetEConst*c = dynamic_cast(expr)) { cerr << c->value() << endl; return; } if (NetECReal*c = dynamic_cast(expr)) { cerr << c->value() << endl; return; } assert(0); } /* * A ternary expression evaluation is controlled by the condition * expression. If the condition evaluates to true or false, then * return the evaluated true or false expression. If the condition * evaluates to x or z, then merge the constant bits of the true and * false expressions. */ NetExpr* NetETernary::eval_tree() { eval_expr(cond_); switch (const_logical(cond_)) { case C_0: eval_expr(false_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; print_ternary_cond(cond_); cerr << get_fileline() << ": : Selecting false case: " << *false_val_ << endl; } if (expr_type() == IVL_VT_REAL && false_val_->expr_type() != IVL_VT_REAL) { verireal f; if (get_real_arg_(false_val_, f)) { NetECReal*rc = new NetECReal(f); rc->set_line(*this); return rc; } } return false_val_->dup_expr(); case C_1: eval_expr(true_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; print_ternary_cond(cond_); cerr << get_fileline() << ": : Selecting true case: " << *true_val_ << endl; } if (expr_type() == IVL_VT_REAL && true_val_->expr_type() != IVL_VT_REAL) { verireal t; if (get_real_arg_(true_val_, t)) { NetECReal*rc = new NetECReal(t); rc->set_line(*this); return rc; } } return true_val_->dup_expr(); case C_X: break; default: return 0; } /* Here we have a more complex case. We need to evaluate both expressions down to constants then compare the values to build up a constant result. */ eval_expr(true_val_); eval_expr(false_val_); return blended_arguments_(true_val_, false_val_); } NetExpr*NetETernary::blended_arguments_(const NetExpr*te, const NetExpr*fe) const { const NetEConst*t = dynamic_cast(te); const NetEConst*f = dynamic_cast(fe); if (t == 0 || f == 0) { verireal tv, fv; if (!get_real_arg_(te, tv)) return 0; if (!get_real_arg_(fe, fv)) return 0; verireal val = verireal(0.0); if (tv.as_double() == fv.as_double()) val = tv; if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; print_ternary_cond(cond_); cerr << get_fileline() << ": : Blending real cases " << "true=" << tv.as_double() << ", false=" << fv.as_double() << ", to get " << val << endl; } NetECReal*rc = new NetECReal(val); rc->set_line(*this); return rc; } unsigned tsize = t->expr_width(); unsigned fsize = f->expr_width(); /* Size of the result is the size of the widest operand. */ unsigned rsize = tsize > fsize? tsize : fsize; verinum val (verinum::V0, rsize); for (unsigned idx = 0 ; idx < rsize ; idx += 1) { verinum::V tv = idx < tsize? t->value().get(idx) : verinum::V0; verinum::V fv = idx < fsize? f->value().get(idx) : verinum::V0; if (tv == fv) val.set(idx, tv); else val.set(idx, verinum::Vx); } val.has_sign(has_sign()); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; print_ternary_cond(cond_); cerr << get_fileline() << ": : Blending cases to get " << val << endl; } NetEConst*rc = new NetEConst(val); rc->set_line(*this); return rc; } NetExpr* NetEUnary::eval_tree() { eval_expr(expr_); return eval_arguments_(expr_); } NetExpr* NetEUnary::eval_tree_real_(const NetExpr*ex) const { const NetECReal*val= dynamic_cast (ex); if (val == 0) return 0; double res_val = val->value().as_double(); switch (op_) { case '+': break; case '-': res_val = -res_val; break; case 'm': if (res_val < 0.0) res_val = -res_val; break; default: return 0; } NetECReal *res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); eval_debug(this, res, true); return res; } NetExpr* NetEUnary::eval_arguments_(const NetExpr*ex) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(ex); const NetEConst*rval = dynamic_cast(ex); if (rval == 0) return 0; verinum val = rval->value(); switch (op_) { case '+': /* Unary + is a no-op. */ break; case '-': val = -val; break; case 'm': if (!val.is_defined()) { for (unsigned idx = 0 ; idx < val.len() ; idx += 1) val.set(idx, verinum::Vx); } else if (val.is_negative()) { val = -val; } break; case '~': /* Bitwise not is even simpler than logical not. Just invert all the bits of the operand and make the new value with the same dimensions. */ for (unsigned idx = 0 ; idx < val.len() ; idx += 1) switch (val.get(idx)) { case verinum::V0: val.set(idx, verinum::V1); break; case verinum::V1: val.set(idx, verinum::V0); break; default: val.set(idx, verinum::Vx); } break; case '!': assert(0); default: return 0; } NetEConst *res = new NetEConst(val); ivl_assert(*this, res); eval_debug(this, res, false); return res; } NetEConst* NetEUReduce::eval_tree_real_(const NetExpr*ex) const { ivl_assert(*this, op_ == '!'); const NetECReal*val= dynamic_cast (ex); if (val == 0) return 0; verinum::V res = val->value().as_double() == 0.0 ? verinum::V1 : verinum::V0; NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); eval_debug(this, tmp, true); return tmp; } NetEConst* NetEUReduce::eval_arguments_(const NetExpr*ex) const { if (expr_type() == IVL_VT_REAL) return eval_tree_real_(ex); const NetEConst*rval = dynamic_cast(ex); if (rval == 0) return 0; verinum val = rval->value(); verinum::V res; bool invert = false; switch (op_) { case '!': { /* Evaluate the unary logical not by first scanning the operand value for V1 and Vx bits. If we find any V1 bits we know that the value is TRUE, so the result of ! is V0. If there are no V1 bits but there are some Vx/Vz bits, the result is unknown. Otherwise, the result is V1. */ bool v1 = false, vx = false; for (unsigned idx = 0 ; idx < val.len() && !v1 ; idx += 1) { switch (val.get(idx)) { case verinum::V0: break; case verinum::V1: v1 = true; break; default: vx = true; break; } } res = v1? verinum::V0 : (vx? verinum::Vx : verinum::V1); break; } case 'A': invert = true; case '&': { res = verinum::V1; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) res = res & val.get(idx); break; } case 'N': invert = true; case '|': { res = verinum::V0; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) res = res | val.get(idx); break; } case 'X': invert = true; case '^': { /* Reduction XOR. */ unsigned ones = 0, unknown = 0; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) switch (val.get(idx)) { case verinum::V0: break; case verinum::V1: ones += 1; break; default: unknown += 1; break; } if (unknown) res = verinum::Vx; else if (ones%2) res = verinum::V1; else res = verinum::V0; break; } default: return 0; } if (invert) res = ~res; NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); eval_debug(this, tmp, false); return tmp; } NetExpr* NetECast::eval_arguments_(const NetExpr*ex) const { NetExpr*res = 0; switch (op_) { case 'r': if (const NetEConst*val = dynamic_cast(ex)) { verireal res_val(val->value().as_double()); res = new NetECReal(res_val); } break; case '2': if (const NetEConst*val = dynamic_cast(ex)) { verinum res_val(val->value()); res_val.cast_to_int2(); if (expr_width() > 0) res_val = cast_to_width(res_val, expr_width()); res = new NetEConst(res_val); } case 'v': if (const NetECReal*val = dynamic_cast(ex)) { verinum res_val(val->value().as_double(), false); if (expr_width() > 0) res_val = cast_to_width(res_val, expr_width()); res = new NetEConst(res_val); } break; default: ivl_assert(*this, 0); return 0; } if (res == 0) return 0; ivl_assert(*this, res); eval_debug(this, res, op_ == 'r'); return res; } NetEConst* NetESFunc::evaluate_clog2_(const NetExpr*arg_) const { const NetEConst*tmpi = dynamic_cast(arg_); const NetECReal*tmpr = dynamic_cast(arg_); if (tmpi == 0 && tmpr == 0) return 0; verinum arg; if (tmpi) { arg = tmpi->value(); } else { arg = verinum(tmpr->value().as_double(), true); } NetEConst*rtn; /* If we have an x in the verinum we return 'bx. */ if (!arg.is_defined()) { verinum tmp (verinum::Vx, integer_width); tmp.has_sign(true); rtn = new NetEConst(tmp); ivl_assert(*this, rtn); } else { bool is_neg = false; uint64_t res = 0; if (arg.is_negative()) { is_neg = true; // If the length is not defined, then work with // the trimmed version of the number. if (! arg.has_len()) arg = trim_vnum(arg); } arg.has_sign(false); // $unsigned() if (!arg.is_zero()) { arg = arg - verinum((uint64_t)1, 1); while (!arg.is_zero()) { res += 1; arg = arg >> 1; } } if (is_neg && res < integer_width) res = integer_width; verinum tmp (res, integer_width); tmp.has_sign(true); rtn = new NetEConst(tmp); ivl_assert(*this, rtn); } eval_debug(this, rtn, false); return rtn; } NetEConst* NetESFunc::evaluate_rtoi_(const NetExpr*arg_) const { const NetEConst*tmpi = dynamic_cast(arg_); const NetECReal*tmpr = dynamic_cast(arg_); if (tmpi == 0 && tmpr == 0) return 0; /* If the argument is already a bit based value just extend/trim it * to the integer width and translate all undefined bits to zero. */ if (tmpi) { verinum arg = verinum(tmpi->value(), integer_width); arg.cast_to_int2(); return new NetEConst(arg); } /* Get the value of the real argument as a bit based value and then * extend/trim it to the integer width. */ double arg = tmpr->value().as_double(); if (arg >= 0.0) arg = floor(arg); else arg = ceil(arg); return new NetEConst(verinum(verinum(arg, false), integer_width)); } NetECReal* NetESFunc::evaluate_itor_(const NetExpr*arg_) const { const NetEConst*tmpi = dynamic_cast(arg_); const NetECReal*tmpr = dynamic_cast(arg_); if (tmpi == 0 && tmpr == 0) return 0; /* If the argument is already a real value round it, but NaN and * +/- infinity need to be translated to 0.0. */ if (tmpr) { double arg = tmpr->value().as_double(); /* Convert a NaN or +/- infinity to 0.0 since these convert * to 'bz which is then translated to 0.0. */ if (arg != arg || (arg && (arg == 0.5*arg))) { return new NetECReal(verireal(0.0)); } if (arg >= 0.0) arg = floor(arg + 0.5); else arg = ceil(arg - 0.5); return new NetECReal(verireal(arg)); } /* Convert the bit based value to a real value. */ double arg = tmpi->value().as_double(); return new NetECReal(verireal(arg)); } NetECReal* NetESFunc::evaluate_math_one_arg_(ID id, const NetExpr*arg_) const { const NetEConst*tmpi = dynamic_cast(arg_); const NetECReal*tmpr = dynamic_cast(arg_); NetECReal*res = 0; if (tmpi || tmpr) { double arg; if (tmpi) { arg = tmpi->value().as_double(); } else { arg = tmpr->value().as_double(); } switch (id) { case LN: res = new NetECReal(verireal(log(arg))); break; case LOG10: res = new NetECReal(verireal(log10(arg))); break; case EXP: res = new NetECReal(verireal(exp(arg))); break; case SQRT: res = new NetECReal(verireal(sqrt(arg))); break; case FLOOR: res = new NetECReal(verireal(floor(arg))); break; case CEIL: res = new NetECReal(verireal(ceil(arg))); break; case SIN: res = new NetECReal(verireal(sin(arg))); break; case COS: res = new NetECReal(verireal(cos(arg))); break; case TAN: res = new NetECReal(verireal(tan(arg))); break; case ASIN: res = new NetECReal(verireal(asin(arg))); break; case ACOS: res = new NetECReal(verireal(acos(arg))); break; case ATAN: res = new NetECReal(verireal(atan(arg))); break; case SINH: res = new NetECReal(verireal(sinh(arg))); break; case COSH: res = new NetECReal(verireal(cosh(arg))); break; case TANH: res = new NetECReal(verireal(tanh(arg))); break; case ASINH: res = new NetECReal(verireal(asinh(arg))); break; case ACOSH: res = new NetECReal(verireal(acosh(arg))); break; case ATANH: res = new NetECReal(verireal(atanh(arg))); break; default: ivl_assert(*this, 0); break; } ivl_assert(*this, res); } eval_debug(this, res, true); return res; } NetECReal* NetESFunc::evaluate_math_two_arg_(ID id, const NetExpr*arg0_, const NetExpr*arg1_) const { const NetEConst*tmpi0 = dynamic_cast(arg0_); const NetECReal*tmpr0 = dynamic_cast(arg0_); const NetEConst*tmpi1 = dynamic_cast(arg1_); const NetECReal*tmpr1 = dynamic_cast(arg1_); NetECReal*res = 0; if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) { double arg0, arg1; if (tmpi0) { arg0 = tmpi0->value().as_double(); } else { arg0 = tmpr0->value().as_double(); } if (tmpi1) { arg1 = tmpi1->value().as_double(); } else { arg1 = tmpr1->value().as_double(); } switch (id) { case POW: res = new NetECReal(verireal(pow(arg0, arg1))); break; case ATAN2: res = new NetECReal(verireal(atan2(arg0, arg1))); break; case HYPOT: res = new NetECReal(verireal(hypot(arg0, arg1))); break; default: ivl_assert(*this, 0); break; } ivl_assert(*this, res); } eval_debug(this, res, true); return res; } NetExpr* NetESFunc::evaluate_abs_(const NetExpr*arg_) const { NetExpr*res = 0; const NetEConst*tmpi = dynamic_cast(arg_); const NetECReal*tmpr = dynamic_cast(arg_); if (tmpi || tmpr) { double arg; if (tmpi) { arg = tmpi->value().as_double(); } else { arg = tmpr->value().as_double(); } res = new NetECReal(verireal(fabs(arg))); ivl_assert(*this, res); } eval_debug(this, res, true); return res; } NetExpr* NetESFunc::evaluate_min_max_(ID id, const NetExpr*arg0_, const NetExpr*arg1_) const { const NetEConst*tmpi0 = dynamic_cast(arg0_); const NetECReal*tmpr0 = dynamic_cast(arg0_); const NetEConst*tmpi1 = dynamic_cast(arg1_); const NetECReal*tmpr1 = dynamic_cast(arg1_); NetExpr*res = 0; if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) { double arg0, arg1; if (tmpi0) { arg0 = tmpi0->value().as_double(); } else { arg0 = tmpr0->value().as_double(); } if (tmpi1) { arg1 = tmpi1->value().as_double(); } else { arg1 = tmpr1->value().as_double(); } switch (id) { case MIN: res = new NetECReal(verireal(arg0 < arg1 ? arg0 : arg1)); break; case MAX: res = new NetECReal(verireal(arg0 < arg1 ? arg1 : arg0)); break; default: ivl_assert(*this, 0); break; } ivl_assert(*this, res); } eval_debug(this, res, true); return res; } NetEConst* NetESFunc::evaluate_countbits_(const NetExpr* /*arg0*/, const NetExpr* /*arg1*/) const { return 0; } NetEConst* NetESFunc::evaluate_countones_(const NetExpr* /*arg*/) const { return 0; } /* Get the total number of dimensions for the given expression. */ NetEConst* NetESFunc::evaluate_dimensions_(const NetExpr*arg) const { const NetESignal*esig = dynamic_cast(arg); long res = 0; if (esig != 0) { const NetNet *sig = esig->sig(); res = sig->packed_dimensions() + sig->unpacked_dimensions(); /* Icarus does not think a string has a packed size so to * make these routines work correct add one if this is a * string data type. */ if (sig->data_type() == IVL_VT_STRING) { assert(sig->packed_dimensions() == 0); res += 1; } } /* Return the result as an integer sized constant. */ return new NetEConst(verinum(verinum(res), integer_width)); } NetEConst* NetESFunc::evaluate_isunknown_(const NetExpr* /*arg*/) const { return 0; } NetEConst* NetESFunc::evaluate_onehot_(const NetExpr* /*arg*/) const { return 0; } NetEConst* NetESFunc::evaluate_onehot0_(const NetExpr* /*arg*/) const { return 0; } /* Get the number of unpacked dimensions for the given expression. */ NetEConst* NetESFunc::evaluate_unpacked_dimensions_(const NetExpr*arg) const { const NetESignal*esig = dynamic_cast(arg); long res = 0; if (esig != 0) { const NetNet *sig = esig->sig(); res = sig->unpacked_dimensions(); } /* Return the result as an integer sized constant. */ return new NetEConst(verinum(verinum(res), integer_width)); } /* This code assumes that the dimension value will fit in a long. * Return true if no constant dimension value is available. */ static bool check_dimension(const NetExpr*dim_expr, long &dim) { const NetEConst*dimi = dynamic_cast(dim_expr); const NetECReal*dimr = dynamic_cast(dim_expr); if (dimi == 0 && dimr == 0) return true; if (dimi) dim = dimi->value().as_long(); if (dimr) dim = dimr->value().as_long(); return false; } /* Get the left and right values for the argument at the given dimension * if it exists. Return true if no values are available. Set defer to true * if this should be handled in the run time. */ static bool get_array_info(const NetExpr*arg, long dim, long &left, long &right, bool&defer) { if (const NetEConstParam*param = dynamic_cast(arg)) { assert(dim == 1); left = param->expr_width() - 1; right = 0; return false; } /* The argument must be a signal that has enough dimensions. */ const NetESignal*esig = dynamic_cast(arg); if (esig == 0) return true; const NetNet *sig = esig->sig(); /* A string or dynamic array must be handled by the run time. */ switch (sig->data_type()) { case IVL_VT_DARRAY: case IVL_VT_QUEUE: case IVL_VT_STRING: defer = true; return true; break; default: break; } long pdims = sig->packed_dimensions(); long updims = sig->unpacked_dimensions(); if (dim > (pdims + updims)) return true; /* Get the appropriate unpacked or packed dimension information. */ if (dim > updims) { const vector&dim_vals = sig->packed_dims(); const netrange_t&range = dim_vals[dim-updims-1]; left = range.get_msb(); right = range.get_lsb(); } else { const vector&dim_vals = sig->unpacked_dims(); const netrange_t&range = dim_vals[dim-1]; left = range.get_msb(); right = range.get_lsb(); } return false; } /* Calculate the array property functions. */ NetEConst* NetESFunc::evaluate_array_funcs_(ID id, const NetExpr*arg0, const NetExpr*arg1) const { long dim = 0; /* Check to see if the dimension argument is constant. */ if (check_dimension(arg1, dim)) return 0; /* If dimension is less than 1 return undefined. */ if (dim < 1) { return new NetEConst(verinum(verinum::Vx, integer_width)); } /* Get the left/right information for this dimension if it exists. */ long left = 0; long right = 0; bool defer = false; if (get_array_info(arg0, dim, left, right, defer)) { /* If this is a string or dynamic array defer this function * call since the left/right information is dynamic and is * not available yet. */ if (defer) return 0; return new NetEConst(verinum(verinum::Vx, integer_width)); } /* Calculate the appropriate array function result. */ long res; switch (id) { case HIGH: res = (right > left) ? right : left; break; case INCR: res = (right > left) ? -1 : 1; break; case LEFT: res = left; break; case LOW: res = (right > left) ? left : right; break; case RIGHT: res = right; break; case SIZE: res = (right > left) ? right - left : left - right; res += 1; break; default: res = 0; assert(0); } /* Return the result as an integer sized constant. */ return new NetEConst(verinum(verinum(res), integer_width)); } /* Make a constant one value that can be used by the one argument * array properties calls. */ const NetEConst* NetESFunc::const_one_ = new NetEConst(verinum(1U, 32U)); NetExpr* NetESFunc::evaluate_one_arg_(ID id, const NetExpr*arg) const { switch (id) { case ABS: return evaluate_abs_(arg); case CLOG2: return evaluate_clog2_(arg); case CTONES: return evaluate_countones_(arg); case DIMS: return evaluate_dimensions_(arg); /* The array functions are handled together. */ case HIGH: case INCR: case LEFT: case LOW: case RIGHT: case SIZE: return evaluate_array_funcs_(id, arg, const_one_); case ISUNKN: return evaluate_isunknown_(arg); case ITOR: return evaluate_itor_(arg); case ONEHT: return evaluate_onehot_(arg); case ONEHT0: return evaluate_onehot0_(arg); case RTOI: return evaluate_rtoi_(arg); case UPDIMS: return evaluate_unpacked_dimensions_(arg); default: return evaluate_math_one_arg_(id, arg); } } NetExpr* NetESFunc::evaluate_two_arg_(ID id, const NetExpr*arg0, const NetExpr*arg1) const { switch (id) { case CTBITS: return evaluate_countbits_(arg0, arg1); /* The array functions are handled together. */ case HIGH: case INCR: case LEFT: case LOW: case RIGHT: case SIZE: return evaluate_array_funcs_(id, arg0, arg1); case MAX: case MIN: return evaluate_min_max_(id, arg0, arg1); default: return evaluate_math_two_arg_(id, arg0, arg1); } } NetESFunc::ID NetESFunc::built_in_id_() const { static map built_in_func; static bool funcs_need_init = true; /* These functions are always available. */ if (funcs_need_init) { built_in_func["$itor"] = ITOR; built_in_func["$rtoi"] = RTOI; } /* These are available in 1364-2005 and later or if the Icarus misc * flag was given. */ if (funcs_need_init && ((generation_flag >= GN_VER2005) || gn_icarus_misc_flag)) { built_in_func["$acos" ] = ACOS; built_in_func["$acosh"] = ACOSH; built_in_func["$asin" ] = ASIN; built_in_func["$asinh"] = ASINH; built_in_func["$atan" ] = ATAN; built_in_func["$atanh"] = ATANH; built_in_func["$atan2"] = ATAN2; built_in_func["$ceil" ] = CEIL; built_in_func["$clog2"] = CLOG2; built_in_func["$cos" ] = COS; built_in_func["$cosh" ] = COSH; built_in_func["$exp" ] = EXP; built_in_func["$floor"] = FLOOR; built_in_func["$hypot"] = HYPOT; built_in_func["$ln" ] = LN; built_in_func["$log10"] = LOG10; built_in_func["$pow" ] = POW; built_in_func["$sin" ] = SIN; built_in_func["$sinh" ] = SINH; built_in_func["$sqrt" ] = SQRT; built_in_func["$tan" ] = TAN; built_in_func["$tanh" ] = TANH; } /* These are available in 1800-2005 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2005_SV)) { built_in_func["$dimensions" ] = DIMS; built_in_func["$high" ] = HIGH; built_in_func["$increment" ] = INCR; built_in_func["$isunknown" ] = ISUNKN; built_in_func["$left" ] = LEFT; built_in_func["$low" ] = LOW; built_in_func["$onehot" ] = ONEHT; built_in_func["$onehot0" ] = ONEHT0; built_in_func["$right" ] = RIGHT; built_in_func["$size" ] = SIZE; built_in_func["$unpacked_dimensions" ] = UPDIMS; } /* These are available in 1800-2009 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2009)) { built_in_func["$countones" ] = CTONES; } /* These are available in 1800-2012 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2012)) { built_in_func["$countbits" ] = CTBITS; } /* These are available in Verilog-A as Icarus extensions or if the * Icarus misc flag was given. */ if (funcs_need_init && (gn_verilog_ams_flag || gn_icarus_misc_flag)) { built_in_func["$abs"] = ABS; built_in_func["$max"] = MAX; built_in_func["$min"] = MIN; } /* The function table has been initialized at this point. */ funcs_need_init = false; /* Look for the given function and if it is not available return * NOT_BUILT_IN otherwise return the ID for the function. */ map::iterator idx = built_in_func.find(name_); if (idx == built_in_func.end()) return NOT_BUILT_IN; return idx->second; } NetExpr* NetESFunc::eval_tree() { /* Get the ID for this system function if it is can be used as a * constant function. */ ID id = built_in_id_(); if (id == NOT_BUILT_IN) return 0; switch (parms_.size()) { case 1: if (! takes_nargs_(id, 1)) { cerr << get_fileline() << ": error: " << name_ << "() does not support a single argument." << endl; return 0; } eval_expr(parms_[0]); return evaluate_one_arg_(id, parms_[0]); case 2: if (! takes_nargs_(id, 2)) { cerr << get_fileline() << ": error: " << name_ << "() does not support two arguments." << endl; return 0; } eval_expr(parms_[0]); eval_expr(parms_[1]); return evaluate_two_arg_(id, parms_[0], parms_[1]); default: /* Check to see if the function was called correctly. */ if (! takes_nargs_(id, parms_.size())) { cerr << get_fileline() << ": error: " << name_ << "() does not support " << parms_.size() << " arguments." << endl; return 0; } // HERE: Need to add support for a multi argument $countbits(). cerr << get_fileline() << ": sorry: functions with " << parms_.size() << " arguments are not supported: " << name_ << "()." << endl; return 0; } } NetExpr* NetEUFunc::eval_tree() { // If we know the function cannot be evaluated as a constant, // give up now. if (!func()->is_const_func() || (func()->calls_sys_task() && !need_const_)) return 0; // If we neither want nor need to evaluate the function at // compile time, give up now. if (!opt_const_func && !need_const_) return 0; // Variables inside static functions can be accessed from outside // the function, so we can't be sure they are constant unless the // function was called in a constant context or the user has told // us this is safe. if (!func()->is_auto() && !need_const_ && (opt_const_func < 2)) return 0; // Run through the input parameters to check they are constants. for (unsigned idx = 0; idx < parm_count(); idx += 1) { if (dynamic_cast (parm(idx))) continue; if (dynamic_cast (parm(idx))) continue; return 0; } NetFuncDef*def = func_->func_def(); ivl_assert(*this, def); vectorargs(parms_.size()); for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) args[idx] = parms_[idx]->dup_expr(); NetExpr*res = def->evaluate_function(*this, args); return res; } iverilog-10_1/examples/000077500000000000000000000000001265551621300151605ustar00rootroot00000000000000iverilog-10_1/examples/clbff.v000066400000000000000000000064601265551621300164310ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This source file demonstrates how to synthesize CLB flip-flops from * Icarus Verilog, including giving the device an initial value. * * To compile this for XNF, try a command like this: * * iverilog -txnf -ppart=XC4010XLPQ160 -pncf=clbff.ncf -oclbff.xnf clbff.v * * That command causes an clbff.xnf and clbff.ncf file to be created. * Next, make the clbff.ngd file with the command: * * xnf2ngd -l xilinxun -u clbff.xnf clbff.ngo * ngdbuild clbff.ngo clbff.ngd * * Finally, map the file to fully render it in the target part. The * par command is the step that actually optimizes the design and tries * to meet timing constraints. * * map -o map.ncd clbff.ngd * par -w map.ncd clbff.ncd * * At this point, you can use the FPGA Editor to edit the clbff.ncd * file. Notice that the design uses two CLB flip-flops (possibly in * the same CLB) with their outputs ANDed together. If you go into the * block editor, you will see that the FF connected to main/Q<0> is * configured so start up reset, and the FF connected to main/Q<1> is * configured to start up set. */ module main; wire clk, iclk; wire i0, i1; wire out; wire [1:0] D = {i1, i0}; // This statement declares Q to be a 2 bit reg vector. The // initial assignment will cause the synthesized device to take // on an initial value specified here. Without the assignment, // the initial value is unspecified. (Verilog simulates it as 2'bx.) reg [1:0] Q = 2'b10; // This simple logic gate get turned into a function unit. // The par program will map this into a CLB F or G unit. and (out, Q[0], Q[1]); // This creates a global clock buffer. Notice how I attach an // attribute to the named gate to force it to be mapped to the // desired XNF device. This device will not be pulled into the // IOB associated with iclk because of the attribute. buf gbuf(clk, iclk); $attribute(gbuf, "XNF-LCA", "GCLK:O,I"); // This is mapped to a DFF. Since Q and D are two bits wide, the // code generator actually makes two DFF devices that share a // clock input. always @(posedge clk) Q <= D; // These attribute commands assign pins to the listed wires. // This can be done to wires and registers, as internally both // are treated as named signals. $attribute(out, "PAD", "o150"); $attribute(i0, "PAD", "i152"); $attribute(i1, "PAD", "i153"); $attribute(iclk,"PAD", "i154"); endmodule /* main */ iverilog-10_1/examples/des.v000066400000000000000000001354741265551621300161400ustar00rootroot00000000000000// // Name: testbench1.vhdl // // Author: Chris Eilbeck, chris@yordas.demon.co.uk // // Purpose: VHDL testbench for a DES encryptor. // // IP Status: Free use is hereby granted for all civil use including personal, educational and commercial use. // The use of this code for military, diplomatic or governmental purposes is specifically forbidden. // // Warranty: There is absolutely no warranty given with this code. You accept all responsibility for the use // of this code and any damage so caused. // // Vers Info: v0.1 14/11/1998 - Creation. // 14/11/1999 - Converted to Verilog (ajb) // module top; reg clk; reg [1:64] pt, key; wire [1:64] ct; integer i; des des(pt, key, ct, clk); initial begin $dumpfile("des.vcd"); $dumpvars(0, top); key = 64'h0000000000000000; pt = 64'h0000000000000000; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'hffffffffffffffff; pt = 64'hffffffffffffffff; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h3000000000000000; pt = 64'h1000000000000001; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h1111111111111111; pt = 64'h1111111111111111; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h0123456789abcdef; pt = 64'h1111111111111111; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h1111111111111111; pt = 64'h0123456789abcdef; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h0000000000000000; pt = 64'h0000000000000000; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'hfedcba9876543210; pt = 64'h0123456789abcdef; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h7ca110454a1a6e57; pt = 64'h01a1d6d039776742; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h0131d9619dc1376e; pt = 64'h5cd54ca83def57da; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h07a1133e4a0b2686; pt = 64'h0248d43806f67172; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h3849674c2602319e; pt = 64'h51454b582ddf440a; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h04b915ba43feb5b6; pt = 64'h42fd443059577fa2; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h0113b970fd34f2ce; pt = 64'h059b5e0851cf143a; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h0170f175468fb5e6; pt = 64'h0756d8e0774761d2; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h43297fad38e373fe; pt = 64'h762514b829bf486a; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h07a7137045da2a16; pt = 64'h3bdd119049372802; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h04689104c2fd3b2f; pt = 64'h26955f6835af609a; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h37d06bb516cb7546; pt = 64'h164d5e404f275232; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h1f08260d1ac2465e; pt = 64'h6b056e18759f5cca; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h584023641aba6176; pt = 64'h004bd6ef09176062; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end key = 64'h025816164629b007; pt = 64'h480d39006ee762f2; for(i=0;i<16;i=i+1) begin #1 clk=0; #1 clk=1; end /* int testkeys[]= // key, pt, ct { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8ca64de9, 0xc1b123a7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7359b216, 0x3e4edc58, 0x30000000, 0x00000000, 0x10000000, 0x00000001, 0x958e6e62, 0x7a05557b, 0x11111111, 0x11111111, 0x11111111, 0x11111111, 0xf40379ab, 0x9e0ec533, 0x01234567, 0x89abcdef, 0x11111111, 0x11111111, 0x17668dfc, 0x7292532d, 0x11111111, 0x11111111, 0x01234567, 0x89abcdef, 0x8a5ae1f8, 0x1ab8f2dd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8ca64de9, 0xc1b123a7, 0xfedcba98, 0x76543210, 0x01234567, 0x89abcdef, 0xed39d950, 0xfa74bcc4, 0x7ca11045, 0x4a1a6e57, 0x01a1d6d0, 0x39776742, 0x690f5b0d, 0x9a26939b, 0x0131d961, 0x9dc1376e, 0x5cd54ca8, 0x3def57da, 0x7a389d10, 0x354bd271, 0x07a1133e, 0x4a0b2686, 0x0248d438, 0x06f67172, 0x868ebb51, 0xcab4599a, 0x3849674c, 0x2602319e, 0x51454b58, 0x2ddf440a, 0x7178876e, 0x01f19b2a, 0x04b915ba, 0x43feb5b6, 0x42fd4430, 0x59577fa2, 0xaf37fb42, 0x1f8c4095, 0x0113b970, 0xfd34f2ce, 0x059b5e08, 0x51cf143a, 0x86a560f1, 0x0ec6d85b, 0x0170f175, 0x468fb5e6, 0x0756d8e0, 0x774761d2, 0x0cd3da02, 0x0021dc09, 0x43297fad, 0x38e373fe, 0x762514b8, 0x29bf486a, 0xea676b2c, 0xb7db2b7a, 0x07a71370, 0x45da2a16, 0x3bdd1190, 0x49372802, 0xdfd64a81, 0x5caf1a0f, 0x04689104, 0xc2fd3b2f, 0x26955f68, 0x35af609a, 0x5c513c9c, 0x4886c088, 0x37d06bb5, 0x16cb7546, 0x164d5e40, 0x4f275232, 0x0a2aeeae, 0x3ff4ab77, 0x1f08260d, 0x1ac2465e, 0x6b056e18, 0x759f5cca, 0xef1bf03e, 0x5dfa575a, 0x58402364, 0x1aba6176, 0x004bd6ef, 0x09176062, 0x88bf0db6, 0xd70dee56, 0x02581616, 0x4629b007, 0x480d3900, 0x6ee762f2, 0xa1f99155, 0x41020b56, 0x49793ebc, 0x79b3258f, 0x437540c8, 0x698f3cfa, 0x6fbf1caf, 0xcffd0556, 0x4fb05e15, 0x15ab73a7, 0x072d43a0, 0x77075292, 0x2f22e49b, 0xab7ca1ac, 0x49e95d6d, 0x4ca229bf, 0x02fe5577, 0x8117f12a, 0x5a6b612c, 0xc26cce4a, 0x018310dc, 0x409b26d6, 0x1d9d5c50, 0x18f728c2, 0x5f4c038e, 0xd12b2e41, 0x1c587f1c, 0x13924fef, 0x30553228, 0x6d6f295a, 0x63fac0d0, 0x34d9f793, 0x01010101, 0x01010101, 0x01234567, 0x89abcdef, 0x617b3a0c, 0xe8f07100, 0x1f1f1f1f, 0x0e0e0e0e, 0x01234567, 0x89abcdef, 0xdb958605, 0xf8c8c606, 0xe0fee0fe, 0xf1fef1fe, 0x01234567, 0x89abcdef, 0xedbfd1c6, 0x6c29ccc7, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x355550b2, 0x150e2451, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xcaaaaf4d, 0xeaf1dbae, 0x01234567, 0x89abcdef, 0x00000000, 0x00000000, 0xd5d44ff7, 0x20683d0d, 0xfedcba98, 0x76543210, 0xffffffff, 0xffffffff, 0x2a2bb008, 0xdf97c2f2, }; */ end endmodule module des(pt, key, ct, clk); input [1:64] pt; input [1:64] key; output [1:64] ct; input clk; wire [1:48] k1x,k2x,k3x,k4x,k5x,k6x,k7x,k8x,k9x,k10x,k11x,k12x,k13x,k14x,k15x,k16x; wire [1:32] l0x,l1x,l2x,l3x,l4x,l5x,l6x,l7x,l8x,l9x,l10x,l11x,l12x,l13x,l14x,l15x,l16x; wire [1:32] r0x,r1x,r2x,r3x,r4x,r5x,r6x,r7x,r8x,r9x,r10x,r11x,r12x,r13x,r14x,r15x,r16x; keysched keysched(key, k1x,k2x,k3x,k4x,k5x,k6x,k7x,k8x,k9x,k10x,k11x,k12x,k13x,k14x,k15x,k16x); ip ip(pt, l0x, r0x); roundfunc round1(clk, l0x, r0x, l1x, r1x, k1x); roundfunc round2(clk, l1x, r1x, l2x, r2x, k2x); roundfunc round3(clk, l2x, r2x, l3x, r3x, k3x); roundfunc round4(clk, l3x, r3x, l4x, r4x, k4x); roundfunc round5(clk, l4x, r4x, l5x, r5x, k5x); roundfunc round6(clk, l5x, r5x, l6x, r6x, k6x); roundfunc round7(clk, l6x, r6x, l7x, r7x, k7x); roundfunc round8(clk, l7x, r7x, l8x, r8x, k8x); roundfunc round9(clk, l8x, r8x, l9x, r9x, k9x); roundfunc round10(clk, l9x, r9x, l10x, r10x, k10x); roundfunc round11(clk, l10x, r10x, l11x, r11x, k11x); roundfunc round12(clk, l11x, r11x, l12x, r12x, k12x); roundfunc round13(clk, l12x, r12x, l13x, r13x, k13x); roundfunc round14(clk, l13x, r13x, l14x, r14x, k14x); roundfunc round15(clk, l14x, r14x, l15x, r15x, k15x); roundfunc round16(clk, l15x, r15x, l16x, r16x, k16x); fp fp(r16x, l16x, ct); endmodule module pc1(key, c0x, d0x); input [1:64] key; output [1:28] c0x, d0x; wire [1:56] XX; assign XX[1]=key[57]; assign XX[2]=key[49]; assign XX[3]=key[41]; assign XX[4]=key[33]; assign XX[5]=key[25]; assign XX[6]=key[17]; assign XX[7]=key[9]; assign XX[8]=key[1]; assign XX[9]=key[58]; assign XX[10]=key[50]; assign XX[11]=key[42]; assign XX[12]=key[34]; assign XX[13]=key[26]; assign XX[14]=key[18]; assign XX[15]=key[10]; assign XX[16]=key[2]; assign XX[17]=key[59]; assign XX[18]=key[51]; assign XX[19]=key[43]; assign XX[20]=key[35]; assign XX[21]=key[27]; assign XX[22]=key[19]; assign XX[23]=key[11]; assign XX[24]=key[3]; assign XX[25]=key[60]; assign XX[26]=key[52]; assign XX[27]=key[44]; assign XX[28]=key[36]; assign XX[29]=key[63]; assign XX[30]=key[55]; assign XX[31]=key[47]; assign XX[32]=key[39]; assign XX[33]=key[31]; assign XX[34]=key[23]; assign XX[35]=key[15]; assign XX[36]=key[7]; assign XX[37]=key[62]; assign XX[38]=key[54]; assign XX[39]=key[46]; assign XX[40]=key[38]; assign XX[41]=key[30]; assign XX[42]=key[22]; assign XX[43]=key[14]; assign XX[44]=key[6]; assign XX[45]=key[61]; assign XX[46]=key[53]; assign XX[47]=key[45]; assign XX[48]=key[37]; assign XX[49]=key[29]; assign XX[50]=key[21]; assign XX[51]=key[13]; assign XX[52]=key[5]; assign XX[53]=key[28]; assign XX[54]=key[20]; assign XX[55]=key[12]; assign XX[56]=key[4]; assign c0x=XX[1:28]; assign d0x=XX[29:56]; endmodule module pc2(c,d,k); input [1:28] c,d; output [1:48] k; wire [1:56] YY; assign YY[1:28]=c; assign YY[29:56]=d; assign k[1]=YY[14]; assign k[2]=YY[17]; assign k[3]=YY[11]; assign k[4]=YY[24]; assign k[5]=YY[1]; assign k[6]=YY[5]; assign k[7]=YY[3]; assign k[8]=YY[28]; assign k[9]=YY[15]; assign k[10]=YY[6]; assign k[11]=YY[21]; assign k[12]=YY[10]; assign k[13]=YY[23]; assign k[14]=YY[19]; assign k[15]=YY[12]; assign k[16]=YY[4]; assign k[17]=YY[26]; assign k[18]=YY[8]; assign k[19]=YY[16]; assign k[20]=YY[7]; assign k[21]=YY[27]; assign k[22]=YY[20]; assign k[23]=YY[13]; assign k[24]=YY[2]; assign k[25]=YY[41]; assign k[26]=YY[52]; assign k[27]=YY[31]; assign k[28]=YY[37]; assign k[29]=YY[47]; assign k[30]=YY[55]; assign k[31]=YY[30]; assign k[32]=YY[40]; assign k[33]=YY[51]; assign k[34]=YY[45]; assign k[35]=YY[33]; assign k[36]=YY[48]; assign k[37]=YY[44]; assign k[38]=YY[49]; assign k[39]=YY[39]; assign k[40]=YY[56]; assign k[41]=YY[34]; assign k[42]=YY[53]; assign k[43]=YY[46]; assign k[44]=YY[42]; assign k[45]=YY[50]; assign k[46]=YY[36]; assign k[47]=YY[29]; assign k[48]=YY[32]; endmodule module rol1(o, i); output [1:28] o; input [1:28] i; assign o={i[2:28],i[1]}; endmodule module rol2(o, i); output [1:28] o; input [1:28] i; assign o={i[3:28],i[1:2]}; endmodule module keysched(key,k1x,k2x,k3x,k4x,k5x,k6x,k7x,k8x,k9x,k10x,k11x,k12x,k13x,k14x,k15x,k16x); input [1:64] key; output [1:48] k1x,k2x,k3x,k4x,k5x,k6x,k7x,k8x,k9x,k10x,k11x,k12x,k13x,k14x,k15x,k16x; wire [1:28] c0x,c1x,c2x,c3x,c4x,c5x,c6x,c7x,c8x,c9x,c10x,c11x,c12x,c13x,c14x,c15x,c16x; wire [1:28] d0x,d1x,d2x,d3x,d4x,d5x,d6x,d7x,d8x,d9x,d10x,d11x,d12x,d13x,d14x,d15x,d16x; pc1 pc1(key, c0x, d0x); rol1 rc1(c1x, c0x); rol1 rd1(d1x, d0x); rol1 rc2(c2x, c1x); rol1 rd2(d2x, d1x); rol2 rc3(c3x, c2x); rol2 rd3(d3x, d2x); rol2 rc4(c4x, c3x); rol2 rd4(d4x, d3x); rol2 rc5(c5x, c4x); rol2 rd5(d5x, d4x); rol2 rc6(c6x, c5x); rol2 rd6(d6x, d5x); rol2 rc7(c7x, c6x); rol2 rd7(d7x, d6x); rol2 rc8(c8x, c7x); rol2 rd8(d8x, d7x); rol1 rc9(c9x, c8x); rol1 rd9(d9x, d8x); rol2 rca(c10x, c9x); rol2 rda(d10x, d9x); rol2 rcb(c11x, c10x); rol2 rdb(d11x, d10x); rol2 rcc(c12x, c11x); rol2 rdc(d12x, d11x); rol2 rcd(c13x, c12x); rol2 rdd(d13x, d12x); rol2 rce(c14x, c13x); rol2 rde(d14x, d13x); rol2 rcf(c15x, c14x); rol2 rdf(d15x, d14x); rol1 rcg(c16x, c15x); rol1 rdg(d16x, d15x); pc2 pc2x1(c1x,d1x,k1x); pc2 pc2x2(c2x,d2x,k2x); pc2 pc2x3(c3x,d3x,k3x); pc2 pc2x4(c4x,d4x,k4x); pc2 pc2x5(c5x,d5x,k5x); pc2 pc2x6(c6x,d6x,k6x); pc2 pc2x7(c7x,d7x,k7x); pc2 pc2x8(c8x,d8x,k8x); pc2 pc2x9(c9x,d9x,k9x); pc2 pc2x10(c10x,d10x,k10x); pc2 pc2x11(c11x,d11x,k11x); pc2 pc2x12(c12x,d12x,k12x); pc2 pc2x13(c13x,d13x,k13x); pc2 pc2x14(c14x,d14x,k14x); pc2 pc2x15(c15x,d15x,k15x); pc2 pc2x16(c16x,d16x,k16x); endmodule module s1(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'he; 6'b000010 : so=4'h4; 6'b000100 : so=4'hd; 6'b000110 : so=4'h1; 6'b001000 : so=4'h2; 6'b001010 : so=4'hf; 6'b001100 : so=4'hb; 6'b001110 : so=4'h8; 6'b010000 : so=4'h3; 6'b010010 : so=4'ha; 6'b010100 : so=4'h6; 6'b010110 : so=4'hc; 6'b011000 : so=4'h5; 6'b011010 : so=4'h9; 6'b011100 : so=4'h0; 6'b011110 : so=4'h7; 6'b000001 : so=4'h0; 6'b000011 : so=4'hf; 6'b000101 : so=4'h7; 6'b000111 : so=4'h4; 6'b001001 : so=4'he; 6'b001011 : so=4'h2; 6'b001101 : so=4'hd; 6'b001111 : so=4'h1; 6'b010001 : so=4'ha; 6'b010011 : so=4'h6; 6'b010101 : so=4'hc; 6'b010111 : so=4'hb; 6'b011001 : so=4'h9; 6'b011011 : so=4'h5; 6'b011101 : so=4'h3; 6'b011111 : so=4'h8; 6'b100000 : so=4'h4; 6'b100010 : so=4'h1; 6'b100100 : so=4'he; 6'b100110 : so=4'h8; 6'b101000 : so=4'hd; 6'b101010 : so=4'h6; 6'b101100 : so=4'h2; 6'b101110 : so=4'hb; 6'b110000 : so=4'hf; 6'b110010 : so=4'hc; 6'b110100 : so=4'h9; 6'b110110 : so=4'h7; 6'b111000 : so=4'h3; 6'b111010 : so=4'ha; 6'b111100 : so=4'h5; 6'b111110 : so=4'h0; 6'b100001 : so=4'hf; 6'b100011 : so=4'hc; 6'b100101 : so=4'h8; 6'b100111 : so=4'h2; 6'b101001 : so=4'h4; 6'b101011 : so=4'h9; 6'b101101 : so=4'h1; 6'b101111 : so=4'h7; 6'b110001 : so=4'h5; 6'b110011 : so=4'hb; 6'b110101 : so=4'h3; 6'b110111 : so=4'he; 6'b111001 : so=4'ha; 6'b111011 : so=4'h0; 6'b111101 : so=4'h6; default so=4'hd; endcase endmodule module s2(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'hf; 6'b000010 : so=4'h1; 6'b000100 : so=4'h8; 6'b000110 : so=4'he; 6'b001000 : so=4'h6; 6'b001010 : so=4'hb; 6'b001100 : so=4'h3; 6'b001110 : so=4'h4; 6'b010000 : so=4'h9; 6'b010010 : so=4'h7; 6'b010100 : so=4'h2; 6'b010110 : so=4'hd; 6'b011000 : so=4'hc; 6'b011010 : so=4'h0; 6'b011100 : so=4'h5; 6'b011110 : so=4'ha; 6'b000001 : so=4'h3; 6'b000011 : so=4'hd; 6'b000101 : so=4'h4; 6'b000111 : so=4'h7; 6'b001001 : so=4'hf; 6'b001011 : so=4'h2; 6'b001101 : so=4'h8; 6'b001111 : so=4'he; 6'b010001 : so=4'hc; 6'b010011 : so=4'h0; 6'b010101 : so=4'h1; 6'b010111 : so=4'ha; 6'b011001 : so=4'h6; 6'b011011 : so=4'h9; 6'b011101 : so=4'hb; 6'b011111 : so=4'h5; 6'b100000 : so=4'h0; 6'b100010 : so=4'he; 6'b100100 : so=4'h7; 6'b100110 : so=4'hb; 6'b101000 : so=4'ha; 6'b101010 : so=4'h4; 6'b101100 : so=4'hd; 6'b101110 : so=4'h1; 6'b110000 : so=4'h5; 6'b110010 : so=4'h8; 6'b110100 : so=4'hc; 6'b110110 : so=4'h6; 6'b111000 : so=4'h9; 6'b111010 : so=4'h3; 6'b111100 : so=4'h2; 6'b111110 : so=4'hf; 6'b100001 : so=4'hd; 6'b100011 : so=4'h8; 6'b100101 : so=4'ha; 6'b100111 : so=4'h1; 6'b101001 : so=4'h3; 6'b101011 : so=4'hf; 6'b101101 : so=4'h4; 6'b101111 : so=4'h2; 6'b110001 : so=4'hb; 6'b110011 : so=4'h6; 6'b110101 : so=4'h7; 6'b110111 : so=4'hc; 6'b111001 : so=4'h0; 6'b111011 : so=4'h5; 6'b111101 : so=4'he; default so=4'h9; endcase endmodule module s3(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'ha; 6'b000010 : so=4'h0; 6'b000100 : so=4'h9; 6'b000110 : so=4'he; 6'b001000 : so=4'h6; 6'b001010 : so=4'h3; 6'b001100 : so=4'hf; 6'b001110 : so=4'h5; 6'b010000 : so=4'h1; 6'b010010 : so=4'hd; 6'b010100 : so=4'hc; 6'b010110 : so=4'h7; 6'b011000 : so=4'hb; 6'b011010 : so=4'h4; 6'b011100 : so=4'h2; 6'b011110 : so=4'h8; 6'b000001 : so=4'hd; 6'b000011 : so=4'h7; 6'b000101 : so=4'h0; 6'b000111 : so=4'h9; 6'b001001 : so=4'h3; 6'b001011 : so=4'h4; 6'b001101 : so=4'h6; 6'b001111 : so=4'ha; 6'b010001 : so=4'h2; 6'b010011 : so=4'h8; 6'b010101 : so=4'h5; 6'b010111 : so=4'he; 6'b011001 : so=4'hc; 6'b011011 : so=4'hb; 6'b011101 : so=4'hf; 6'b011111 : so=4'h1; 6'b100000 : so=4'hd; 6'b100010 : so=4'h6; 6'b100100 : so=4'h4; 6'b100110 : so=4'h9; 6'b101000 : so=4'h8; 6'b101010 : so=4'hf; 6'b101100 : so=4'h3; 6'b101110 : so=4'h0; 6'b110000 : so=4'hb; 6'b110010 : so=4'h1; 6'b110100 : so=4'h2; 6'b110110 : so=4'hc; 6'b111000 : so=4'h5; 6'b111010 : so=4'ha; 6'b111100 : so=4'he; 6'b111110 : so=4'h7; 6'b100001 : so=4'h1; 6'b100011 : so=4'ha; 6'b100101 : so=4'hd; 6'b100111 : so=4'h0; 6'b101001 : so=4'h6; 6'b101011 : so=4'h9; 6'b101101 : so=4'h8; 6'b101111 : so=4'h7; 6'b110001 : so=4'h4; 6'b110011 : so=4'hf; 6'b110101 : so=4'he; 6'b110111 : so=4'h3; 6'b111001 : so=4'hb; 6'b111011 : so=4'h5; 6'b111101 : so=4'h2; default so=4'hc; endcase endmodule module s4(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'h7; 6'b000010 : so=4'hd; 6'b000100 : so=4'he; 6'b000110 : so=4'h3; 6'b001000 : so=4'h0; 6'b001010 : so=4'h6; 6'b001100 : so=4'h9; 6'b001110 : so=4'ha; 6'b010000 : so=4'h1; 6'b010010 : so=4'h2; 6'b010100 : so=4'h8; 6'b010110 : so=4'h5; 6'b011000 : so=4'hb; 6'b011010 : so=4'hc; 6'b011100 : so=4'h4; 6'b011110 : so=4'hf; 6'b000001 : so=4'hd; 6'b000011 : so=4'h8; 6'b000101 : so=4'hb; 6'b000111 : so=4'h5; 6'b001001 : so=4'h6; 6'b001011 : so=4'hf; 6'b001101 : so=4'h0; 6'b001111 : so=4'h3; 6'b010001 : so=4'h4; 6'b010011 : so=4'h7; 6'b010101 : so=4'h2; 6'b010111 : so=4'hc; 6'b011001 : so=4'h1; 6'b011011 : so=4'ha; 6'b011101 : so=4'he; 6'b011111 : so=4'h9; 6'b100000 : so=4'ha; 6'b100010 : so=4'h6; 6'b100100 : so=4'h9; 6'b100110 : so=4'h0; 6'b101000 : so=4'hc; 6'b101010 : so=4'hb; 6'b101100 : so=4'h7; 6'b101110 : so=4'hd; 6'b110000 : so=4'hf; 6'b110010 : so=4'h1; 6'b110100 : so=4'h3; 6'b110110 : so=4'he; 6'b111000 : so=4'h5; 6'b111010 : so=4'h2; 6'b111100 : so=4'h8; 6'b111110 : so=4'h4; 6'b100001 : so=4'h3; 6'b100011 : so=4'hf; 6'b100101 : so=4'h0; 6'b100111 : so=4'h6; 6'b101001 : so=4'ha; 6'b101011 : so=4'h1; 6'b101101 : so=4'hd; 6'b101111 : so=4'h8; 6'b110001 : so=4'h9; 6'b110011 : so=4'h4; 6'b110101 : so=4'h5; 6'b110111 : so=4'hb; 6'b111001 : so=4'hc; 6'b111011 : so=4'h7; 6'b111101 : so=4'h2; default so=4'he; endcase endmodule module s5(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'h2; 6'b000010 : so=4'hc; 6'b000100 : so=4'h4; 6'b000110 : so=4'h1; 6'b001000 : so=4'h7; 6'b001010 : so=4'ha; 6'b001100 : so=4'hb; 6'b001110 : so=4'h6; 6'b010000 : so=4'h8; 6'b010010 : so=4'h5; 6'b010100 : so=4'h3; 6'b010110 : so=4'hf; 6'b011000 : so=4'hd; 6'b011010 : so=4'h0; 6'b011100 : so=4'he; 6'b011110 : so=4'h9; 6'b000001 : so=4'he; 6'b000011 : so=4'hb; 6'b000101 : so=4'h2; 6'b000111 : so=4'hc; 6'b001001 : so=4'h4; 6'b001011 : so=4'h7; 6'b001101 : so=4'hd; 6'b001111 : so=4'h1; 6'b010001 : so=4'h5; 6'b010011 : so=4'h0; 6'b010101 : so=4'hf; 6'b010111 : so=4'ha; 6'b011001 : so=4'h3; 6'b011011 : so=4'h9; 6'b011101 : so=4'h8; 6'b011111 : so=4'h6; 6'b100000 : so=4'h4; 6'b100010 : so=4'h2; 6'b100100 : so=4'h1; 6'b100110 : so=4'hb; 6'b101000 : so=4'ha; 6'b101010 : so=4'hd; 6'b101100 : so=4'h7; 6'b101110 : so=4'h8; 6'b110000 : so=4'hf; 6'b110010 : so=4'h9; 6'b110100 : so=4'hc; 6'b110110 : so=4'h5; 6'b111000 : so=4'h6; 6'b111010 : so=4'h3; 6'b111100 : so=4'h0; 6'b111110 : so=4'he; 6'b100001 : so=4'hb; 6'b100011 : so=4'h8; 6'b100101 : so=4'hc; 6'b100111 : so=4'h7; 6'b101001 : so=4'h1; 6'b101011 : so=4'he; 6'b101101 : so=4'h2; 6'b101111 : so=4'hd; 6'b110001 : so=4'h6; 6'b110011 : so=4'hf; 6'b110101 : so=4'h0; 6'b110111 : so=4'h9; 6'b111001 : so=4'ha; 6'b111011 : so=4'h4; 6'b111101 : so=4'h5; default so=4'h3; endcase endmodule module s6(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'hc; 6'b000010 : so=4'h1; 6'b000100 : so=4'ha; 6'b000110 : so=4'hf; 6'b001000 : so=4'h9; 6'b001010 : so=4'h2; 6'b001100 : so=4'h6; 6'b001110 : so=4'h8; 6'b010000 : so=4'h0; 6'b010010 : so=4'hd; 6'b010100 : so=4'h3; 6'b010110 : so=4'h4; 6'b011000 : so=4'he; 6'b011010 : so=4'h7; 6'b011100 : so=4'h5; 6'b011110 : so=4'hb; 6'b000001 : so=4'ha; 6'b000011 : so=4'hf; 6'b000101 : so=4'h4; 6'b000111 : so=4'h2; 6'b001001 : so=4'h7; 6'b001011 : so=4'hc; 6'b001101 : so=4'h9; 6'b001111 : so=4'h5; 6'b010001 : so=4'h6; 6'b010011 : so=4'h1; 6'b010101 : so=4'hd; 6'b010111 : so=4'he; 6'b011001 : so=4'h0; 6'b011011 : so=4'hb; 6'b011101 : so=4'h3; 6'b011111 : so=4'h8; 6'b100000 : so=4'h9; 6'b100010 : so=4'he; 6'b100100 : so=4'hf; 6'b100110 : so=4'h5; 6'b101000 : so=4'h2; 6'b101010 : so=4'h8; 6'b101100 : so=4'hc; 6'b101110 : so=4'h3; 6'b110000 : so=4'h7; 6'b110010 : so=4'h0; 6'b110100 : so=4'h4; 6'b110110 : so=4'ha; 6'b111000 : so=4'h1; 6'b111010 : so=4'hd; 6'b111100 : so=4'hb; 6'b111110 : so=4'h6; 6'b100001 : so=4'h4; 6'b100011 : so=4'h3; 6'b100101 : so=4'h2; 6'b100111 : so=4'hc; 6'b101001 : so=4'h9; 6'b101011 : so=4'h5; 6'b101101 : so=4'hf; 6'b101111 : so=4'ha; 6'b110001 : so=4'hb; 6'b110011 : so=4'he; 6'b110101 : so=4'h1; 6'b110111 : so=4'h7; 6'b111001 : so=4'h6; 6'b111011 : so=4'h0; 6'b111101 : so=4'h8; default so=4'hd; endcase endmodule module s7(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'h4; 6'b000010 : so=4'hb; 6'b000100 : so=4'h2; 6'b000110 : so=4'he; 6'b001000 : so=4'hf; 6'b001010 : so=4'h0; 6'b001100 : so=4'h8; 6'b001110 : so=4'hd; 6'b010000 : so=4'h3; 6'b010010 : so=4'hc; 6'b010100 : so=4'h9; 6'b010110 : so=4'h7; 6'b011000 : so=4'h5; 6'b011010 : so=4'ha; 6'b011100 : so=4'h6; 6'b011110 : so=4'h1; 6'b000001 : so=4'hd; 6'b000011 : so=4'h0; 6'b000101 : so=4'hb; 6'b000111 : so=4'h7; 6'b001001 : so=4'h4; 6'b001011 : so=4'h9; 6'b001101 : so=4'h1; 6'b001111 : so=4'ha; 6'b010001 : so=4'he; 6'b010011 : so=4'h3; 6'b010101 : so=4'h5; 6'b010111 : so=4'hc; 6'b011001 : so=4'h2; 6'b011011 : so=4'hf; 6'b011101 : so=4'h8; 6'b011111 : so=4'h6; 6'b100000 : so=4'h1; 6'b100010 : so=4'h4; 6'b100100 : so=4'hb; 6'b100110 : so=4'hd; 6'b101000 : so=4'hc; 6'b101010 : so=4'h3; 6'b101100 : so=4'h7; 6'b101110 : so=4'he; 6'b110000 : so=4'ha; 6'b110010 : so=4'hf; 6'b110100 : so=4'h6; 6'b110110 : so=4'h8; 6'b111000 : so=4'h0; 6'b111010 : so=4'h5; 6'b111100 : so=4'h9; 6'b111110 : so=4'h2; 6'b100001 : so=4'h6; 6'b100011 : so=4'hb; 6'b100101 : so=4'hd; 6'b100111 : so=4'h8; 6'b101001 : so=4'h1; 6'b101011 : so=4'h4; 6'b101101 : so=4'ha; 6'b101111 : so=4'h7; 6'b110001 : so=4'h9; 6'b110011 : so=4'h5; 6'b110101 : so=4'h0; 6'b110111 : so=4'hf; 6'b111001 : so=4'he; 6'b111011 : so=4'h2; 6'b111101 : so=4'h3; default so=4'hc; endcase endmodule module s8(clk, b, so); input clk; input [1:6] b; output [1:4] so; reg [1:4] so; always @(posedge clk) casex(b) 6'b000000 : so=4'hd; 6'b000010 : so=4'h2; 6'b000100 : so=4'h8; 6'b000110 : so=4'h4; 6'b001000 : so=4'h6; 6'b001010 : so=4'hf; 6'b001100 : so=4'hb; 6'b001110 : so=4'h1; 6'b010000 : so=4'ha; 6'b010010 : so=4'h9; 6'b010100 : so=4'h3; 6'b010110 : so=4'he; 6'b011000 : so=4'h5; 6'b011010 : so=4'h0; 6'b011100 : so=4'hc; 6'b011110 : so=4'h7; 6'b000001 : so=4'h1; 6'b000011 : so=4'hf; 6'b000101 : so=4'hd; 6'b000111 : so=4'h8; 6'b001001 : so=4'ha; 6'b001011 : so=4'h3; 6'b001101 : so=4'h7; 6'b001111 : so=4'h4; 6'b010001 : so=4'hc; 6'b010011 : so=4'h5; 6'b010101 : so=4'h6; 6'b010111 : so=4'hb; 6'b011001 : so=4'h0; 6'b011011 : so=4'he; 6'b011101 : so=4'h9; 6'b011111 : so=4'h2; 6'b100000 : so=4'h7; 6'b100010 : so=4'hb; 6'b100100 : so=4'h4; 6'b100110 : so=4'h1; 6'b101000 : so=4'h9; 6'b101010 : so=4'hc; 6'b101100 : so=4'he; 6'b101110 : so=4'h2; 6'b110000 : so=4'h0; 6'b110010 : so=4'h6; 6'b110100 : so=4'ha; 6'b110110 : so=4'hd; 6'b111000 : so=4'hf; 6'b111010 : so=4'h3; 6'b111100 : so=4'h5; 6'b111110 : so=4'h8; 6'b100001 : so=4'h2; 6'b100011 : so=4'h1; 6'b100101 : so=4'he; 6'b100111 : so=4'h7; 6'b101001 : so=4'h4; 6'b101011 : so=4'ha; 6'b101101 : so=4'h8; 6'b101111 : so=4'hd; 6'b110001 : so=4'hf; 6'b110011 : so=4'hc; 6'b110101 : so=4'h9; 6'b110111 : so=4'h0; 6'b111001 : so=4'h3; 6'b111011 : so=4'h5; 6'b111101 : so=4'h6; default so=4'hb; endcase endmodule module ip(pt, l0x, r0x); input [1:64] pt; output [1:32] l0x, r0x; assign l0x[1]=pt[58]; assign l0x[2]=pt[50]; assign l0x[3]=pt[42]; assign l0x[4]=pt[34]; assign l0x[5]=pt[26]; assign l0x[6]=pt[18]; assign l0x[7]=pt[10]; assign l0x[8]=pt[2]; assign l0x[9]=pt[60]; assign l0x[10]=pt[52]; assign l0x[11]=pt[44]; assign l0x[12]=pt[36]; assign l0x[13]=pt[28]; assign l0x[14]=pt[20]; assign l0x[15]=pt[12]; assign l0x[16]=pt[4]; assign l0x[17]=pt[62]; assign l0x[18]=pt[54]; assign l0x[19]=pt[46]; assign l0x[20]=pt[38]; assign l0x[21]=pt[30]; assign l0x[22]=pt[22]; assign l0x[23]=pt[14]; assign l0x[24]=pt[6]; assign l0x[25]=pt[64]; assign l0x[26]=pt[56]; assign l0x[27]=pt[48]; assign l0x[28]=pt[40]; assign l0x[29]=pt[32]; assign l0x[30]=pt[24]; assign l0x[31]=pt[16]; assign l0x[32]=pt[8]; assign r0x[1]=pt[57]; assign r0x[2]=pt[49]; assign r0x[3]=pt[41]; assign r0x[4]=pt[33]; assign r0x[5]=pt[25]; assign r0x[6]=pt[17]; assign r0x[7]=pt[9]; assign r0x[8]=pt[1]; assign r0x[9]=pt[59]; assign r0x[10]=pt[51]; assign r0x[11]=pt[43]; assign r0x[12]=pt[35]; assign r0x[13]=pt[27]; assign r0x[14]=pt[19]; assign r0x[15]=pt[11]; assign r0x[16]=pt[3]; assign r0x[17]=pt[61]; assign r0x[18]=pt[53]; assign r0x[19]=pt[45]; assign r0x[20]=pt[37]; assign r0x[21]=pt[29]; assign r0x[22]=pt[21]; assign r0x[23]=pt[13]; assign r0x[24]=pt[5]; assign r0x[25]=pt[63]; assign r0x[26]=pt[55]; assign r0x[27]=pt[47]; assign r0x[28]=pt[39]; assign r0x[29]=pt[31]; assign r0x[30]=pt[23]; assign r0x[31]=pt[15]; assign r0x[32]=pt[7]; endmodule module xp(ri, e); input [1:32] ri; output [1:48] e; assign e[1]=ri[32]; assign e[2]=ri[1]; assign e[3]=ri[2]; assign e[4]=ri[3]; assign e[5]=ri[4]; assign e[6]=ri[5]; assign e[7]=ri[4]; assign e[8]=ri[5]; assign e[9]=ri[6]; assign e[10]=ri[7]; assign e[11]=ri[8]; assign e[12]=ri[9]; assign e[13]=ri[8]; assign e[14]=ri[9]; assign e[15]=ri[10]; assign e[16]=ri[11]; assign e[17]=ri[12]; assign e[18]=ri[13]; assign e[19]=ri[12]; assign e[20]=ri[13]; assign e[21]=ri[14]; assign e[22]=ri[15]; assign e[23]=ri[16]; assign e[24]=ri[17]; assign e[25]=ri[16]; assign e[26]=ri[17]; assign e[27]=ri[18]; assign e[28]=ri[19]; assign e[29]=ri[20]; assign e[30]=ri[21]; assign e[31]=ri[20]; assign e[32]=ri[21]; assign e[33]=ri[22]; assign e[34]=ri[23]; assign e[35]=ri[24]; assign e[36]=ri[25]; assign e[37]=ri[24]; assign e[38]=ri[25]; assign e[39]=ri[26]; assign e[40]=ri[27]; assign e[41]=ri[28]; assign e[42]=ri[29]; assign e[43]=ri[28]; assign e[44]=ri[29]; assign e[45]=ri[30]; assign e[46]=ri[31]; assign e[47]=ri[32]; assign e[48]=ri[1]; endmodule module desxor1(e,b1x,b2x,b3x,b4x,b5x,b6x,b7x,b8x,k); input [1:48] e; output [1:6] b1x,b2x,b3x,b4x,b5x,b6x,b7x,b8x; input [1:48] k; wire [1:48] XX; assign XX = k ^ e; assign b1x = XX[1:6]; assign b2x = XX[7:12]; assign b3x = XX[13:18]; assign b4x = XX[19:24]; assign b5x = XX[25:30]; assign b6x = XX[31:36]; assign b7x = XX[37:42]; assign b8x = XX[43:48]; endmodule module pp(so1x,so2x,so3x,so4x,so5x,so6x,so7x,so8x,ppo); input [1:4] so1x,so2x,so3x,so4x,so5x,so6x,so7x,so8x; output [1:32] ppo; wire [1:32] XX; assign XX[1:4]=so1x; assign XX[5:8]=so2x; assign XX[9:12]=so3x; assign XX[13:16]=so4x; assign XX[17:20]=so5x; assign XX[21:24]=so6x; assign XX[25:28]=so7x; assign XX[29:32]=so8x; assign ppo[1]=XX[16]; assign ppo[2]=XX[7]; assign ppo[3]=XX[20]; assign ppo[4]=XX[21]; assign ppo[5]=XX[29]; assign ppo[6]=XX[12]; assign ppo[7]=XX[28]; assign ppo[8]=XX[17]; assign ppo[9]=XX[1]; assign ppo[10]=XX[15]; assign ppo[11]=XX[23]; assign ppo[12]=XX[26]; assign ppo[13]=XX[5]; assign ppo[14]=XX[18]; assign ppo[15]=XX[31]; assign ppo[16]=XX[10]; assign ppo[17]=XX[2]; assign ppo[18]=XX[8]; assign ppo[19]=XX[24]; assign ppo[20]=XX[14]; assign ppo[21]=XX[32]; assign ppo[22]=XX[27]; assign ppo[23]=XX[3]; assign ppo[24]=XX[9]; assign ppo[25]=XX[19]; assign ppo[26]=XX[13]; assign ppo[27]=XX[30]; assign ppo[28]=XX[6]; assign ppo[29]=XX[22]; assign ppo[30]=XX[11]; assign ppo[31]=XX[4]; assign ppo[32]=XX[25]; endmodule module desxor2(d,l,q); input [1:32] d,l; output [1:32] q; assign q = d ^ l; endmodule module roundfunc(clk, li, ri, lo, ro, k); input clk; input [1:32] li, ri; input [1:48] k; output [1:32] lo, ro; wire [1:48] e; wire [1:6] b1x,b2x,b3x,b4x,b5x,b6x,b7x,b8x; wire [1:4] so1x,so2x,so3x,so4x,so5x,so6x,so7x,so8x; wire [1:32] ppo; xp xp(ri, e); desxor1 desxor1(e, b1x, b2x, b3x, b4x, b5x, b6x, b7x, b8x, k); s1 s1(clk, b1x, so1x); s2 s2(clk, b2x, so2x); s3 s3(clk, b3x, so3x); s4 s4(clk, b4x, so4x); s5 s5(clk, b5x, so5x); s6 s6(clk, b6x, so6x); s7 s7(clk, b7x, so7x); s8 s8(clk, b8x, so8x); pp pp(so1x,so2x,so3x,so4x,so5x,so6x,so7x,so8x, ppo); desxor2 desxor2(ppo, li, ro); assign lo=ri; endmodule module fp(l,r,ct); input [1:32] l,r; output [1:64] ct; assign ct[1]=r[8]; assign ct[2]=l[8]; assign ct[3]=r[16]; assign ct[4]=l[16]; assign ct[5]=r[24]; assign ct[6]=l[24]; assign ct[7]=r[32]; assign ct[8]=l[32]; assign ct[9]=r[7]; assign ct[10]=l[7]; assign ct[11]=r[15]; assign ct[12]=l[15]; assign ct[13]=r[23]; assign ct[14]=l[23]; assign ct[15]=r[31]; assign ct[16]=l[31]; assign ct[17]=r[6]; assign ct[18]=l[6]; assign ct[19]=r[14]; assign ct[20]=l[14]; assign ct[21]=r[22]; assign ct[22]=l[22]; assign ct[23]=r[30]; assign ct[24]=l[30]; assign ct[25]=r[5]; assign ct[26]=l[5]; assign ct[27]=r[13]; assign ct[28]=l[13]; assign ct[29]=r[21]; assign ct[30]=l[21]; assign ct[31]=r[29]; assign ct[32]=l[29]; assign ct[33]=r[4]; assign ct[34]=l[4]; assign ct[35]=r[12]; assign ct[36]=l[12]; assign ct[37]=r[20]; assign ct[38]=l[20]; assign ct[39]=r[28]; assign ct[40]=l[28]; assign ct[41]=r[3]; assign ct[42]=l[3]; assign ct[43]=r[11]; assign ct[44]=l[11]; assign ct[45]=r[19]; assign ct[46]=l[19]; assign ct[47]=r[27]; assign ct[48]=l[27]; assign ct[49]=r[2]; assign ct[50]=l[2]; assign ct[51]=r[10]; assign ct[52]=l[10]; assign ct[53]=r[18]; assign ct[54]=l[18]; assign ct[55]=r[26]; assign ct[56]=l[26]; assign ct[57]=r[1]; assign ct[58]=l[1]; assign ct[59]=r[9]; assign ct[60]=l[9]; assign ct[61]=r[17]; assign ct[62]=l[17]; assign ct[63]=r[25]; assign ct[64]=l[25]; endmodule iverilog-10_1/examples/hello.vl000066400000000000000000000031451265551621300166310ustar00rootroot00000000000000/* * Copyright (c) 1998 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Here we have the canonical "Hello, World" program written in Verilog. * We simply print to the program output a simple message. This example * demonstrates the process of compiling and executing a program with * Icarus Verilog. * * Compile this program with the command: * * iverilog -ohello hello.vl * * After churning for a little while, the program will create the output * file "hello" which is compiled, linked and ready to run. Run this * program like so: * * vvp hello * * and the program will print the message to its output. Easy! For * more on how to make the iverilog command work, see the iverilog * manual page. */ module main(); initial begin $display("Hello, World"); $finish ; end endmodule iverilog-10_1/examples/hello_vpi.c000066400000000000000000000034301265551621300173050ustar00rootroot00000000000000/* * Copyright (c) 2002 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file contains an example VPI module to demonstrate the tools * to create vpi modules. To compile this module, use the iverilog-vpi * command like so: * * iverilog-vpi hello_vpi.c * * The result is the hello_vpi.vpi module. See the hello_vpi.vl * program for example Verilog code to call this module. */ # include static PLI_INT32 my_hello_calltf(char *xx) { vpi_printf("Hello World, from VPI.\n"); return 0; } static void my_hello_register() { s_vpi_systf_data tf_data; tf_data.type = vpiSysTask; tf_data.tfname = "$my_hello"; tf_data.calltf = my_hello_calltf; tf_data.compiletf = 0; tf_data.sizetf = 0; vpi_register_systf(&tf_data); } /* * This is a table of register functions. This table is the external * symbol that the simulator looks for when loading this .vpi module. */ void (*vlog_startup_routines[])() = { my_hello_register, 0 }; iverilog-10_1/examples/hello_vpi.vl000066400000000000000000000032101265551621300175000ustar00rootroot00000000000000/* * Copyright (c) 2002 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Here we have the canonical "Hello, World" program written in Verilog, * with VPI. It uses the hello_vpi.vpi module that is compiled from * the hello_vpi.c program also in this directory. See the * hello_vpi.c for instructions on how to compile it. * * Compile this program with the command: * * iverilog -ohello_vpi hello_vpi.vl * * After churning for a little while, the program will create the output * file "hello" which is compiled, linked and ready to run. Run this * program like so: * * vvp -M. -mhello_vpi hello_vpi * * and the program will print the message to its output. Easy! For * more on how to make the iverilog command work, see the iverilog * manual page. */ module main(); initial begin $my_hello; $finish ; end endmodule iverilog-10_1/examples/outff.v000066400000000000000000000054171265551621300165010ustar00rootroot00000000000000/* * Copyright (c) 1998-1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * IVL should generate an AND gate, and should make an OBUF and two * IBUF objects, along with the PAD objects. * * To compile this for XNF, try a command like this: * * iverilog -txnf -ppart=XC4010XLPQ160 -ooutff.xnf -pncf=outff.ncf outff.v * * That command causes an outff.xnf and outff.ncf file to be created. * Next, make the outff.ngd file with the command: * * xnf2ngd -l xilinxun -u outff.xnf outff.ngo * ngdbuild outff.ngo outff.ngd * * Finally, map the file to fully render it in the target part. The * par command is the step that actually optimizes the design and tries * to meet timing constraints. * * map -o map.ncd outff.ngd * par -w map.ncd outff.ncd * * At this point, you can use the FPGA Editor to edit the outff.ncd * file to see that the AND gate is in a CLB and the IOB for pin 150 * has its flip-flop in use, and that gbuf is a global buffer. */ module main; wire clk, iclk; wire i0, i1; wire out; reg o0; // This simple logic gate get turned into a function unit. // The par program will map this into a CLB F or G unit. and (out, i0, i1); // This creates a global clock buffer. Notice how I attach an // attribute to the named gate to force it to be mapped to the // desired XNF device. This device will not be pulled into the // IOB associated with iclk because of the attribute. buf gbuf(clk, iclk); $attribute(gbuf, "XNF-LCA", "GCLK:O,I"); // This is mapped to a DFF. Since o0 is connected to a PAD, it // is turned into a OUTFF so that it get placed into an IOB. always @(posedge clk) o0 = out; // These attribute commands assign pins to the listed wires. // This can be done to wires and registers, as internally both // are treated as named signals. $attribute(o0, "PAD", "o150"); $attribute(i0, "PAD", "i152"); $attribute(i1, "PAD", "i153"); $attribute(iclk,"PAD", "i154"); endmodule /* main */ iverilog-10_1/examples/pal_reg.v000066400000000000000000000104231265551621300167600ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This example shows how to use Icarus Verilog to generate PLD output. * The design is intended to fit into a 22v10 in a PLCC package, with * pin assignments locked down by design. The command to compile this * into a jedec file is; * * iverilog -tpal -ppart=generic-22v10-plcc -opal_reg.jed pal_reg.v * * The output file name (passed through the -o switch) can be * any file you desire. If the compilation and fitting all succeed, the * output file will be a JEDEC file that you can take to your favorite * PROM programmer to program the part. * * This source demonstrates some important principles of synthesizing * a design for a PLD, including how to specify synchronous logic, and * how to assign signals to pins. The pin assignment in particular is * part specific, and must be right for the fitting to succeed. */ /* * The register module is an 8 bit register that copies the input to * the output registers on the rising edge of the clk input. The * always statement creates a simple d-type flip-flop that is loaded * on the rising edge of the clock. * * The output drivers are controlled by a single active low output * enable. I used bufif0 devices in this example, but the exact same * thing can be achieved with a continuous assignment like so: * * assign out = oe? 8'hzz : Q; * * Many people prefer the expression form. It is true that it does * seem to express the intent a bit more clearly. */ module register (out, val, clk, oe); output [7:0] out; input [7:0] val; input clk, oe; reg [7:0] Q; wire [7:0] out; bufif0 drv[7:0](out, Q, oe); always @(posedge clk) Q = val; endmodule /* * The module pal is used to attach pin information to all the pins of * the device. We use this to lock down the pin assignments of the * synthesized result. The pin number assignments are for a 22v10 in * a PLCC package. * * Note that this module has no logic in it. It is a convention I use * that I put all the functionality in a separate module (seen above) * and isolate the Icarus Verilog specific $attribute madness into a * top-level module. The advantage of this style is that the entire * module can be `ifdef'ed out when doing simulation and you don't * need to worry that functionality will be affected. */ module pal; wire out7, out6, out5, out4, out3, out2, out1, out0; wire inp7, inp6, inp5, inp4, inp3, inp2, inp1, inp0; wire clk, oe; // The PAD attributes attach the wires to pins of the // device. Output pins are prefixed by a 'o', and input pins by an // 'i'. If not all the available output pins are used, then the // remaining are available for the synthesizer to drop internal // registers or extra logic layers. $attribute(out7, "PAD", "o27"); $attribute(out6, "PAD", "o26"); $attribute(out5, "PAD", "o25"); $attribute(out4, "PAD", "o24"); $attribute(out3, "PAD", "o23"); $attribute(out2, "PAD", "o21"); $attribute(out1, "PAD", "o20"); $attribute(out0, "PAD", "o19"); $attribute(inp7, "PAD", "i10"); $attribute(inp6, "PAD", "i9"); $attribute(inp5, "PAD", "i7"); $attribute(inp4, "PAD", "i6"); $attribute(inp3, "PAD", "i5"); $attribute(inp2, "PAD", "i4"); $attribute(inp1, "PAD", "i3"); $attribute(inp0, "PAD", "i2"); //$attribute(clk, "PAD", "CLK"); $attribute(oe, "PAD", "i13"); register dev({out7, out6, out5, out4, out3, out2, out1, out0}, {inp7, inp6, inp5, inp4, inp3, inp2, inp1, inp0}, clk, oe); endmodule // pal iverilog-10_1/examples/show_vcd.vl000066400000000000000000000075141265551621300173460ustar00rootroot00000000000000/* * Copyright (c) 1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This example program simulates a 16x1 ram, and is used as an * example for using VCD output and waveform viewers. * * Like any other Verilog simulation, compile this program with the * command: * * iverilog show_vcd.vl * * This will generate the show_vcd command in the current directory. * When you run the command, you will see the output from all the * calls to $display, but also there will be a dump file ``show_vcd.vcd''. * The name of this file is set by the statement: * * $dumpfile("show_vcd.vcd"); * * in the main module. The output file uses the standard VCD file format * so can be viewed using off-the-shelf waveform viewers. The remaining * steps describe how to use GTKWave to view the file. If you are using * a different viewer, see the documentation for that tool. * * To view the output generated by running show_vcd, start the GTKWave * viewer with the command: * * gtkwave show_vcd.vcd * * The GTKWave program will display its main window, and show in a small * status box (upper left corner) that it succeeded in loading the dump * file. However, there are no waveforms displayed yet. Select signals to * add to the waveform display using the menu selection: * * "Search --> Signal Search Tree" * * This will bring up a dialog box that shows in directory tree format * the signals of the program. Select the signals you wish to view, and * click one of the buttons on the bottom of the dialog box to display * the selected signals in the waveform window. Click "Exit" on the box * to get rid of it. * * The magic that makes all this work is contained in the $dumpfile and * $dumpvars system tasks. The $dumpfile task tells the simulation where * to write the VCD output. This task must be called once before the * $dumpvars task is called. * * The $dumpvars task tells the simulation what variables to write to * the VCD output. The first parameter is how far to descend while * scanning a scope, and the remaining parameters are signals or scope * names to include in the dump. If a scope name is given, all the * signals within the scope are dumped. If a wire or register name is * given, that signal is included. */ module ram16x1 (q, d, a, we, wclk); output q; input d; input [3:0] a; input we; input wclk; reg mem[15:0]; assign q = mem[a]; always @(posedge wclk) if (we) mem[a] = d; endmodule /* ram16x1 */ module main; wire q; reg d; reg [3:0] a; reg we, wclk; ram16x1 r1 (q, d, a, we, wclk); initial begin $dumpfile("show_vcd.vcd"); $dumpvars(1, main.r1); wclk = 0; we = 1; for (a = 0 ; a < 4'hf ; a = a + 1) begin d = a[0]; #1 wclk = 1; #1 wclk = 0; $display("r1[%x] == %b", a, q); end for (a = 0 ; a < 4'hf ; a = a + 1) #1 if (q !== a[0]) begin $display("FAILED -- mem[%h] !== %b", a, a[0]); $finish; end $display("PASSED"); end endmodule /* main */ iverilog-10_1/examples/sqrt-virtex.v000066400000000000000000000274131265551621300176660ustar00rootroot00000000000000/* * Copyright (c) 2002 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id: sqrt-virtex.v,v 1.5 2007/03/22 16:08:18 steve Exp $" */ /* * This module is a synthesizable square-root function. It is also a * detailed example of how to target Xilinx Virtex parts using * Icarus Verilog. In fact, for no particular reason other than to * be excessively specific, I will step through the process of * generating a design for a Spartan-II XC2S15-VQ100, and also how to * generate a generic library part for larger Virtex designs. * * In addition to Icarus Verilog, you will need implementation * software from Xilinx. As of this writing, this example was tested * with Foundation 4.2i, but it should work the same with ISE and * WebPACK software. * * This example source contains all the Verilog needed to do * everything described below. We use conditional compilation to * select the bits of Verilog that are needed to perform each specific * task. * * SIMULATE THE DESIGN * * This source file includes a simulation test bench. To compile the * program to include this test bench, use the command line: * * iverilog -DSIMULATE=1 -oa.out sqrt-virtex.v * * This generates the file "a.out" that can then be executed with the * command: * * vvp a.out * * This causes the simulation to run a long set of example sqrt * calculations. Each result is checked by the test bench to assure * that the result is valid. When it is done, the program prints * "PASSED" and finishes the simulation. * * When you take a close look at the "main" module below, you will see * that it uses Verilog constructs that are not synthesizable. This * is fine, as we will never try to synthesize it. * * LIBRARY PARTS * * One can use the sqrt32 module to generate an EDIF file suitable for * use as a library part. This part can be imported to the Xilinx * schematic editor, then placed like any other pre-existing * macro. One can also pass the generated EDIF as a precompiled macro * that other designers may use as they see fit. * * To make an EDIF file from the sqrt32 module, execute the command: * * iverilog -osqrt32.edf -tfpga -parch=virtex sqrt-virtex.v * * The -parch=virtex tells the code generator to generate code for the * virtex architecture family (we don't yet care what specific part) * and the -osqrt32.edf places the output into the file * sqrt32.edf. * * Without any preprocessor directives, the only module is the sqrt32 * module, so sqrt32 is compiled as the root. The ports of the module * are automatically made into ports of the sqrt32.edf netlist, and * the contents of the sqrt32 module are connected appropriately. * * COMPLETE CHIP DESIGNS * * To make a complete chip design, there are other bits that need to * be accounted for. Signals must be assigned to pins, and some * special devices may need to be created. We also want to write into * the EDIF file complete part information so that the implementation * tools know how to route the complete design. The command to compile * for our target part is: * * iverilog -ochip.edf -tfpga \ * -parch=virtex -ppart=XC2S15-VQ100 \ * -DMAKE_CHIP=1 sqrt-virtex.v * * This command uses the "chip" module as the root. This module in * turn has ports that are destined to be the pins of the completed * part. The -ppart= option gives complete part information, that is * in turn written into the EDIF file. This saves us the drudgery of * repeating that part number for later commands. * * The next steps involve Xilinx software, and to talk to Xilinx * software, the netlist must be in the form of an "ngd" file, a * binary netlist format. The command: * * ngdbuild chip.edf chip.ngd * * does the trick. The input to ngdbuild is the chip.edf file created * by Icarus Verilog, and the output is the chip.ngd file that the * implementation tools may read. From this point, it is best to refer * to Xilinx documentation for the software you are using, but the * quick summary is: * * map -o map.ncd chip.ngd * par -w map.ncd chip.ncd * * The result of this sequence of commands is the chip.ncd file that * is ready to be viewed by FPGA Edit, or converted to a bit stream, * or whatever. * * POST MAP SIMULATION * * Warm fuzzies are good, and retesting your design after the part * is mapped by the Xilinx backend tools is a cheap source of fuzzies. * The command to make a Verilog file out of the mapped design is: * * ngd2ver chip.ngd chip_root.v * * This command creates from the chip.ngd the file "chip_root.v" that * contains Verilog code that simulates the mapped design. This output * Verilog has the single root module "chip_root", which came from the * name of the root module when we were making the EDIF file in the * first place. The module has ports named just line the ports of the * chip_root module below. * * The generated Verilog uses the library in the directory * $(XILINX)/verilog/src/simprims. This directory comes with the ISE * WebPACK installation that you are using. Icarus Verilog is able to * simulate using that library. * * To compile a post-map simulation of the chip_root.v, use the * command: * * iverilog -DSIMULATE -DPOST_MAP -ob.out \ * -y $(XILINX)/verilog/src/simprims \ * sqrt-virtex.v chip_root.v \ * $(XILINX)/verilog/src/glbl.v * * This command line generates b.out from the source files * sqrt-virtex.v and chip_root.v (the latter from ngd2ver) * and the "-y " flag specifies the library directory that will * be needed. The glbl.v source file is also included to provide the * GSR and related signals. * * The POST_MAP compiler directive causes the GSR manipulations * included in the test bench to be compiled in, to simulate the chip * startup. Other than that, the test bench runs the post-map design * the same way the pre-synthesis design works. * * Run this design with the command: * * vvp b.out * * And there you go. */ `ifndef POST_MAP /* * This module approximates the square root of an unsigned 32bit * number. The algorithm works by doing a bit-wise binary search. * Starting from the most significant bit, the accumulated value * tries to put a 1 in the bit position. If that makes the square * too big for the input, the bit is left zero, otherwise it is set * in the result. This continues for each bit, decreasing in * significance, until all the bits are calculated or all the * remaining bits are zero. * * Since the result is an integer, this function really calculates * value of the expression: * * x = floor(sqrt(y)) * * where sqrt(y) is the exact square root of y and floor(N) is the * largest integer <= N. * * For 32 bit numbers, this will never run more than 16 iterations, * which amounts to 16 clocks. */ module sqrt32(clk, rdy, reset, x, .y(acc)); input clk; output rdy; input reset; input [31:0] x; output [15:0] acc; // acc holds the accumulated result, and acc2 is the accumulated // square of the accumulated result. reg [15:0] acc; reg [31:0] acc2; // Keep track of which bit I'm working on. reg [4:0] bitl; wire [15:0] bit = 1 << bitl; wire [31:0] bit2 = 1 << (bitl << 1); // The output is ready when the bitl counter underflows. wire rdy = bitl[4]; // guess holds the potential next values for acc, and guess2 holds // the square of that guess. The guess2 calculation is a little bit // subtle. The idea is that: // // guess2 = (acc + bit) * (acc + bit) // = (acc * acc) + 2*acc*bit + bit*bit // = acc2 + 2*acc*bit + bit2 // = acc2 + 2 * (acc< ((y + 1)*(y + 1))) begin $display("ERROR: y is too small"); $finish; end end $display("PASSED"); $finish; end endmodule // main `endif `ifdef MAKE_CHIP /* * This module represents the chip packaging that we intend to * generate. We bind pins here, and route the clock to the global * clock buffer. */ module chip_root(clk, rdy, reset, x, y); input clk; output rdy; input reset; input [31:0] x; output [15:0] y; wire clk_int; (* cellref="BUFG:O,I" *) buf gbuf (clk_int, clk); sqrt32 dut(.clk(clk_int), .reset(reset), .rdy(rdy), .x(x), .y(y)); /* Assign the clk to GCLK0, which is on pin P39. */ $attribute(clk, "PAD", "P39"); // We don't care where the remaining pins go, so set the pin number // to 0. This tells the implementation tools that we want a PAD, // but we don't care which. Also note the use of a comma (,) // separated list to assign pins to the bits of a vector. $attribute(rdy, "PAD", "0"); $attribute(reset, "PAD", "0"); $attribute(x, "PAD", "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"); $attribute(y, "PAD", "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"); endmodule // chip_root `endif iverilog-10_1/examples/sqrt.vl000066400000000000000000000076651265551621300165320ustar00rootroot00000000000000/* * Copyright (c) 1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id: sqrt.vl,v 1.5 2007/03/22 16:08:18 steve Exp $" */ /* * This example shows that Icarus Verilog can run non-trivial * programs, too. This uses a variety of Verilog language features * to implement the module of a square-root device. The program * uses IEEE1364-1995 language features and should work correctly * on any Verilog compiler. * * Run the file with Icarus Verilog under UNIX using the command: * * % iverilog -osqrt sqrt.v * % ./sqrt */ /* * This module approximates the square root of an unsigned 32bit * number. The algorithm works by doing a bit-wise binary search. * Starting from the most significant bit, the accumulated value * tries to put a 1 in the bit position. If that makes the square * to big for the input, the bit is left zero, otherwise it is set * in the result. This continues for each bit, decreasing in * significance, until all the bits are calculated or all the * remaining bits are zero. * * Since the result is an integer, this function really calculates * value of the expression: * * x = floor(sqrt(y)) * * where sqrt(y) is the exact square root of y and floor(N) is the * largest integer <= N. * * For 32 bit numbers, this will never run more than 16 iterations, * which amounts to 16 clocks. */ module sqrt32(clk, rdy, reset, x, .y(acc)); input clk; output rdy; input reset; input [31:0] x; output [15:0] acc; // acc holds the accumulated result, and acc2 is the accumulated // square of the accumulated result. reg [15:0] acc; reg [31:0] acc2; // Keep track of which bit I'm working on. reg [4:0] bitl; wire [15:0] bit = 1 << bitl; wire [31:0] bit2 = 1 << (bitl << 1); // The output is ready when the bitl counter underflows. wire rdy = bitl[4]; // guess holds the potential next values for acc, and guess2 holds // the square of that guess. The guess2 calculation is a little bit // subtle. The idea is that: // // guess2 = (acc + bit) * (acc + bit) // = (acc * acc) + 2*acc*bit + bit*bit // = acc2 + 2*acc*bit + bit2 // = acc2 + 2 * (acc< %d", value, result); $finish; end initial begin clk = 0; reset = 1; $monitor($time,,"%m.acc = %b", root.acc); #100 value = 63; reset = 0; end endmodule /* main */ iverilog-10_1/examples/xnf_add.vl000066400000000000000000000072361265551621300171360ustar00rootroot00000000000000/* * Copyright (c) 1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This example demonstrates Icarus Verilog's ability to synthesize * efficient adders for Xilinx 4000 series FPGAs. The synthesis and * code generation makes an adder and translates that into XOR gates * and fast carry chain hardware. * * To compile this for XNF, try a command like this: * * iverilog -txnf -ppart=XC4010XLPQ160 -pncf=xnf_add.ncf -oxnf_add.xnf xnf_add.v * * That command causes an xnf_add.xnf and xnf_add.ncf file to be created. * Next, Use Xilinx Alliance or Foundation tools to make the xnf_add.ngd * file with the command: * * xnf2ngd -l xilinxun -u xnf_add.xnf xnf_add.ngo * ngdbuild xnf_add.ngo xnf_add.ngd * * Finally, map the file to fully render it in the target part. The * par command is the step that actually optimizes the design and tries * to meet timing constraints. * * map -o map.ncd xnf_add.ngd * par -w map.ncd xnf_add.ncd * * At this point, you can use the Xilinx FPGA Editor to edit the xnf_add.ncd * file and see the carry chains made up to support the adder. */ module main; wire [3:0] a, b; wire [3:0] out; wire carry; wire a0, a1, a2, a3, b0, b1, b2, b3; wire out0, out1, out2, out3; // This creates the actual adder. Note that we also create a link // to the carry output. The principle adder is 4 bits wide, so two // IOBs are used to to the actual addition. PAR will place them in // order along carry lines. An extra carry cell from below a[0] is // used in FORCE-0 mode to load the bottom carry node. // // The carry signal from the top CY device is used to drive the // main.carry wire shown here. It is managed in this case by using // an ADD-FG-CI CY device for the top pair and using the CLB above // the carry chain, with its CY in EXAMINE-CI mode, to put the carry // out through its G function unit. assign {carry, out} = a + b; // These attribute commands assign pins to the listed wires. // This can be done to wires and registers, as internally both // are treated as named signals. It doesn't work (yet) on vectors, // though, so break out the vectors with scalar assignments. assign a[0] = a0; assign a[1] = a1; assign a[2] = a2; assign a[3] = a3; $attribute(a0, "PAD", "i150"); $attribute(a1, "PAD", "i152"); $attribute(a2, "PAD", "i153"); $attribute(a3, "PAD", "i154"); assign b[0] = b0; assign b[1] = b1; assign b[2] = b2; assign b[3] = b3; $attribute(b0, "PAD", "i155"); $attribute(b1, "PAD", "i156"); $attribute(b2, "PAD", "i157"); $attribute(b3, "PAD", "i158"); assign out0 = out[0]; assign out1 = out[1]; assign out2 = out[2]; assign out3 = out[3]; $attribute(out0, "PAD", "o71"); $attribute(out1, "PAD", "o72"); $attribute(out2, "PAD", "o73"); $attribute(out3, "PAD", "o74"); $attribute(carry, "PAD", "o75"); endmodule /* main */ iverilog-10_1/examples/xram16x1.v000066400000000000000000000026411265551621300167410ustar00rootroot00000000000000/* * Copyright (c) 1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // This example describes a 16x1 RAM that can be synthesized into // a CLB ram in a Xilinx FPGA. module ram16x1 (q, d, a, we, wclk); output q; input d; input [3:0] a; input we; input wclk; reg mem[15:0]; assign q = mem[a]; always @(posedge wclk) if (we) mem[a] = d; endmodule /* ram16x1 */ module main; wire q; reg d; reg [3:0] a; reg we, wclk; ram16x1 r1 (q, d, a, we, wclk); initial begin $monitor("q = %b", q); d = 0; wclk = 0; a = 5; we = 1; #1 wclk = 1; #1 wclk = 0; end endmodule /* main */ iverilog-10_1/expr_synth.cc000066400000000000000000001324361265551621300160650ustar00rootroot00000000000000/* * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include # include # include "netlist.h" # include "netvector.h" # include "netmisc.h" # include "ivl_assert.h" static NetNet* convert_to_real_const(Design*des, NetScope*scope, NetEConst*expr) { verireal vrl(expr->value().as_double()); NetECReal rlval(vrl); NetNet* sig = rlval.synthesize(des, scope, 0); return sig; } /* Note that lsig, rsig and real_args are references. */ static bool process_binary_args(Design*des, NetScope*scope, NetExpr*root, NetExpr*left, NetExpr*right, NetNet*&lsig, NetNet*&rsig, bool&real_args) { if (left->expr_type() == IVL_VT_REAL || right->expr_type() == IVL_VT_REAL) { real_args = true; /* Convert the arguments to real. Handle the special cases of constants, which can be converted more directly. */ if (left->expr_type() == IVL_VT_REAL) { lsig = left->synthesize(des, scope, root); } else if (NetEConst*tmpc = dynamic_cast (left)) { lsig = convert_to_real_const(des, scope, tmpc); } else { NetNet*tmp = left->synthesize(des, scope, root); lsig = cast_to_real(des, scope, tmp); } if (right->expr_type() == IVL_VT_REAL) { rsig = right->synthesize(des, scope, root); } else if (NetEConst*tmpc = dynamic_cast (right)) { rsig = convert_to_real_const(des, scope, tmpc); } else { NetNet*tmp = right->synthesize(des, scope, root); rsig = cast_to_real(des, scope, tmp); } } else { real_args = false; lsig = left->synthesize(des, scope, root); rsig = right->synthesize(des, scope, root); } if (lsig == 0 || rsig == 0) return true; else return false; } NetNet* NetExpr::synthesize(Design*des, NetScope*, NetExpr*) { cerr << get_fileline() << ": internal error: cannot synthesize expression: " << *this << endl; des->errors += 1; return 0; } /* * Make an LPM_ADD_SUB device from addition operators. */ NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope, NetExpr*root) { ivl_assert(*this, (op()=='+') || (op()=='-')); NetNet *lsig=0, *rsig=0; bool real_args=false; if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args)) { return 0; } ivl_assert(*this, expr_width() >= lsig->vector_width()); ivl_assert(*this, expr_width() >= rsig->vector_width()); unsigned width; if (expr_type() == IVL_VT_REAL) { width = 1; if (lsig->data_type() != IVL_VT_REAL) lsig = cast_to_real(des, scope, lsig); if (rsig->data_type() != IVL_VT_REAL) rsig = cast_to_real(des, scope, rsig); } else { lsig = pad_to_width(des, lsig, expr_width(), *this); rsig = pad_to_width(des, rsig, expr_width(), *this); assert(lsig->vector_width() == rsig->vector_width()); width=lsig->vector_width(); } perm_string path = lsig->scope()->local_symbol(); netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); osig_vec->set_signed(has_sign()); NetNet*osig = new NetNet(lsig->scope(), path, NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); perm_string oname = osig->scope()->local_symbol(); NetAddSub *adder = new NetAddSub(lsig->scope(), oname, width); adder->set_line(*this); connect(lsig->pin(0), adder->pin_DataA()); connect(rsig->pin(0), adder->pin_DataB()); connect(osig->pin(0), adder->pin_Result()); des->add_node(adder); switch (op()) { case '+': adder->attribute(perm_string::literal("LPM_Direction"), verinum("ADD")); break; case '-': adder->attribute(perm_string::literal("LPM_Direction"), verinum("SUB")); break; } return osig; } /* * The bitwise logic operators are turned into discrete gates pretty * easily. Synthesize the left and right sub-expressions to get * signals, then just connect a single gate to each bit of the vector * of the expression. */ NetNet* NetEBBits::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet*lsig = left_->synthesize(des, scope, root); NetNet*rsig = right_->synthesize(des, scope, root); if (lsig == 0 || rsig == 0) return 0; /* You cannot do bitwise operations on real values. */ if (lsig->data_type() == IVL_VT_REAL || rsig->data_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " << human_readable_op(op_) << " operator may not have REAL operands." << endl; des->errors += 1; return 0; } unsigned width = expr_width(); if (rsig->vector_width() > width) width = rsig->vector_width(); lsig = pad_to_width(des, lsig, width, *this); rsig = pad_to_width(des, rsig, width, *this); assert(lsig->vector_width() == rsig->vector_width()); netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); perm_string oname = scope->local_symbol(); NetLogic*gate; switch (op()) { case '&': gate = new NetLogic(scope, oname, 3, NetLogic::AND, width, true); break; case 'A': gate = new NetLogic(scope, oname, 3, NetLogic::NAND, width, true); break; case '|': gate = new NetLogic(scope, oname, 3, NetLogic::OR, width, true); break; case '^': gate = new NetLogic(scope, oname, 3, NetLogic::XOR, width, true); break; case 'O': gate = new NetLogic(scope, oname, 3, NetLogic::NOR, width, true); break; case 'X': gate = new NetLogic(scope, oname, 3, NetLogic::XNOR, width, true); break; default: gate = NULL; assert(0); } connect(osig->pin(0), gate->pin(0)); connect(lsig->pin(0), gate->pin(1)); connect(rsig->pin(0), gate->pin(2)); gate->set_line(*this); des->add_node(gate); return osig; } NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args)) { return 0; } if (real_args) { width = 1; } else { width = lsig->vector_width(); if (rsig->vector_width() > width) width = rsig->vector_width(); if (lsig->get_signed()) lsig = pad_to_width_signed(des, lsig, width, *this); else lsig = pad_to_width(des, lsig, width, *this); if (rsig->get_signed()) rsig = pad_to_width_signed(des, rsig, width, *this); else rsig = pad_to_width(des, rsig, width, *this); } netvector_t*osig_vec = new netvector_t(IVL_VT_LOGIC); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); // Test if the comparison is signed. // // Note 1: This is not the same as asking if the result is // signed. In fact, the result will typically be UNsigned. The // decision to make the comparison signed depends on the // operand expressions. // // Note 2: The operand expressions may be signed even if the // sig that comes out of synthesis is unsigned. The $signed() // function marks the expression but doesn't change the // underlying signals. bool signed_compare = left_->has_sign() && right_->has_sign(); if (debug_elaborate) { cerr << get_fileline() << ": debug: Comparison (" << op_ << ")" << " is " << (signed_compare? "signed" : "unsigned") << endl; cerr << get_fileline() << ": : lsig is " << (lsig->get_signed()? "signed" : "unsigned") << " rsig is " << (rsig->get_signed()? "signed" : "unsigned") << endl; } if (op_ == 'E' || op_ == 'N') { NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(), width, op_=='E'?NetCaseCmp::EEQ:NetCaseCmp::NEQ); gate->set_line(*this); connect(gate->pin(0), osig->pin(0)); connect(gate->pin(1), lsig->pin(0)); connect(gate->pin(2), rsig->pin(0)); des->add_node(gate); return osig; } /* Handle the special case of a single bit equality operation. Make an XNOR gate instead of a comparator. */ if ((width == 1) && (op_ == 'e') && !real_args) { NetLogic*gate = new NetLogic(scope, scope->local_symbol(), 3, NetLogic::XNOR, 1, true); gate->set_line(*this); connect(gate->pin(0), osig->pin(0)); connect(gate->pin(1), lsig->pin(0)); connect(gate->pin(2), rsig->pin(0)); des->add_node(gate); return osig; } /* Handle the special case of a single bit inequality operation. This is similar to single bit equality, but uses an XOR instead of an XNOR gate. */ if ((width == 1) && (op_ == 'n') && !real_args) { NetLogic*gate = new NetLogic(scope, scope->local_symbol(), 3, NetLogic::XOR, 1, true); gate->set_line(*this); connect(gate->pin(0), osig->pin(0)); connect(gate->pin(1), lsig->pin(0)); connect(gate->pin(2), rsig->pin(0)); des->add_node(gate); return osig; } NetCompare*dev = new NetCompare(scope, scope->local_symbol(), width); dev->set_line(*this); des->add_node(dev); connect(dev->pin_DataA(), lsig->pin(0)); connect(dev->pin_DataB(), rsig->pin(0)); switch (op_) { case '<': connect(dev->pin_ALB(), osig->pin(0)); dev->set_signed(signed_compare); break; case '>': connect(dev->pin_AGB(), osig->pin(0)); dev->set_signed(signed_compare); break; case 'E': // === ? if (real_args) { cerr << get_fileline() << ": error: Case equality may " "not have real operands." << endl; des->errors += 1; return 0; } case 'e': // == connect(dev->pin_AEB(), osig->pin(0)); break; case 'G': // >= connect(dev->pin_AGEB(), osig->pin(0)); dev->set_signed(signed_compare); break; case 'L': // <= connect(dev->pin_ALEB(), osig->pin(0)); dev->set_signed(signed_compare); break; case 'N': // !== if (real_args) { cerr << get_fileline() << ": error: Case inequality may " "not have real operands." << endl; des->errors += 1; return 0; } case 'n': // != connect(dev->pin_ANEB(), osig->pin(0)); break; default: cerr << get_fileline() << ": internal error: cannot synthesize " "comparison: " << *this << endl; des->errors += 1; return 0; } return osig; } NetNet* NetEBPow::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args)) { return 0; } if (real_args) width = 1; else width = expr_width(); NetPow*powr = new NetPow(scope, scope->local_symbol(), width, lsig->vector_width(), rsig->vector_width()); powr->set_line(*this); des->add_node(powr); powr->set_signed( has_sign() ); connect(powr->pin_DataA(), lsig->pin(0)); connect(powr->pin_DataB(), rsig->pin(0)); netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); osig_vec->set_signed(has_sign()); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); connect(powr->pin_Result(), osig->pin(0)); return osig; } NetNet* NetEBMult::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args)) { return 0; } if (real_args) width = 1; else width = expr_width(); NetMult*mult = new NetMult(scope, scope->local_symbol(), width, lsig->vector_width(), rsig->vector_width()); mult->set_line(*this); des->add_node(mult); mult->set_signed( has_sign() ); connect(mult->pin_DataA(), lsig->pin(0)); connect(mult->pin_DataB(), rsig->pin(0)); netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); osig_vec->set_signed(has_sign()); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); connect(mult->pin_Result(), osig->pin(0)); return osig; } NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args)) { return 0; } if (real_args) width = 1; else width = expr_width(); netvector_t*osig_vec = new netvector_t(lsig->data_type(), width-1, 0); osig_vec->set_signed(has_sign()); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); switch (op()) { case '/': { NetDivide*div = new NetDivide(scope, scope->local_symbol(), width, lsig->vector_width(), rsig->vector_width()); div->set_line(*this); div->set_signed(has_sign()); des->add_node(div); connect(div->pin_DataA(), lsig->pin(0)); connect(div->pin_DataB(), rsig->pin(0)); connect(div->pin_Result(),osig->pin(0)); break; } case '%': { /* Baseline Verilog does not support the % operator with real arguments, but we allow it in our extended form. */ if (real_args && !gn_icarus_misc_flag) { cerr << get_fileline() << ": error: Modulus operator " "may not have REAL operands." << endl; des->errors += 1; return 0; } NetModulo*div = new NetModulo(scope, scope->local_symbol(), width, lsig->vector_width(), rsig->vector_width()); div->set_line(*this); div->set_signed(has_sign()); des->add_node(div); connect(div->pin_DataA(), lsig->pin(0)); connect(div->pin_DataB(), rsig->pin(0)); connect(div->pin_Result(),osig->pin(0)); break; } default: { cerr << get_fileline() << ": internal error: " << "NetEBDiv has unexpected op() code: " << op() << endl; des->errors += 1; delete osig; return 0; } } return osig; } NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet*lsig = left_->synthesize(des, scope, root); NetNet*rsig = right_->synthesize(des, scope, root); if (lsig == 0 || rsig == 0) return 0; /* Any real value should have already been converted to a bit value. */ if (lsig->data_type() == IVL_VT_REAL || rsig->data_type() == IVL_VT_REAL) { cerr << get_fileline() << ": internal error: " << human_readable_op(op_) << " is missing real to bit conversion." << endl; des->errors += 1; return 0; } netvector_t*osig_tmp = new netvector_t(expr_type()); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_tmp); osig->set_line(*this); osig->local_flag(true); NetLogic*olog; perm_string oname = scope->local_symbol(); /* Create the logic OR/AND gate. This has a single bit output, * with single bit inputs for the two operands. */ if (op() == 'o') { olog = new NetLogic(scope, oname, 3, NetLogic::OR, 1, true); } else { assert(op() == 'a'); olog = new NetLogic(scope, oname, 3, NetLogic::AND, 1, true); } olog->set_line(*this); des->add_node(olog); connect(osig->pin(0), olog->pin(0)); /* The left and right operands have already been reduced to a * single bit value, so just connect then to the logic gate. */ assert(lsig->pin_count() == 1); connect(lsig->pin(0), olog->pin(1)); assert(rsig->pin_count() == 1); connect(rsig->pin(0), olog->pin(2)); return osig; } NetNet* NetEBShift::synthesize(Design*des, NetScope*scope, NetExpr*root) { eval_expr(right_); NetNet*lsig = left_->synthesize(des, scope, root); if (lsig == 0) return 0; /* Cannot shift a real values. */ if (lsig->data_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: shift operator (" << human_readable_op(op_) << ") cannot shift a real values." << endl; des->errors += 1; return 0; } const bool right_flag = op_ == 'r' || op_ == 'R'; const bool signed_flag = has_sign() && op_ == 'R'; /* Detect the special case where the shift amount is constant. Evaluate the shift amount, and simply reconnect the left operand to the output, but shifted. */ if (NetEConst*rcon = dynamic_cast(right_)) { verinum shift_v = rcon->value(); long shift = shift_v.as_long(); if (right_flag) shift = 0-shift; if (shift == 0) return lsig; netvector_t*osig_vec = new netvector_t(expr_type(), expr_width()-1,0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); // ushift is the amount of pad created by the shift. unsigned long ushift = shift>=0? shift : -shift; ivl_assert(*this, ushift < osig->vector_width()); // part_width is the bits of the vector that survive the shift. unsigned long part_width = osig->vector_width() - ushift; // Create a part select to reduce the width of the lsig // to the amount left by the shift. NetPartSelect*psel = new NetPartSelect(lsig, shift<0? ushift : 0, part_width, NetPartSelect::VP, signed_flag && right_flag); psel->set_line(*this); des->add_node(psel); netvector_t*psig_vec = new netvector_t(expr_type(), part_width-1, 0); NetNet*psig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, psig_vec); psig->set_line(*this); psig->local_flag(true); connect(psig->pin(0), psel->pin(0)); // Handle the special case of a signed right shift. In // this case, use the NetSignExtend device to pad the // result to the desired width. if (signed_flag && right_flag) { NetSignExtend*pad = new NetSignExtend(scope, scope->local_symbol(), osig->vector_width()); pad->set_line(*this); des->add_node(pad); connect(pad->pin(1), psig->pin(0)); connect(pad->pin(0), osig->pin(0)); return osig; } // Other cases are handled by zero-extending on the // proper end. verinum znum (verinum::V0, ushift, true); NetConst*zcon = new NetConst(scope, scope->local_symbol(), znum); des->add_node(zcon); netvector_t*zsig_vec = new netvector_t(osig->data_type(), znum.len()-1, 0); NetNet*zsig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, zsig_vec); zsig->set_line(*this); zsig->local_flag(true); connect(zcon->pin(0), zsig->pin(0)); NetConcat*ccat = new NetConcat(scope, scope->local_symbol(), osig->vector_width(), 2); ccat->set_line(*this); des->add_node(ccat); connect(ccat->pin(0), osig->pin(0)); if (shift > 0) { // Left shift. connect(ccat->pin(1), zsig->pin(0)); connect(ccat->pin(2), psig->pin(0)); } else { // Right shift connect(ccat->pin(1), psig->pin(0)); connect(ccat->pin(2), zsig->pin(0)); } return osig; } NetNet*rsig = right_->synthesize(des, scope, root); if (rsig == 0) return 0; netvector_t*osig_vec = new netvector_t(expr_type(), expr_width()-1, 0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); NetCLShift*dev = new NetCLShift(scope, scope->local_symbol(), osig->vector_width(), rsig->vector_width(), right_flag, signed_flag); dev->set_line(*this); des->add_node(dev); connect(dev->pin_Result(), osig->pin(0)); assert(lsig->vector_width() == dev->width()); connect(dev->pin_Data(), lsig->pin(0)); connect(dev->pin_Distance(), rsig->pin(0)); return osig; } NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) { /* First, synthesize the operands. */ unsigned num_parms = parms_.size(); NetNet**tmp = new NetNet*[parms_.size()]; bool flag = true; ivl_variable_type_t data_type = IVL_VT_NO_TYPE; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]->expr_width() == 0) { /* We need to synthesize a replication of zero. */ tmp[idx] = parms_[idx]->synthesize(des, scope, root); assert(tmp[idx] == 0); num_parms -= 1; } else { tmp[idx] = parms_[idx]->synthesize(des, scope, root); if (tmp[idx] == 0) flag = false; /* Set the data type to the first one found. */ switch (data_type) { case IVL_VT_NO_TYPE: data_type = tmp[idx]->data_type(); break; case IVL_VT_BOOL: if (tmp[idx]->data_type()==IVL_VT_LOGIC) data_type = IVL_VT_LOGIC; break; default: break; } } } if (flag == false) return 0; ivl_assert(*this, data_type != IVL_VT_NO_TYPE); /* If this is a replication of zero just return 0. */ if (expr_width() == 0) return 0; /* Make a NetNet object to carry the output vector. */ perm_string path = scope->local_symbol(); netvector_t*osig_vec = new netvector_t(data_type, expr_width()-1, 0); NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); NetConcat*concat = new NetConcat(scope, scope->local_symbol(), osig->vector_width(), num_parms * repeat()); concat->set_line(*this); des->add_node(concat); connect(concat->pin(0), osig->pin(0)); unsigned count_input_width = 0; unsigned cur_pin = 1; for (unsigned rpt = 0; rpt < repeat(); rpt += 1) { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { unsigned concat_item = parms_.size()-idx-1; if (tmp[concat_item] == 0) continue; connect(concat->pin(cur_pin), tmp[concat_item]->pin(0)); cur_pin += 1; count_input_width += tmp[concat_item]->vector_width(); } } if (count_input_width != osig->vector_width()) { cerr << get_fileline() << ": internal error: " << "NetEConcat input width = " << count_input_width << ", expecting " << osig->vector_width() << " (repeat=" << repeat() << ")" << endl; des->errors += 1; } delete[]tmp; return osig; } NetNet* NetEConst::synthesize(Design*des, NetScope*scope, NetExpr*) { perm_string path = scope->local_symbol(); unsigned width=expr_width(); if (width == 0) { cerr << get_fileline() << ": internal error: " << "Found a zero width constant!" << endl; return 0; } netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); osig_vec->set_signed(has_sign()); NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); NetConst*con = new NetConst(scope, scope->local_symbol(), value()); con->set_line(*this); des->add_node(con); connect(osig->pin(0), con->pin(0)); return osig; } /* * Create a NetLiteral object to represent real valued constants. */ NetNet* NetECReal::synthesize(Design*des, NetScope*scope, NetExpr*) { perm_string path = scope->local_symbol(); netvector_t*osig_vec = new netvector_t(IVL_VT_REAL); osig_vec->set_signed(has_sign()); NetNet*osig = new NetNet(scope, path, NetNet::WIRE, osig_vec); osig->set_line(*this); osig->local_flag(true); NetLiteral*con = new NetLiteral(scope, scope->local_symbol(), value_); con->set_line(*this); des->add_node(con); connect(osig->pin(0), con->pin(0)); return osig; } /* * The bitwise unary logic operator (there is only one) is turned * into discrete gates just as easily as the binary ones above. */ NetNet* NetEUBits::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet*isig = expr_->synthesize(des, scope, root); if (isig == 0) return 0; if (isig->data_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: bit-wise negation (" << human_readable_op(op_) << ") may not have a REAL operand." << endl; des->errors += 1; return 0; } unsigned width = isig->vector_width(); netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); perm_string oname = scope->local_symbol(); NetLogic*gate; switch (op()) { case '~': gate = new NetLogic(scope, oname, 2, NetLogic::NOT, width, true); gate->set_line(*this); break; default: gate = NULL; assert(0); } connect(osig->pin(0), gate->pin(0)); connect(isig->pin(0), gate->pin(1)); des->add_node(gate); return osig; } NetNet* NetEUnary::synthesize(Design*des, NetScope*scope, NetExpr*root) { if (op_ == '+') return expr_->synthesize(des, scope, root); if (op_ == '-') { NetNet*sig = expr_->synthesize(des, scope, root); sig = sub_net_from(des, scope, 0, sig); return sig; } if (op_ == 'm') { NetNet*sub = expr_->synthesize(des, scope, root); if (expr_->has_sign() == false) return sub; netvector_t*sig_vec = new netvector_t(sub->data_type(), sub->vector_width()-1, 0); NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec); sig->set_line(*this); sig->local_flag(true); NetAbs*tmp = new NetAbs(scope, scope->local_symbol(), sub->vector_width()); tmp->set_line(*this); des->add_node(tmp); connect(tmp->pin(1), sub->pin(0)); connect(tmp->pin(0), sig->pin(0)); return sig; } cerr << get_fileline() << ": internal error: " << "NetEUnary::synthesize cannot handle op_=" << op_ << endl; des->errors += 1; return expr_->synthesize(des, scope, root); } NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet*isig = expr_->synthesize(des, scope, root); if (isig == 0) return 0; if (isig->data_type() == IVL_VT_REAL) { if (op() == '!') { cerr << get_fileline() << ": sorry: ! is currently " "unsupported for real values." << endl; des->errors += 1; return 0; } cerr << get_fileline() << ": error: reduction operator (" << human_readable_op(op_) << ") may not have a REAL operand." << endl; des->errors += 1; return 0; } NetUReduce::TYPE rtype = NetUReduce::NONE; switch (op()) { case 'N': case '!': rtype = NetUReduce::NOR; break; case '&': rtype = NetUReduce::AND; break; case '|': rtype = NetUReduce::OR; break; case '^': rtype = NetUReduce::XOR; break; case 'A': rtype = NetUReduce::NAND; break; case 'X': rtype = NetUReduce::XNOR; break; default: cerr << get_fileline() << ": internal error: " << "Unable to synthesize " << *this << "." << endl; return 0; } NetUReduce*gate = new NetUReduce(scope, scope->local_symbol(), rtype, isig->vector_width()); gate->set_line(*this); des->add_node(gate); netvector_t*osig_vec = new netvector_t(expr_type()); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); connect(gate->pin(0), osig->pin(0)); for (unsigned idx = 0 ; idx < isig->pin_count() ; idx += 1) connect(gate->pin(1+idx), isig->pin(idx)); return osig; } NetNet* NetECast::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet*isig = expr_->synthesize(des, scope, root); if (isig == 0) return 0; switch (op()) { case 'v': isig = cast_to_int4(des, scope, isig, expr_width()); break; case '2': isig = cast_to_int2(des, scope, isig, expr_width()); break; case 'r': isig = cast_to_real(des, scope, isig); break; default: cerr << get_fileline() << ": internal error: " << "Unable to synthesize " << *this << "." << endl; return 0; } return isig; } /* * Turn a part/bit select expression into gates. * We know some things about the expression that elaboration enforces * for us: * * - Expression elaboration already converted the offset expression into * canonical form, so we don't have to worry about that here. */ NetNet* NetESelect::synthesize(Design *des, NetScope*scope, NetExpr*root) { NetNet*sub = expr_->synthesize(des, scope, root); if (sub == 0) return 0; // Detect the special case that there is a base expression and // it is constant. In this case we can generate fixed part selects. if (NetEConst*base_const = dynamic_cast(base_)) { verinum base_tmp = base_const->value(); unsigned select_width = expr_width(); // Return 'bx for a constant undefined selections. if (!base_tmp.is_defined()) { NetNet*result = make_const_x(des, scope, select_width); result->set_line(*this); return result; } long base_val = base_tmp.as_long(); unsigned below_width = 0; // Any below X bits? NetNet*below = 0; if (base_val < 0) { below_width = abs(base_val); base_val = 0; if (below_width > select_width) { below_width = select_width; select_width = 0; } else { select_width -= below_width; } below = make_const_x(des, scope, below_width); below->set_line(*this); // All the selected bits are below the signal. if (select_width == 0) return below; } // Any above bits? NetNet*above = 0; if ((unsigned)base_val+select_width > sub->vector_width()) { if (base_val > (long)sub->vector_width()) { select_width = 0; } else { select_width = sub->vector_width() - base_val; } ivl_assert(*this, expr_width() > (select_width+below_width)); unsigned above_width = expr_width() - select_width - below_width; above = make_const_x(des, scope, above_width); above->set_line(*this); // All the selected bits are above the signal. if (select_width == 0) return above; } // Make the make part select. NetPartSelect*sel = new NetPartSelect(sub, base_val, select_width, NetPartSelect::VP); des->add_node(sel); ivl_assert(*this, select_width > 0); netvector_t*tmp_vec = new netvector_t(sub->data_type(), select_width-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*this); tmp->local_flag(true); connect(sel->pin(0), tmp->pin(0)); unsigned concat_count = 1; if (above) concat_count += 1; if (below) concat_count += 1; if (concat_count > 1) { NetConcat*cat = new NetConcat(scope, scope->local_symbol(), expr_width(), concat_count); cat->set_line(*this); des->add_node(cat); if (below) { connect(cat->pin(1), below->pin(0)); connect(cat->pin(2), tmp->pin(0)); } else { connect(cat->pin(1), tmp->pin(0)); } if (above) { connect(cat->pin(concat_count), above->pin(0)); } tmp_vec = new netvector_t(sub->data_type(), expr_width()-1, 0); tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*this); tmp->local_flag(true); connect(cat->pin(0), tmp->pin(0)); } return tmp; } // This handles the case that the NetESelect exists to do an // actual part/bit select. Generate a NetPartSelect object to // do the work, and replace "sub" with the selected output. if (base_ != 0) { NetNet*off = base_->synthesize(des, scope, root); NetPartSelect*sel = new NetPartSelect(sub, off, expr_width(), base_->has_sign()); sel->set_line(*this); des->add_node(sel); netvector_t*tmp_vec = new netvector_t(sub->data_type(), expr_width()-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, tmp_vec); tmp->local_flag(true); tmp->set_line(*this); sub = tmp; connect(sub->pin(0), sel->pin(0)); } // Now look for the case that the NetESelect actually exists // to change the width of the expression. (i.e. to do // padding.) If this was for an actual part select that at // this point the output vector_width is exactly right, and we // are done. if (sub->vector_width() == expr_width()) return sub; netvector_t*net_vec = new netvector_t(expr_type(), expr_width()-1, 0); net_vec->set_signed(has_sign()); NetNet*net = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, net_vec); net->set_line(*this); net->local_flag(true); // It may still happen that the expression is wider than the selection, // and there was no part select created earlier (size casting). if(expr_width() < sub->vector_width()) { NetPartSelect*sel = new NetPartSelect(sub, 0, expr_width(), NetPartSelect::VP, has_sign()); sel->set_line(*this); des->add_node(sel); connect(net->pin(0), sel->pin(0)); // The vector_width is not exactly right, so the source is // probably asking for padding. Create nodes to do sign // extension or 0 extension, depending on the has_sign() mode // of the expression. } else if (has_sign()) { NetSignExtend*pad = new NetSignExtend(scope, scope->local_symbol(), expr_width()); pad->set_line(*this); des->add_node(pad); connect(pad->pin(1), sub->pin(0)); connect(pad->pin(0), net->pin(0)); } else { NetConcat*cat = new NetConcat(scope, scope->local_symbol(), expr_width(), 2); cat->set_line(*this); des->add_node(cat); unsigned pad_width = expr_width() - sub->vector_width(); verinum pad((uint64_t)0, pad_width); NetConst*con = new NetConst(scope, scope->local_symbol(), pad); con->set_line(*this); des->add_node(con); netvector_t*tmp_vec = new netvector_t(expr_type(), pad_width-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, tmp_vec); tmp->set_line(*this); tmp->local_flag(true); connect(tmp->pin(0), con->pin(0)); connect(cat->pin(0), net->pin(0)); connect(cat->pin(1), sub->pin(0)); connect(cat->pin(2), con->pin(0)); } return net; } /* * Synthesize a ?: operator as a NetMux device. Connect the condition * expression to the select input, then connect the true and false * expressions to the B and A inputs. This way, when the select input * is one, the B input, which is the true expression, is selected. */ NetNet* NetETernary::synthesize(Design *des, NetScope*scope, NetExpr*root) { NetNet*csig = cond_->synthesize(des, scope, root), *tsig = true_val_->synthesize(des, scope, root), *fsig = false_val_->synthesize(des, scope, root); if (csig == 0 || tsig == 0 || fsig == 0) return 0; if (! NetETernary::test_operand_compat(tsig->data_type(),fsig->data_type())) { cerr << get_fileline() << ": internal error: " << " True and False clauses of ternary expression " << " have incompatible types." << endl; cerr << get_fileline() << ": : True clause is: " << tsig->data_type() << " (" << true_val_->expr_type() << "): " << *true_val_ << endl; cerr << get_fileline() << ": : False clause is: " << fsig->data_type() << " (" << false_val_->expr_type() << "): " << *false_val_ << endl; des->errors += 1; return 0; } else if (tsig->data_type() == IVL_VT_NO_TYPE) { cerr << get_fileline() << ": internal error: True and False " "clauses of ternary both have NO TYPE." << endl; des->errors += 1; return 0; } perm_string path = csig->scope()->local_symbol(); ivl_assert(*this, csig->vector_width() == 1); unsigned width=expr_width(); netvector_t*osig_vec = new netvector_t(expr_type(), width-1, 0); NetNet*osig = new NetNet(csig->scope(), path, NetNet::IMPLICIT, osig_vec); osig->set_line(*this); osig->local_flag(true); /* Make sure the types match. */ if (expr_type() == IVL_VT_REAL) { tsig = cast_to_real(des, scope, tsig); fsig = cast_to_real(des, scope, fsig); } /* Make sure both value operands are the right width. */ if (type_is_vectorable(expr_type())) { tsig = crop_to_width(des, pad_to_width(des, tsig, width, *this), width); fsig = crop_to_width(des, pad_to_width(des, fsig, width, *this), width); ivl_assert(*this, width == tsig->vector_width()); ivl_assert(*this, width == fsig->vector_width()); } perm_string oname = csig->scope()->local_symbol(); NetMux *mux = new NetMux(csig->scope(), oname, width, 2, csig->vector_width()); mux->set_line(*this); connect(tsig->pin(0), mux->pin_Data(1)); connect(fsig->pin(0), mux->pin_Data(0)); connect(osig->pin(0), mux->pin_Result()); connect(csig->pin(0), mux->pin_Sel()); des->add_node(mux); return osig; } /* * When synthesizing a signal expression, it is usually fine to simply * return the NetNet that it refers to. If this is an array word though, * a bit more work needs to be done. Return a temporary that represents * the selected word. */ NetNet* NetESignal::synthesize(Design*des, NetScope*scope, NetExpr*root) { // If this is a synthesis with a specific value for the // signal, then replace it (here) with a constant value. if (net_->scope()==scope && net_->name()==scope->genvar_tmp) { netvector_t*tmp_vec = new netvector_t(net_->data_type(), net_->vector_width()-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, tmp_vec); tmp->set_line(*this); tmp->local_flag(true); verinum tmp_val ((uint64_t)scope->genvar_tmp_val, net_->vector_width()); NetConst*tmp_const = new NetConst(scope, scope->local_symbol(), tmp_val); tmp_const->set_line(*this); des->add_node(tmp_const); connect(tmp->pin(0), tmp_const->pin(0)); return tmp; } if (word_ == 0) return net_; netvector_t*tmp_vec = new netvector_t(net_->data_type(), net_->vector_width()-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, tmp_vec); tmp->set_line(*this); tmp->local_flag(true); // For NetExpr objects, the word index is already converted to // a canonical (lsb==0) address. Just use the index directly. if (NetEConst*index_co = dynamic_cast (word_)) { long index = index_co->value().as_long(); connect(tmp->pin(0), net_->pin(index)); } else { unsigned selwid = word_->expr_width(); NetArrayDq*mux = new NetArrayDq(scope, scope->local_symbol(), net_, selwid); mux->set_line(*this); des->add_node(mux); NetNet*index_net = word_->synthesize(des, scope, root); connect(mux->pin_Address(), index_net->pin(0)); connect(tmp->pin(0), mux->pin_Result()); } return tmp; } static NetEvWait* make_func_trigger(Design*des, NetScope*scope, NetExpr*root) { NetEvWait*trigger = 0; NexusSet*nset = root->nex_input(false); if (nset && (nset->size() > 0)) { NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*root); NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, nset->size()); pr->set_line(*root); for (unsigned idx = 0 ; idx < nset->size() ; idx += 1) connect(nset->at(idx).lnk, pr->pin(idx)); des->add_node(pr); scope->add_event(ev); trigger = new NetEvWait(0); trigger->set_line(*root); trigger->add_event(ev); } delete nset; return trigger; } NetNet* NetESFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) { const struct sfunc_return_type*def = lookup_sys_func(name_); /* We cannot use the default value for system functions in a * continuous assignment since the function name is NULL. */ if (def == 0 || def->name == 0) { cerr << get_fileline() << ": error: System function " << name_ << " not defined in system " "table or SFT file(s)." << endl; des->errors += 1; return 0; } if (debug_elaborate) { cerr << get_fileline() << ": debug: Net system function " << name_ << " returns " << def->type << endl; } NetEvWait*trigger = 0; if (parms_.empty()) { trigger = make_func_trigger(des, scope, root); } NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(), def, 1+parms_.size(), trigger); net->set_line(*this); des->add_node(net); netvector_t*osig_vec = new netvector_t(def->type, def->wid-1, 0); osig_vec->set_signed(def->type==IVL_VT_REAL? true : false); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, osig_vec); osig->set_line(*this); osig->local_flag(true); connect(net->pin(0), osig->pin(0)); unsigned errors = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { NetNet*tmp = parms_[idx]->synthesize(des, scope, root); if (tmp == 0) { cerr << get_fileline() << ": error: Unable to elaborate " << "argument " << idx << " of call to " << name_ << "." << endl; errors += 1; des->errors += 1; continue; } connect(net->pin(1+idx), tmp->pin(0)); } if (errors > 0) return 0; return osig; } NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) { vector eparms (parms_.size()); /* Synthesize the arguments. */ bool errors = false; for (unsigned idx = 0; idx < eparms.size(); idx += 1) { if (dynamic_cast (parms_[idx])) { errors = true; continue; } NetNet*tmp = parms_[idx]->synthesize(des, scope, root); if (tmp == 0) { cerr << get_fileline() << ": error: Unable to synthesize " "port " << idx << " of call to " << func_->basename() << "." << endl; errors = true; des->errors += 1; continue; } eparms[idx] = tmp; } if (errors) return 0; NetEvWait*trigger = 0; if (gn_strict_ca_eval_flag) { /* Ideally we would only do this for functions that have hidden dependencies or side effects. Once constant functions are implemented, we may be able to reuse some code to achieve this. */ trigger = make_func_trigger(des, scope, root); } NetUserFunc*net = new NetUserFunc(scope_, scope_->local_symbol(), func_, trigger); net->set_line(*this); des->add_node(net); /* Create an output signal and connect it to the function. */ netvector_t*osig_vec = new netvector_t(result_sig_->expr_type(), result_sig_->vector_width()-1, 0); NetNet*osig = new NetNet(scope_, scope_->local_symbol(), NetNet::WIRE, osig_vec); osig->set_line(*this); osig->local_flag(true); connect(net->pin(0), osig->pin(0)); if (debug_synth2) { cerr << get_fileline() << ": NetEUFunc::synthesize: " << "result_sig_->vector_width()=" << result_sig_->vector_width() << ", osig->vector_width()=" << osig->vector_width() << endl; } /* Connect the pins to the arguments. */ NetFuncDef*def = func_->func_def(); for (unsigned idx = 0; idx < eparms.size(); idx += 1) { unsigned width = def->port(idx)->vector_width(); NetNet*tmp; if (eparms[idx]->get_signed()) { tmp = pad_to_width_signed(des, eparms[idx], width, *this); } else { tmp = pad_to_width(des, eparms[idx], width, *this); } NetNet*tmpc = crop_to_width(des, tmp, width); connect(net->pin(idx+1), tmpc->pin(0)); } return osig; } iverilog-10_1/extensions.txt000066400000000000000000000060271265551621300163070ustar00rootroot00000000000000 Icarus Verilog Extensions Icarus Verilog supports certain extensions to the baseline IEEE1364 standard. Some of these are picked from extended variants of the language, such as SystemVerilog, and some are expressions of internal behavior of Icarus Verilog, made available as a tool debugging aid. * Builtin System Functions ** Extended Verilog Data Types This feature is turned off if the generation flag "-g" is set to other then the default "2x". For example, "iverilog -g2x" enables extended data types, and "iverilog -g2" disables them. Icarus Verilog adds support for extended data types. This extended type syntax is based on a proposal by Cadence Design Systems, originally as an update to the IEEE1364. That original proposal has apparently been absorbed by the IEEE1800 SystemVerilog standard. Icarus Verilog currently only takes the new primitive types from the proposal. Extended data types separates the concept of net/variable from the data type. Both nets and variables can declared with any data type. The primitive types available are: logic - The familiar 0, 1, x and z, optionally with strength. bool - Limited to only 0 and 1 real - 64bit real values Nets with logic type may have multiple drivers with strength, and the value is resolved the usual way. Only logic values may be driven to logic nets, so bool values driven onto logic nets are implicitly converted to logic. Nets with any other type may not have multiple drivers. The compiler should detect the multiple drivers and report an error. - Declarations The declaration of a net is extended to include the type of the wire, with the syntax: wire ... ; The , if omitted, is taken to be logic. The "wire" can be any of the net keywords. Wires can be logic, bool, real, or vectors of logic or bool. Some valid examples: wire real foo = 1.0; tri logic bus[31:0]; wire bool addr[23:0]; ... and so on. The declarations of variables is similar. The "reg" keyword is used to specify that this is a variable. Variables can have the same data types as nets. - Ports Module and task ports in standard Verilog are restricted to logic types. This extension removes that restriction, allowing any type to pass through the port consistent with the continuous assignment connectivity that is implied by the type. - Expressions Expressions in the face of real values is covered by the baseline Verilog standard. The bool type supports the same operators as the logic type, with the obvious differences imposed by the limited domain. Comparison operators (not case compare) return logic if either of their operands is logic. If both are bool or real (including mix of bool and real) then the result is bool. This is because comparison of bools and reals always return exactly true or false. Case comparison returns bool. This differs from baseline Verilog, which strictly speaking returns a logic, but only 0 or 1 values. All the arithmetic operators return bool if both of their operands are bool or real. Otherwise, they return logic. iverilog-10_1/functor.cc000066400000000000000000000141461265551621300153370ustar00rootroot00000000000000/* * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include "functor.h" # include "netlist.h" using namespace std; functor_t::~functor_t() { } void functor_t::event(Design*, NetEvent*) { } void functor_t::signal(Design*, NetNet*) { } void functor_t::process(Design*, NetProcTop*) { } void functor_t::lpm_abs(Design*, NetAbs*) { } void functor_t::lpm_add_sub(Design*, NetAddSub*) { } void functor_t::lpm_compare(Design*, NetCompare*) { } void functor_t::lpm_concat(Design*, NetConcat*) { } void functor_t::lpm_const(Design*, NetConst*) { } void functor_t::lpm_divide(Design*, NetDivide*) { } void functor_t::lpm_literal(Design*, NetLiteral*) { } void functor_t::lpm_modulo(Design*, NetModulo*) { } void functor_t::lpm_ff(Design*, NetFF*) { } void functor_t::lpm_logic(Design*, NetLogic*) { } void functor_t::lpm_mult(Design*, NetMult*) { } void functor_t::lpm_mux(Design*, NetMux*) { } void functor_t::lpm_part_select(Design*, NetPartSelect*) { } void functor_t::lpm_pow(Design*, NetPow*) { } void functor_t::sign_extend(Design*, NetSignExtend*) { } void functor_t::lpm_ureduce(Design*, NetUReduce*) { } void NetScope::run_functor(Design*des, functor_t*fun) { for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) cur->second->run_functor(des, fun); for (NetEvent*cur = events_ ; cur ; /* */) { NetEvent*tmp = cur; cur = cur->snext_; fun->event(des, tmp); } // apply to signals. Each iteration, allow for the possibility // that the current signal deletes itself. signals_map_iter_t cur = signals_map_.begin(); while (cur != signals_map_.end()) { signals_map_iter_t tmp = cur; ++ cur; fun->signal(des, tmp->second); } } void Design::functor(functor_t*fun) { // Scan the scopes for (list::const_iterator scope = root_scopes_.begin(); scope != root_scopes_.end(); ++ scope ) (*scope)->run_functor(this, fun); // apply to processes procs_idx_ = procs_; while (procs_idx_) { NetProcTop*idx = procs_idx_; procs_idx_ = idx->next_; fun->process(this, idx); } // apply to nodes if (nodes_) { assert(nodes_functor_cur_ == 0); assert(nodes_functor_nxt_ == 0); /* Scan the circular list of nodes, starting with the front of the list. This loop interacts with the Design::del_node method so that the functor is free to delete any nodes it choose. The destructors of the NetNode objects call the del_node method, which checks with the nodes_functor_* members, to keep the iterator operating safely. */ nodes_functor_cur_ = nodes_; do { nodes_functor_nxt_ = nodes_functor_cur_->node_next_; nodes_functor_cur_->functor_node(this, fun); if (nodes_functor_nxt_ == 0) break; nodes_functor_cur_ = nodes_functor_nxt_; } while (nodes_ && (nodes_functor_cur_ != nodes_)); nodes_functor_cur_ = 0; nodes_functor_nxt_ = 0; } } void NetNode::functor_node(Design*, functor_t*) { } void NetAbs::functor_node(Design*des, functor_t*fun) { fun->lpm_abs(des, this); } void NetAddSub::functor_node(Design*des, functor_t*fun) { fun->lpm_add_sub(des, this); } void NetCompare::functor_node(Design*des, functor_t*fun) { fun->lpm_compare(des, this); } void NetConcat::functor_node(Design*des, functor_t*fun) { fun->lpm_concat(des, this); } void NetConst::functor_node(Design*des, functor_t*fun) { fun->lpm_const(des, this); } void NetDivide::functor_node(Design*des, functor_t*fun) { fun->lpm_divide(des, this); } void NetFF::functor_node(Design*des, functor_t*fun) { fun->lpm_ff(des, this); } void NetLiteral::functor_node(Design*des, functor_t*fun) { fun->lpm_literal(des, this); } void NetLogic::functor_node(Design*des, functor_t*fun) { fun->lpm_logic(des, this); } void NetModulo::functor_node(Design*des, functor_t*fun) { fun->lpm_modulo(des, this); } void NetMult::functor_node(Design*des, functor_t*fun) { fun->lpm_mult(des, this); } void NetMux::functor_node(Design*des, functor_t*fun) { fun->lpm_mux(des, this); } void NetPartSelect::functor_node(Design*des, functor_t*fun) { fun->lpm_part_select(des, this); } void NetPow::functor_node(Design*des, functor_t*fun) { fun->lpm_pow(des, this); } void NetSignExtend::functor_node(Design*des, functor_t*fun) { fun->sign_extend(des, this); } void NetUReduce::functor_node(Design*des, functor_t*fun) { fun->lpm_ureduce(des, this); } proc_match_t::~proc_match_t() { } int NetProc::match_proc(proc_match_t*) { return 0; } int proc_match_t::assign(NetAssign*) { return 0; } int NetAssign::match_proc(proc_match_t*that) { return that->assign(this); } int proc_match_t::assign_nb(NetAssignNB*) { return 0; } int NetAssignNB::match_proc(proc_match_t*that) { return that->assign_nb(this); } int proc_match_t::block(NetBlock*) { return 0; } int NetBlock::match_proc(proc_match_t*that) { return that->block(this); } int proc_match_t::condit(NetCondit*) { return 0; } int NetCondit::match_proc(proc_match_t*that) { return that->condit(this); } int NetEvWait::match_proc(proc_match_t*that) { return that->event_wait(this); } int proc_match_t::event_wait(NetEvWait*) { return 0; } iverilog-10_1/functor.h000066400000000000000000000076551265551621300152100ustar00rootroot00000000000000#ifndef IVL_functor_H #define IVL_functor_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The functor is an object that can be applied to a design to * transform it. This is different from the target_t, which can only * scan the design but not transform it in any way. * * When a functor it scanning a process, signal or node, the functor * is free to manipulate the list by deleting items, including the * node being scanned. The Design class scanner knows how to handle * the situation. However, if objects are added to the netlist, there * is no guarantee that object will be scanned unless the functor is * rerun. */ class Design; class NetNet; class NetProcTop; struct functor_t { virtual ~functor_t(); /* Events are scanned here. */ virtual void event(class Design*des, class NetEvent*); /* This is called once for each signal in the design. */ virtual void signal(class Design*des, class NetNet*); /* This method is called for each process in the design. */ virtual void process(class Design*des, class NetProcTop*); /* This method is called for each structural abs(). */ virtual void lpm_abs(class Design*des, class NetAbs*); /* This method is called for each structural adder. */ virtual void lpm_add_sub(class Design*des, class NetAddSub*); /* This method is called for each structural comparator. */ virtual void lpm_compare(class Design*des, class NetCompare*); /* This method is called for each structural concatenation. */ virtual void lpm_concat(class Design*des, class NetConcat*); /* This method is called for each structural constant. */ virtual void lpm_const(class Design*des, class NetConst*); /* This method is called for each structural constant. */ virtual void lpm_divide(class Design*des, class NetDivide*); /* Constant literals. */ virtual void lpm_literal(class Design*des, class NetLiteral*); /* This method is called for each structural constant. */ virtual void lpm_modulo(class Design*des, class NetModulo*); /* This method is called for each FF in the design. */ virtual void lpm_ff(class Design*des, class NetFF*); /* Handle LPM combinational logic devices. */ virtual void lpm_logic(class Design*des, class NetLogic*); /* This method is called for each multiplier. */ virtual void lpm_mult(class Design*des, class NetMult*); /* This method is called for each MUX. */ virtual void lpm_mux(class Design*des, class NetMux*); virtual void lpm_part_select(class Design*des, class NetPartSelect*); /* This method is called for each power. */ virtual void lpm_pow(class Design*des, class NetPow*); /* This method is called for each unary reduction gate. */ virtual void lpm_ureduce(class Design*des, class NetUReduce*); virtual void sign_extend(class Design*des, class NetSignExtend*); }; struct proc_match_t { virtual ~proc_match_t(); virtual int assign(class NetAssign*); virtual int assign_nb(class NetAssignNB*); virtual int condit(class NetCondit*); virtual int event_wait(class NetEvWait*); virtual int block(class NetBlock*); }; #endif /* IVL_functor_H */ iverilog-10_1/glossary.txt000066400000000000000000000024551265551621300157540ustar00rootroot00000000000000 Throughout Icarus Verilog descriptions and source code, I use a variety of terms and acronyms that might be specific to Icarus Verilog, have an Icarus Verilog specific meaning, or just aren't widely known. So here I define these terms. LRM - Language Reference Manual This is a generic acronym, but in the Verilog world we sometimes mean *the* language reference manual, the IEEE1364 standard. PLI - Programming Language Interface This is a C API into Verilog simulators that is defined by the IEEE1364. There are two major interfaces, sometimes called PLI 1 and PLI 2. PLI 2 is also often called VPI. UDP - User Defined Primitive These are objects that Verilog programmers define with the "primitive" keyword. They are truth-table based devices. The syntax for defining them is described in the LRM. VPI - This is the C API that is defined by the Verilog standard, and that Icarus Verilog partially implements. See also PLI. VVM - Verilog Virtual Machine This is the Icarus Verilog runtime that works with the code generator that generates C++. VVP - Verilog Virtual Processor This is the Icarus Verilog runtime that reads in custom code in a form that I call "VVP Assembly". See the vvp/ directory for documentation on that. iverilog-10_1/ieee1364-notes.txt000066400000000000000000000466061265551621300164720ustar00rootroot00000000000000 NOTE: THE CONTENTS OF THIS FILE ARE BEING MOVED TO THE DOCUMENTATION WIKI AT http://iverilog.wikia.com. PLEASE ADD NEW ENTRIES THERE. Icarus Verilog vs. IEEE1364 Copyright 2000 Stephen Williams The IEEE1364 standard is the bible that defines the correctness of the Icarus Verilog implementation and behavior of the compiled program. The IEEE1364.1 is also referenced for matters of synthesis. So the ultimate definition of right and wrong comes from those documents. That does not mean that a Verilog implementation is fully constrained. The standard document allows for implementation specific behavior that, when properly accounted for, does not effect the intended semantics of the specified language. It is therefore possible and common to write programs that produce different results when run by different Verilog implementations. STANDARDIZATION ISSUES These are some issues where the IEEE1364 left unclear, unspecified or simply wrong. I'll try to be precise as I can, and reference the standard as needed. I've made implementation decisions for Icarus Verilog, and I will make clear what those decisions are and how they affect the language. * OBJECTS CAN BE DECLARED ANYWHERE IN THE MODULE Consider this module: module sample1; initial foo = 1; reg foo; wire tmp = bar; initial #1 $display("foo = %b, bar = %b", foo, tmp); endmodule Notice that the ``reg foo;'' declaration is placed after the first initial statement. It turns out that this is a perfectly legal module according to the -1995 and -2000 versions of the standard. The statement ``reg foo;'' is a module_item_declaration which is in turn a module_item. The BNF in the appendix of IEEE1364-1995 treats all module_item statements equally, so no order is imposed. Furthermore, there is no text (that I can find) elsewhere in the standard that imposes any ordering restriction. The sorts of restrictions I would look for are "module_item_declarations must appear before all other module_items" or "variables must be declared textually before they are referenced." Such statements simply do not exist. (Personally, I think it is fine that they don't.) The closest is the rules for implicit declarations of variables that are otherwise undeclared. In the above example, ``bar'' is implicitly declared and is therefore a wire. However, although ``initial foo = 1;'' is written before foo is declared, foo *is* declared within the module, and declared legally by the BNF of the standard. Here is another example: module sample2; initial x.foo = 1; test x; initial #1 $display("foo = %b", x.foo); endmodule module test; reg foo; endmodule; From this example one can clearly see that foo is once again declared after its use in behavioral code. One also sees a forward reference of an entire module. Once again, the standard places no restriction on the order of module declarations in a source file, so this program is, according to the standard, perfectly well formed. Icarus Verilog interprets both of these examples according to "The Standard As I Understand It." However, commercial tools in general break down with these programs. In particular, the first example may generate different errors depending on the tool. The most common error is to claim that ``foo'' is declared twice, once (implicitly) as a wire and once as a reg. So the question now becomes, "Is the standard broken, or are the tools limited?" Coverage of the standard seems to vary widely from tool to tool so it is not clear that the standard really is at fault. It is clear, however, that somebody goofed somewhere. My personal opinion is that there is no logical need to require that all module_item_declarations precede any other module items. I personally would oppose such a restriction. It may make sense to require that declarations of variables within a module be preceded by their use, although even that is not necessary for the implementation of efficient compilers. However, the existence hierarchical naming syntax as demonstrated in sample2 can have implications that affect any declaration order rules. When reaching into a module with a hierarchical name, the module being referenced is already completely declared (or not declared at all, as in sample2) so module_item order is completely irrelevant. But a "declare before use" rule would infect module ordering, by requiring that modules that are used be first defined. * TASK AND FUNCTION PARAMETERS CANNOT HAVE EXPLICIT TYPES Consider a function negate that wants to take a signed integer value and return its negative: function integer negate; input [15:0] val; negate = -val; endfunction This is not quite right, because the input is implicitly a reg type, which is unsigned. The result, then, will always be a negative value, even if a negative val is passed in. It is possible to fix up this specific example to work properly with the bit pattern of a 16bit number, but that is not the point. What's needed is clarification on whether an input can be declared in the port declaration as well as in the contained block declaration. As I understand the situation, this should be allowed: function integer negate; input [15:0] val; reg signed [15:0] val; negate = -val; endfunction In the -1995 standard, the variable is already implicitly a reg if declared within a function or task. However, in the -2000 standard there is now (as in this example) a reason why one might want to actually declare the type explicitly. I think that a port *cannot* be declared as an integer or time type (though the result can) because the range of the port declaration must match the range of the integer/time declaration, but the range of integers is unspecified. This, by the way, also applies to module ports. With the above in mind, I have decided to *allow* function and task ports to be declared with types, as long as the types are variable types, such as reg or integer. Without this, there would be no portable way to pass integers into functions/tasks. The standard does not say it is allowed, but it doesn't *disallow* it, and other commercial tools seem to work similarly. * ROUNDING OF TIME When the `timescale directive is present, the compiler is supposed to round fractional times (after scaling) to the nearest integer. The confusing bit here is that it is apparently conventional that if the `timescale directive is *not* present, times are rounded towards zero always. * VALUE OF X IN PRIMITIVE OUTPUTS The IEEE1364-1995 standard clearly states in Table 8-1 that the x symbols is allowed in input columns, but is not allowed in outputs. Furthermore, none of the examples have an x in the output of a primitive. Table 8-1 in the IEEE1364-2000 also says the same thing. However, the BNF clearly states that 0, 1, x and X are valid output_symbol characters. The standard is self contradictory. So I take it that x is allowed, as that is what Verilog-XL does. * REPEAT LOOPS vs. REPEAT EVENT CONTROL There seems to be ambiguity in how code like this should be parsed: repeat (5) @(posedge clk) ; There are two valid interpretations of this code, from the IEEE1364-1995 standard. One looks like this: procedural_timing_control_statement ::= delay_or_event_control statement_or_null delay_or_event_control ::= event_control | repeat ( expression ) event_control If this interpretation is used, then the statement should be executed after the 5th posedge of clk. However, there is also this interpretation: loop_statement ::= repeat ( expression ) statement If *this* interpretation is used, then should be executed 5 times on the posedge of clk. The way the -1995 standard is written, these are both equally valid interpretations of the example, yet they produce very different results. The standard offers no guidance on how to resolve this conflict, and the IEEE1364-2000 DRAFT does not improve the situation. Practice suggests that a repeat followed by an event control should be interpreted as a loop head, and this is what Icarus Verilog does, as well as all the other major Verilog tools, but the standard does not say this. * UNSIZED NUMERIC CONSTANTS ARE NOT LIMITED TO 32 BITS The Verilog standard allows Verilog implementations to limit the size of unsized constants to a bit width of at least 32. That means that a constant 17179869183 (36'h3_ffff_ffff) may overflow some compilers. In fact, it is common to limit these values to 32bits. However, a compiler may just as easily choose another width limit, for example 64bits. That value is equally good. However, it is not *required* that an implementation truncate at 32 bits, and in fact Icarus Verilog does not truncate at all. It will make the unsized constant as big as it needs to be to hold the value accurately. This is especially useful in situations like this; reg [width-1:0] foo = 17179869183; The programmer wants the constant to take on the width of the reg, which in this example is parameterized. Since constant sizes cannot be parameterized, the programmer ideally gives an unsized constant, which the compiler then expands/contracts to match the l-value. Also, by choosing to not ever truncate, Icarus Verilog can handle code written for a 64bit compiler as easily as for a 32bit compiler. In particular, any constants that the user does not expect to be arbitrarily truncated by his compiler will also not be truncated by Icarus Verilog, no matter what that other compiler chooses as a truncation point. * UNSIZED EXPRESSIONS AS PARAMETERS TO CONCATENATION {} The Verilog standard clearly states in 4.1.14: "Unsized constant numbers shall not be allowed in concatenations. This is because the size of each operand in the concatenation is needed to calculate the complete size of the concatenation." So for example the expression {1'b0, 16} is clearly illegal. It also stands to reason that {1'b0, 15+1} is illegal, for exactly the same justification. What is the size of the expression (15+1)? Furthermore, it is reasonable to expect that (16) and (15+1) are exactly the same so far as the compiler is concerned. Unfortunately, Cadence seems to feel otherwise. In particular, it has been reported that although {1'b0, 16} causes an error, {1'b0, 15+1} is accepted. Further testing shows that any expression other than a simple unsized constant is accepted there, even if all the operands of all the operators that make up the expression are unsized integers. This is a semantic problem. Icarus Verilog doesn't limit the size of integer constants. This is valid as stated in 2.5.1 Note 3: "The number of bits that make up an unsized number (which is a simple decimal number or a number without the size specification) shall be *at*least* 32." [emphasis added] Icarus Verilog will hold any integer constant, so the size will be as large as it needs to be, whether that is 64bits, 128bits, or more. With this in mind, what is the value of these expressions? {'h1_00_00_00_00} {'h1 << 32} {'h0_00_00_00_01 << 32} {'h5_00_00_00_00 + 1} These examples show that the standard is justified in requiring that the operands of concatenation have size. The dispute is what it takes to cause an expression to have a size, and what that size is. Verilog-XL claims that (16) does not have a size, but (15+1) does. The size of the expression (15+1) is the size of the adder that is created, but how wide is the adder when adding unsized constants? One might note that the quote from section 4.1.14 says "Unsized *constant*numbers* shall not be allowed." It does not say "Unsized expressions...", so arguably accepting (15+1) or even (16+0) as an operand to a concatenation is not a violation of the letter of the law. However, the very next sentence of the quote expresses the intent, and accepting (15+1) as having a more defined size than (16) seems to be a violation of that intent. Whatever a compiler decides the size is, the user has no way to predict it, and the compiler should not have the right to treat (15+1) any differently than (16). Therefore, Icarus Verilog takes the position that such expressions are *unsized* and are not allowed as operands to concatenations. Icarus Verilog will in general assume that operations on unsized numbers produce unsized results. There are exceptions when the operator itself does define a size, such as the comparison operators or the reduction operators. Icarus Verilog will generate appropriate error messages. * MODULE INSTANCE WITH WRONG SIZE PORT LIST A module declaration like this declares a module that takes three ports: module three (a, b, c); input a, b, c; reg x; endmodule This is fine and obvious. It is also clear from the standard that these are legal instantiations of this module: three u1 (x,y,z); three u2 ( ,y, ); three u3 ( , , ); three u4 (.b(y)); In some of the above examples, there are unconnected ports. In the case of u4, the pass by name connects only port b, and leaves a and c unconnected. u2 and u4 are the same thing, in fact, but using positional or by-name syntax. The next example is a little less obvious: three u4 (); The trick here is that strictly speaking, the parser cannot tell whether this is a list of no pass by name ports (that is, all unconnected) or an empty positional list. If this were an empty positional list, then the wrong number of ports is given, but if it is an empty by-name list, it is an obviously valid instantiation. So it is fine to accept this case as valid. These are more doubtful: three u5(x,y); three u6(,); These are definitely positional port lists, and they are definitely the wrong length. In this case, the standard is not explicit about what to do about positional port lists in module instantiations, except that the first is connected to the first, second to second, etc. It does not say that the list must be the right length, but every example of unconnected ports used by-name syntax, and every example of ordered list has the right size list. Icarus Verilog takes the (very weak) hint that ordered lists should be the right length, and will therefore flag instances u5 and u6 as errors. The IEEE1364 standard should be more specific one way or the other. * UNKNOWN VALUES IN L-VALUE BIT SELECTS Consider this example: reg [7:0] vec; wire [4:0] idx = ; [...] vec[idx] = 1; So long as the value of idx is a valid bit select address, the behavior of this assignment is obvious. However, there is no explicit word in the standard as to what happens if the value is out of range. The standard clearly states the value of an expression when the bit-select or part select is out of range (the value is x) but does not address the behavior when the expression is an l-value. Icarus Verilog will take the position that bit select expressions in the l-value will select oblivion if it is out of range. That is, if idx has a value that is not a valid bit select of vec, then the assignment will have no effect. * SCHEDULING VALUES IN LOGIC The interaction between blocking assignments in procedural code and logic gates in gate-level code and expressions is poorly defined in Verilog. Consider this example: reg a; reg b; wire q = a & b; initial begin a = 1; b = 0; #1 b = 1; if (q !== 0) begin $display("FAILED -- q changed too soon? %b", q); $finish; end end This is a confusing situation. It is clear from the Verilog standard that an assignment to a variable using a blocking assign causes the l-value to receive the value before the assignment completes. This means that a subsequent read of the assigned variable *must* read back what was blocking-assigned. However, in the example above, the "wire q = a & b" expresses some gate logic between a/b and q. The standard does not say whether a read out of logic should read the value computed from previous assigns to the input from the same thread. Specifically, when "a" and "b" are assigned by blocking assignments, will a read of "q" get the computed value or the existing value? In fact, existing commercial tools do it both ways. Some tools print the FAILED message in the above example, and some do not. Icarus Verilog does not print the FAILED message in the above example, because the gate value change is *scheduled* when inputs are assigned, but not propagated until the thread gives up the processor. Icarus Verilog chooses this behavior in order to filter out zero-width pulses as early as possible. The implication of this is that a read of the output of combinational logic will most likely *not* reflect the changes in inputs until the thread that changed the inputs yields execution. * BIT AND PART SELECTS OF PARAMETERS Bit and part selects are supposed to only be supported on vector nets and variables (wires, regs, etc.) However, it is common for Verilog compilers to also support bit and part select on parameters. Icarus Verilog also chooses to support bit and part selects on parameter names, but we need to define what that means. A bit or a part select on a parameter expression returns an unsigned value with a defined size. The parameter value is considered be a constant vector of bits foo[X:0]. That is, zero based. The bit and part selects operate from that assumption. Verilog 2001 adds syntax to allow the user to explicitly declare the parameter range (i.e. parameter [5:0] foo = 9;) so Icarus Verilog will (or should) use the explicitly declared vector dimensions to interpret bit and part selects. * EDGES OF VECTORS Consider this example: reg [ 5:0] clock; always @(posedge clock) [do stuff] The IEEE1364 standard clearly states that the @(posedge clock) looks only at the bit clock[0] (the least significant bit) to search for edges. It has been pointed out by some that Verilog XL instead implements it as "@(posedge |clock)": it looks for a rise in the reduction or of the vector. Cadence Design Systems technical support has been rumored to claim that the IEEE1364 specification is wrong, but NC-Verilog behaves according to the specification, and thus different from XL. Icarus Verilog, therefore, takes the position that the specification is clear and correct, and it behaves as does NC-Verilog in this matter. * REAL VARIABLES IN $dumpoff DEAD-ZONES The IEEE1364 standard clearly states that in VCD files, the $dumpoff section checkpoints all the dumped variables as X values. For reg and wire bits/vectors, this obviously means 'bx values. Icarus Verilog does this, for example: $dumpoff x! x" $end Real variables can also be included in VCD dumps, but it is not at all obvious what is supposed to be dumped into the $dumpoff-$end section of the VCD file. Verilog-XL dumps "r0 !" to set the real variables to the dead-zone value of 0.0, whereas other tools, such as ModelTech, ignore real variables in this section. For example (from XL): $dumpoff r0 ! r0 " $end Icarus Verilog dumps NaN values for real variables in the $dumpoff-$end section of the VCD file. The NaN value is the IEEE754 equivalent of an unknown value, and so better reflects the unknown (during the dead zone) status of the variable, like this: $dumpoff rNaN ! rNaN " $end It turns out that NaN is conventionally accepted by scanf functions, and viewers that support real variables support NaN values. So while the IEEE1364 doesn't require this behavior, and given the variety that already seems to exist amongst VCD viewers in the wild, this behavior seems to be acceptable according to the standard, is a better mirror of 4-value behavior in the dead zone, and appears more user friendly when viewed by reasonable viewers. iverilog-10_1/install-sh000077500000000000000000000112351265551621300153500ustar00rootroot00000000000000#! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5. # # 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. # # 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 iverilog-10_1/iverilog-vpi.man.in000066400000000000000000000067761265551621300171000ustar00rootroot00000000000000.TH iverilog-vpi 1 "May 10th, 2015" "" "Version %M.%n%E" .SH NAME iverilog-vpi - Compile front end for VPI modules .SH SYNOPSIS .B iverilog-vpi [options] \fIsourcefile\fP... .SH DESCRIPTION .PP \fIiverilog\-vpi\fP is a tool to simplify the compilation of VPI modules for use with Icarus Verilog. It takes on the command line a list of C or C++ source files, and generates as output a linked VPI module. See the \fBvvp\fP(1) man page for a description of how the linked module is loaded by a simulation. By default the output is named after the first source file. For example, if the first source file is named \fIfoo.c\fP, the output becomes \fIfoo.vpi\fP. .SH OPTIONS \fIiverilog\-vpi\fP accepts the following options: .TP 8 .B -l\fIlibrary\fP Include the named library in the link of the VPI module. This allows VPI modules to further reference external libraries. .TP 8 .B -I\fIdirectory\fP Add \fIdirectory\fP to the list of directories that will be searched for header files. .TP 8 .B -D\fIdefine\fP Define a macro named \fIdefine\fP. .TP 8 .B --name=\fIname\fP Normally, the output VPI module will be named after the first source file passed to the command. This flag sets the name (without the .vpi suffix) of the output vpi module. .SH "PC-ONLY OPTIONS" When built as a native Windows program (using the MinGW toolchain), by default \fIiverilog\-vpi\fP will attempt to locate the MinGW tools needed to compile a VPI module on the system path (as set by the PATH environment variable). As an alternative, the user may specify the location of the MinGW tools via the following option. .TP 8 .B -mingw=\fIpath\fP Tell the program the root of the MinGW compiler tool suite. The \fBvvp\fP runtime is compiled with this compiler, and this is the compiler that \fIiverilog\-vpi\fP expects to use to compile your source code. If this option accompanies a list of files, it will apply to the current build only. If this option is provided on its own, \fIiverilog\-vpi\fP will save the \fIpath\fP in the registry and use that path in preference to the system path for subsequent operations, avoiding the need to specify it on the command line every time. .SH "INFORMATIONAL OPTIONS" \fIiverilog\-vpi\fP includes additional flags to let Makefile gurus peek at the configuration of the \fIiverilog\fP installation. This way, Makefiles can be written that handle complex VPI builds natively, and without hard-coding values that depend on the system and installation. If used at all, these options must be used one at a time, and without any other options or directives. .TP 8 .B --install-dir Print the install directory for VPI modules. .TP 8 .B --cflags Print the compiler flags (CFLAGS or CXXFLAGS) needed to compile source code destined for a VPI module. .TP 8 .B --ldflags Print the linker flags (LDFLAGS) needed to link a VPI module. .TP 8 .B --ldlibs Print the libraries (LDLIBS) needed to link a VPI module. .P Example GNU makefile that takes advantage of these flags: .IP "" 4 CFLAGS = \-Wall \-O $(CFLAGS_$@) .br VPI_CFLAGS := $(shell iverilog-vpi \-\-cflags) .br CFLAGS_messagev.o = $(VPI_CFLAGS) .br CFLAGS_fifo.o = $(VPI_CFLAGS) .br messagev.o fifo.o: transport.h .br messagev.vpi: messagev.o fifo.o .br iverilog-vpi $^ .SH "AUTHOR" .nf Steve Williams (steve@icarus.com) .SH SEE ALSO iverilog(1), vvp(1), .BR "", .BR "", .SH COPYRIGHT .nf Copyright \(co 2002\-2015 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 iverilog-10_1/iverilog-vpi.sh000066400000000000000000000057331265551621300163220ustar00rootroot00000000000000#!/bin/sh # # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Boston, MA 02111-1307, USA # # These are the variables used for compiling files CC="@IVCC@" CXX=@IVCXX@ CFLAGS="@PIC@ @IVCFLAGS@ -I@INCLUDEDIR@" CXXFLAGS="@PIC@ @IVCXXFLAGS@ -I@INCLUDEDIR@" SUFFIX=@SUFFIX@ # These are used for linking... LD=$CC LDFLAGS="@IVCTARGETFLAGS@ @SHARED@ -L@LIBDIR@" LDLIBS="-lveriuser$SUFFIX -lvpi$SUFFIX" CCSRC= CXSRC= OBJ= LIB= OUT= INCOPT= DEFS= # -- # parse the command line switches. This collects the source files # and precompiled object files, and maybe user libraries. As we are # going, guess an output file name. for parm do case $parm in *.c) CCSRC="$CCSRC $parm" if [ x$OUT = x ]; then OUT=`basename $parm .c` fi ;; *.cc) CXSRC="$CXSRC $parm" LD=$CXX if [ x$OUT = x ]; then OUT=`basename $parm .cc` fi ;; *.cpp) CXSRC="$CXSRC $parm" LD=$CXX if [ x$OUT = x ]; then OUT=`basename $parm .cpp` fi ;; *.o) OBJ="$OBJ $parm" if [ x$OUT = x ]; then OUT=`basename $parm .o` fi ;; --name=*) OUT=`echo $parm | cut -b8-` ;; -l*) LIB="$LIB $parm" ;; -I*) INCOPT="$INCOPT $parm" ;; -D*) DEFS="$DEFS $parm" ;; --cflags) echo "$CFLAGS" exit; ;; --ldflags) echo "$LDFLAGS" exit; ;; --ldlibs) echo "$LDLIBS" exit; ;; --install-dir) echo "@LIBDIR@/ivl$SUFFIX" exit ;; esac done if [ x$OUT = x ]; then echo "Usage: $0 [src and obj files]..." 1>&2 exit 0 fi # Put the .vpi on the result file. OUT=$OUT".vpi" compile_errors=0 # Compile all the source files into object files for src in $CCSRC do base=`basename $src .c` obj=$base".o" echo "Compiling $src..." $CC -c -o $obj $DEFS $CFLAGS $INCOPT $src || compile_errors=`expr $compile_errors + 1` OBJ="$OBJ $obj" done for src in $CXSRC do base=`basename $src .cc` obj=$base".o" echo "Compiling $src..." $CXX -c -o $obj $DEFS $CXXFLAGS $INCOPT $src || compile_errors=`expr $compile_errors + 1` OBJ="$OBJ $obj" done if test $compile_errors -gt 0 then echo "$0: $compile_errors file(s) failed to compile." exit $compile_errors fi echo "Making $OUT from $OBJ..." exec $LD -o $OUT $LDFLAGS $OBJ $LIB $LDLIBS iverilog-10_1/ivl.def000066400000000000000000000124021265551621300146130ustar00rootroot00000000000000EXPORTS ivl_branch_island ivl_branch_terminal ivl_design_const ivl_design_consts ivl_design_discipline ivl_design_disciplines ivl_design_delay_sel ivl_design_flag ivl_design_process ivl_design_root ivl_design_roots ivl_design_time_precision ivl_const_bits ivl_const_delay ivl_const_file ivl_const_lineno ivl_const_nex ivl_const_real ivl_const_scope ivl_const_signed ivl_const_type ivl_const_width ivl_discipline_domain ivl_discipline_flow ivl_discipline_name ivl_discipline_potential ivl_enum_bits ivl_enum_file ivl_enum_lineno ivl_enum_name ivl_enum_names ivl_enum_signed ivl_enum_type ivl_enum_width ivl_event_any ivl_event_basename ivl_event_file ivl_event_lineno ivl_event_name ivl_event_nany ivl_event_neg ivl_event_nneg ivl_event_npos ivl_event_pos ivl_event_scope ivl_expr_type ivl_expr_bits ivl_expr_branch ivl_expr_def ivl_expr_delay_val ivl_expr_dvalue ivl_expr_enumtype ivl_expr_event ivl_expr_file ivl_expr_lineno ivl_expr_name ivl_expr_nature ivl_expr_net_type ivl_expr_opcode ivl_expr_oper1 ivl_expr_oper2 ivl_expr_oper3 ivl_expr_parameter ivl_expr_parm ivl_expr_parms ivl_expr_property_idx ivl_expr_repeat ivl_expr_scope ivl_expr_sel_type ivl_expr_signal ivl_expr_signed ivl_expr_sized ivl_expr_string ivl_expr_uvalue ivl_expr_value ivl_expr_width ivl_file_table_index ivl_file_table_item ivl_file_table_size ivl_island_flag_set ivl_island_flag_test ivl_logic_attr ivl_logic_attr_cnt ivl_logic_attr_val ivl_logic_basename ivl_logic_delay ivl_logic_drive0 ivl_logic_drive1 ivl_logic_file ivl_logic_is_cassign ivl_logic_lineno ivl_logic_name ivl_logic_pin ivl_logic_pins ivl_logic_scope ivl_logic_type ivl_logic_udp ivl_logic_width ivl_lpm_array ivl_lpm_aset_value ivl_lpm_async_clr ivl_lpm_async_set ivl_lpm_base ivl_lpm_basename ivl_lpm_clk ivl_lpm_data ivl_lpm_datab ivl_lpm_define ivl_lpm_delay ivl_lpm_drive0 ivl_lpm_drive1 ivl_lpm_enable ivl_lpm_file ivl_lpm_lineno ivl_lpm_name ivl_lpm_negedge ivl_lpm_q ivl_lpm_scope ivl_lpm_select ivl_lpm_selects ivl_lpm_signed ivl_lpm_size ivl_lpm_sset_value ivl_lpm_string ivl_lpm_sync_clr ivl_lpm_sync_set ivl_lpm_trigger ivl_lpm_type ivl_lpm_width ivl_lval_idx ivl_lval_mux ivl_lval_nest ivl_lval_part_off ivl_lval_property_idx ivl_lval_sel_type ivl_lval_sig ivl_lval_width ivl_nature_name ivl_nexus_get_private ivl_nexus_name ivl_nexus_ptrs ivl_nexus_ptr ivl_nexus_set_private ivl_nexus_ptr_branch ivl_nexus_ptr_con ivl_nexus_ptr_drive0 ivl_nexus_ptr_drive1 ivl_nexus_ptr_pin ivl_nexus_ptr_lpm ivl_nexus_ptr_log ivl_nexus_ptr_sig ivl_nexus_ptr_switch ivl_parameter_basename ivl_parameter_expr ivl_parameter_file ivl_parameter_lineno ivl_parameter_local ivl_parameter_lsb ivl_parameter_msb ivl_parameter_scope ivl_parameter_signed ivl_parameter_width ivl_path_condit ivl_path_delay ivl_path_is_condit ivl_path_scope ivl_path_source ivl_path_source_negedge ivl_path_source_posedge ivl_process_analog ivl_process_attr_cnt ivl_process_attr_val ivl_process_file ivl_process_lineno ivl_process_scope ivl_process_stmt ivl_process_type ivl_scope_attr_cnt ivl_scope_attr_val ivl_scope_basename ivl_scope_children ivl_scope_child ivl_scope_childs ivl_scope_class ivl_scope_classes ivl_scope_def ivl_scope_def_file ivl_scope_def_lineno ivl_scope_enumerate ivl_scope_enumerates ivl_scope_event ivl_scope_events ivl_scope_file ivl_scope_is_auto ivl_scope_is_cell ivl_scope_lineno ivl_scope_logs ivl_scope_log ivl_scope_lpms ivl_scope_lpm ivl_scope_mod_module_ports ivl_scope_mod_module_port_name ivl_scope_mod_module_port_type ivl_scope_mod_module_port_width ivl_scope_mod_port ivl_scope_name ivl_scope_param ivl_scope_params ivl_scope_parent ivl_scope_port ivl_scope_ports ivl_scope_sigs ivl_scope_sig ivl_scope_switch ivl_scope_switches ivl_scope_time_precision ivl_scope_time_units ivl_scope_type ivl_scope_tname ivl_signal_array_addr_swapped ivl_signal_array_base ivl_signal_array_count ivl_signal_attr ivl_signal_attr_cnt ivl_signal_attr_val ivl_signal_basename ivl_signal_data_type ivl_signal_dimensions ivl_signal_discipline ivl_signal_file ivl_signal_forced_net ivl_signal_integer ivl_signal_lineno ivl_signal_local ivl_signal_lsb ivl_signal_msb ivl_signal_name ivl_signal_net_type ivl_signal_nex ivl_signal_npath ivl_signal_packed_dimensions ivl_signal_packed_lsb ivl_signal_packed_msb ivl_signal_path ivl_signal_port ivl_signal_scope ivl_signal_signed ivl_signal_type ivl_signal_width ivl_statement_type ivl_stmt_block_count ivl_stmt_block_scope ivl_stmt_block_stmt ivl_stmt_call ivl_stmt_case_count ivl_stmt_case_expr ivl_stmt_case_stmt ivl_stmt_cond_expr ivl_stmt_cond_false ivl_stmt_cond_true ivl_stmt_delay_expr ivl_stmt_delay_val ivl_stmt_events ivl_stmt_file ivl_stmt_lineno ivl_stmt_lexp ivl_stmt_lval ivl_stmt_lvals ivl_stmt_lwidth ivl_stmt_name ivl_stmt_nevent ivl_stmt_opcode ivl_stmt_parm ivl_stmt_parm_count ivl_stmt_rval ivl_stmt_sfunc_as_task ivl_stmt_sub_stmt ivl_switch_a ivl_switch_b ivl_switch_basename ivl_switch_delay ivl_switch_enable ivl_switch_file ivl_switch_island ivl_switch_lineno ivl_switch_offset ivl_switch_part ivl_switch_scope ivl_switch_type ivl_switch_width ivl_type_base ivl_type_element ivl_type_name ivl_type_packed_dimensions ivl_type_packed_lsb ivl_type_packed_msb ivl_type_prop_name ivl_type_prop_type ivl_type_properties ivl_type_signed ivl_udp_init ivl_udp_file ivl_udp_lineno ivl_udp_name ivl_udp_nin ivl_udp_port ivl_udp_row ivl_udp_rows ivl_udp_sequ iverilog-10_1/ivl_alloc.h000066400000000000000000000052341265551621300154630ustar00rootroot00000000000000#ifndef IVL_ivl_alloc_H #define IVL_ivl_alloc_H /* * Copyright (C) 2010-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef __cplusplus # include # include #else # include # include #endif #if defined(__GNUC__) /* * Define a safer version of malloc(). */ #define malloc(__ivl_size) \ ({ \ /* To be safe we only evaluate the argument once. */ \ size_t __ivl_lsize = __ivl_size; \ void *__ivl_rtn = malloc(__ivl_lsize); \ /* If we run out of memory then exit with a message. */ \ if ((__ivl_rtn == NULL) && (__ivl_lsize != 0)) { \ fprintf(stderr, "%s:%d: Error: malloc() ran out of memory.\n", \ __FILE__, __LINE__); \ exit(1); \ } \ __ivl_rtn; \ }) /* * Define a safer version of realloc(). */ #define realloc(__ivl_ptr, __ivl_size) \ ({ \ /* To be safe we only evaluate the arguments once. */ \ void *__ivl_lptr = __ivl_ptr; \ size_t __ivl_lsize = __ivl_size; \ void *__ivl_rtn = realloc(__ivl_lptr, __ivl_lsize); \ /* If we run out of memory then exit with a message. */ \ if ((__ivl_rtn == NULL) && (__ivl_lsize != 0)) { \ fprintf(stderr, "%s:%d: Error: realloc() ran out of memory.\n", \ __FILE__, __LINE__); \ free(__ivl_lptr); \ exit(1); \ } \ __ivl_rtn; \ }) /* * Define a safer version of calloc(). */ #define calloc(__ivl_count, __ivl_size) \ ({ \ /* To be safe we only evaluate the arguments once. */ \ size_t __ivl_lcount = __ivl_count; \ size_t __ivl_lsize = __ivl_size; \ void *__ivl_rtn = calloc(__ivl_lcount, __ivl_lsize); \ /* If we run out of memory then exit with a message. */ \ if ((__ivl_rtn == NULL) && (__ivl_lcount != 0) && (__ivl_lsize != 0)) { \ fprintf(stderr, "%s:%d: Error: calloc() ran out of memory.\n", \ __FILE__, __LINE__); \ exit(1); \ } \ __ivl_rtn; \ }) #endif #endif /* IVL_ivl_alloc_H */ iverilog-10_1/ivl_assert.h000066400000000000000000000023221265551621300156650ustar00rootroot00000000000000#ifndef IVL_ivl_assert_H #define IVL_ivl_assert_H /* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include #define ivl_assert(tok, expression) \ do { \ if (! (expression)) { \ cerr << (tok).get_fileline() << ": assert: " \ << __FILE__ << ":" << __LINE__ \ << ": failed assertion " << #expression << endl; \ abort(); \ } \ } while (0) #endif /* IVL_ivl_assert_H */ iverilog-10_1/ivl_target.h000066400000000000000000002701001265551621300156530ustar00rootroot00000000000000#ifndef IVL_ivl_target_H #define IVL_ivl_target_H /* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include /* Re the _CLASS define: clang++ wants this to be class to match the * definition, but clang (the C) compiler needs it to be a struct * since class is not defined in C. They are effectively both pointers * to an object so everything works out. */ #ifdef __cplusplus #define _BEGIN_DECL extern "C" { #define _END_DECL } #define _CLASS class #else #define _BEGIN_DECL #define _END_DECL #define _CLASS struct #endif #ifndef __GNUC__ # define __attribute__(x) #endif #if defined(__cplusplus) && defined(_MSC_VER) # define ENUM_UNSIGNED_INT : unsigned int #else # define ENUM_UNSIGNED_INT #endif _BEGIN_DECL /* * This header file describes the API for the loadable target * module. The main program can load these modules and access the * functions within the loaded module to implement the backend * behavior. * * The interface is divided into two parts: the entry points within * the core that are called by the module, and the entry points in * the module that are called by the core. It is the latter that * causes the module to be invoked in the first place, but most of the * interesting information about the design is accessed through the * various access functions that the modules calls into the core. */ /* * In order to grab onto data in the design, the core passes cookies * to the various functions of the module. These cookies can in turn * be passed to access functions in the core to get more detailed * information. * * The following typedefs list the various cookies that may be passed * around. * * ivl_array_t * This object represents an array that can be a memory or a net * array. (They are the same from the perspective of ivl_target.h.) * * ivl_branch_t * this object represents an analog branch. * * ivl_design_t * This object represents the entire elaborated design. Various * global properties and methods are available from this. * * ivl_event_t * This object represents an event node. An event node stands for * named events written explicitly in the Verilog, and net events * that are implicit when @ statements are used. * * ivl_expr_t * This object represents a node of an expression. If the * expression has sub-expressions, they can be accessed from * various method described below. The ivl_expr_type method in * particular gets the type of the node in the form of an * ivl_expr_type_t enumeration value. * * Objects of this type represent expressions in processes. * Structural expressions are instead treated as logic gates. * * ivl_island_t * Certain types of objects may belong to islands. The island that * they belong to is represented by the ivl_island_t cookie. To * know if objects belong to the same island, it is sufficient to * compare island cookies. If a==b, then island a is the same as * island b. * * ivl_lpm_t * This object is the base class for all the various LPM type * device nodes. This object carries a few base properties * (including a type) including a handle to the specific type. * * ivl_net_logic_t * This object represents various built in logic devices. In fact, * this includes just about every directional device that has a * single output, including logic gates and nmos, pmos and cmos * devices. There is also the occasional Icarus Verilog creation. * What is common about these devices is that they are * bitwise. That is, when fed a vector, they produce a vector * result where each bit of the output is made only from the same * bits in the vector inputs. * * ivl_nexus_t * Structural links within an elaborated design are connected * together at each bit. The connection point is a nexus, so pins * of devices refer to an ivl_nexus_t. Furthermore, from a nexus * there are backward references to all the device pins that point * to it. * * ivl_parameter_t * Scopes have zero or more parameter objects that represent * parameters that the source defined. The parameter has a value * that is fully elaborated, with defparams and other parameter * overrides taken care of. * * ivl_process_t * A Verilog process is represented by one of these. A process may * be an "initial" or an "always" process. These come from initial * or always statements from the Verilog source. * * ivl_scope_t * Elaborated scopes within a design are represented by this * type. Objects of this type also act as containers for scoped * objects such as signals. * * ivl_statement_t * Statements within processes are represented by one of these. The * ivl_process_t object holds one of these, but a statement may in * turn contain other statements. * * ivl_switch_t * Switches are the tran/tranif devices in the design. * * -- A Note About Bit Sets -- * Some objects hold a value as an array of bits. In these cases there * is some method that retrieves the width of the value and another * that returns a "char*". The latter is a pointer to the least * significant bit value. Bit values are represented by the characters * '0', '1', 'x' and 'z'. Strengths are stored elsewhere. * * -- A Note About Names -- * The names of objects are complete, hierarchical names. That is, * they include the instance name of the module that contains them. * * basenames are the name of the object without the containing * scope. These names are unique within a scope, but not necessarily * throughout the design. */ typedef struct ivl_array_s *ivl_array_t; typedef struct ivl_branch_s *ivl_branch_t; typedef struct ivl_delaypath_s*ivl_delaypath_t; typedef struct ivl_design_s *ivl_design_t; typedef _CLASS ivl_discipline_s*ivl_discipline_t; typedef const _CLASS netenum_t*ivl_enumtype_t; typedef struct ivl_event_s *ivl_event_t; typedef struct ivl_expr_s *ivl_expr_t; typedef struct ivl_island_s *ivl_island_t; typedef struct ivl_lpm_s *ivl_lpm_t; typedef struct ivl_lval_s *ivl_lval_t; typedef struct ivl_net_const_s*ivl_net_const_t; typedef struct ivl_net_logic_s*ivl_net_logic_t; typedef struct ivl_udp_s *ivl_udp_t; typedef _CLASS ivl_nature_s *ivl_nature_t; typedef struct ivl_net_probe_s*ivl_net_probe_t; typedef struct ivl_nexus_s *ivl_nexus_t; typedef struct ivl_nexus_ptr_s*ivl_nexus_ptr_t; typedef struct ivl_parameter_s*ivl_parameter_t; typedef struct ivl_process_s *ivl_process_t; typedef struct ivl_scope_s *ivl_scope_t; typedef struct ivl_signal_s *ivl_signal_t; typedef struct ivl_port_info_s*ivl_port_info_t; typedef struct ivl_switch_s *ivl_switch_t; typedef struct ivl_memory_s *ivl_memory_t; //XXXX __attribute__((deprecated)); typedef struct ivl_statement_s*ivl_statement_t; typedef const _CLASS ivl_type_s*ivl_type_t; /* * These are types that are defined as enumerations. These have * explicit values so that the binary API is a bit more resilient to * changes and additions to the enumerations. */ typedef enum ivl_dis_domain_e { IVL_DIS_NONE = 0, IVL_DIS_DISCRETE = 1, IVL_DIS_CONTINUOUS = 2 } ivl_dis_domain_t; typedef enum ivl_drive_e ENUM_UNSIGNED_INT { IVL_DR_HiZ = 0, IVL_DR_SMALL = 1, IVL_DR_MEDIUM = 2, IVL_DR_WEAK = 3, IVL_DR_LARGE = 4, IVL_DR_PULL = 5, IVL_DR_STRONG = 6, IVL_DR_SUPPLY = 7 } ivl_drive_t; /* This is the type of an ivl_expr_t object. The explicit numbers allow additions to the enumeration without causing values to shift and incompatibilities to be introduced. */ typedef enum ivl_expr_type_e { IVL_EX_NONE = 0, IVL_EX_ARRAY = 18, IVL_EX_BACCESS= 19, IVL_EX_BINARY = 2, IVL_EX_CONCAT = 3, IVL_EX_DELAY = 20, IVL_EX_ENUMTYPE = 21, IVL_EX_EVENT = 17, IVL_EX_MEMORY = 4, IVL_EX_NEW = 23, IVL_EX_NULL = 22, IVL_EX_NUMBER = 5, IVL_EX_ARRAY_PATTERN = 26, IVL_EX_PROPERTY = 24, IVL_EX_REALNUM = 16, IVL_EX_SCOPE = 6, IVL_EX_SELECT = 7, IVL_EX_SFUNC = 8, IVL_EX_SHALLOWCOPY = 25, IVL_EX_SIGNAL = 9, IVL_EX_STRING = 10, IVL_EX_TERNARY = 11, IVL_EX_UFUNC = 12, IVL_EX_ULONG = 13, IVL_EX_UNARY = 14 } ivl_expr_type_t; typedef enum ivl_select_type_e ENUM_UNSIGNED_INT { IVL_SEL_OTHER = 0, IVL_SEL_IDX_UP = 1, IVL_SEL_IDX_DOWN = 2 } ivl_select_type_t; /* This is the type code for an ivl_net_logic_t object. */ typedef enum ivl_logic_e { IVL_LO_NONE = 0, IVL_LO_AND = 1, IVL_LO_BUF = 2, IVL_LO_BUFIF0 = 3, IVL_LO_BUFIF1 = 4, IVL_LO_BUFT = 24, /* transparent bufz. (NOT "tri-state") */ IVL_LO_BUFZ = 5, IVL_LO_CMOS = 22, IVL_LO_NAND = 6, IVL_LO_NMOS = 7, IVL_LO_NOR = 8, IVL_LO_NOT = 9, IVL_LO_NOTIF0 = 10, IVL_LO_NOTIF1 = 11, IVL_LO_OR = 12, IVL_LO_PMOS = 17, IVL_LO_PULLDOWN = 13, IVL_LO_PULLUP = 14, IVL_LO_RCMOS = 23, IVL_LO_RNMOS = 15, IVL_LO_RPMOS = 16, IVL_LO_XNOR = 18, IVL_LO_XOR = 19, IVL_LO_UDP = 21 } ivl_logic_t; /* This is the type of a ivl_switch_t object */ typedef enum ivl_switch_type_e { IVL_SW_TRAN = 0, IVL_SW_TRANIF0 = 1, IVL_SW_TRANIF1 = 2, IVL_SW_RTRAN = 3, IVL_SW_RTRANIF0 = 4, IVL_SW_RTRANIF1 = 5, IVL_SW_TRAN_VP = 6 } ivl_switch_type_t; /* This is the type of an LPM object. */ typedef enum ivl_lpm_type_e { IVL_LPM_ABS = 32, IVL_LPM_ADD = 0, IVL_LPM_ARRAY = 30, IVL_LPM_CAST_INT = 34, IVL_LPM_CAST_INT2 = 35, IVL_LPM_CAST_REAL = 33, IVL_LPM_CONCAT = 16, IVL_LPM_CONCATZ = 36, /* Transparent concat */ IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ IVL_LPM_CMP_EQX= 37, /* Wildcard EQ (==?) */ IVL_LPM_CMP_EQZ= 38, /* casez EQ */ IVL_LPM_CMP_EQ = 10, IVL_LPM_CMP_GE = 1, IVL_LPM_CMP_GT = 2, IVL_LPM_CMP_NE = 11, IVL_LPM_CMP_NEE= 19, /* Case NE (!==) */ IVL_LPM_DIVIDE = 12, IVL_LPM_FF = 3, IVL_LPM_MOD = 13, IVL_LPM_MULT = 4, IVL_LPM_MUX = 5, /* IVL_LPM_PART_BI= 28, / obsolete */ IVL_LPM_PART_VP= 15, /* part select: vector to part */ IVL_LPM_PART_PV= 17, /* part select: part written to vector */ IVL_LPM_POW = 31, IVL_LPM_RE_AND = 20, IVL_LPM_RE_NAND= 21, IVL_LPM_RE_NOR = 22, IVL_LPM_RE_OR = 23, IVL_LPM_RE_XNOR= 24, IVL_LPM_RE_XOR = 25, IVL_LPM_REPEAT = 26, IVL_LPM_SFUNC = 29, IVL_LPM_SHIFTL = 6, IVL_LPM_SHIFTR = 7, IVL_LPM_SIGN_EXT=27, IVL_LPM_SUB = 8, IVL_LPM_SUBSTITUTE=39, /* IVL_LPM_RAM = 9, / obsolete */ IVL_LPM_UFUNC = 14 } ivl_lpm_type_t; /* The path edge type is the edge type used to select a specific delay. */ typedef enum ivl_path_edge_e { IVL_PE_01 = 0, IVL_PE_10, IVL_PE_0z, IVL_PE_z1, IVL_PE_1z, IVL_PE_z0, IVL_PE_0x, IVL_PE_x1, IVL_PE_1x, IVL_PE_x0, IVL_PE_xz, IVL_PE_zx, IVL_PE_COUNT } ivl_path_edge_t; /* Processes are initial, always, or final blocks with a statement. This is the type of the ivl_process_t object. */ typedef enum ivl_process_type_e ENUM_UNSIGNED_INT { IVL_PR_INITIAL = 0, IVL_PR_ALWAYS = 1, IVL_PR_FINAL = 2 } ivl_process_type_t; /* These are the sorts of reasons a scope may come to be. These types are properties of ivl_scope_t objects. */ typedef enum ivl_scope_type_e { IVL_SCT_MODULE = 0, IVL_SCT_FUNCTION= 1, IVL_SCT_TASK = 2, IVL_SCT_BEGIN = 3, IVL_SCT_FORK = 4, IVL_SCT_GENERATE= 5, IVL_SCT_PACKAGE = 6, IVL_SCT_CLASS = 7 } ivl_scope_type_t; /* Signals (ivl_signal_t) that are ports into the scope that contains them have a port type. Otherwise, they are port IVL_SIP_NONE. */ typedef enum OUT { IVL_SIP_NONE = 0, IVL_SIP_INPUT = 1, IVL_SIP_OUTPUT= 2, IVL_SIP_INOUT = 3 } ivl_signal_port_t; /* This is the type code for an ivl_signal_t object. Implicit types are resolved by the core compiler, and integers are converted into signed registers. */ typedef enum ivl_signal_type_e { IVL_SIT_NONE = 0, IVL_SIT_REG = 1, IVL_SIT_TRI = 4, IVL_SIT_TRI0 = 5, IVL_SIT_TRI1 = 6, IVL_SIT_TRIAND = 7, IVL_SIT_TRIOR = 8, IVL_SIT_UWIRE = 9 } ivl_signal_type_t; /* This is the type code for ivl_statement_t objects. */ typedef enum ivl_statement_type_e { IVL_ST_NONE = 0, IVL_ST_NOOP = 1, IVL_ST_ALLOC = 25, IVL_ST_ASSIGN = 2, IVL_ST_ASSIGN_NB = 3, IVL_ST_BLOCK = 4, IVL_ST_CASE = 5, IVL_ST_CASER = 24, /* Case statement with real expressions. */ IVL_ST_CASEX = 6, IVL_ST_CASEZ = 7, IVL_ST_CASSIGN = 8, IVL_ST_CONDIT = 9, IVL_ST_CONTRIB = 27, IVL_ST_DEASSIGN = 10, IVL_ST_DELAY = 11, IVL_ST_DELAYX = 12, IVL_ST_DISABLE = 13, IVL_ST_DO_WHILE = 30, IVL_ST_FORCE = 14, IVL_ST_FOREVER = 15, IVL_ST_FORK = 16, IVL_ST_FORK_JOIN_ANY = 28, IVL_ST_FORK_JOIN_NONE = 29, IVL_ST_FREE = 26, IVL_ST_RELEASE = 17, IVL_ST_REPEAT = 18, IVL_ST_STASK = 19, IVL_ST_TRIGGER = 20, IVL_ST_UTASK = 21, IVL_ST_WAIT = 22, IVL_ST_WHILE = 23 } ivl_statement_type_t; /* SystemVerilog allows a system function to be called as a task. */ typedef enum ivl_sfunc_as_task_e { IVL_SFUNC_AS_TASK_ERROR = 0, IVL_SFUNC_AS_TASK_WARNING = 1, IVL_SFUNC_AS_TASK_IGNORE = 2 } ivl_sfunc_as_task_t; /* This is the type of a variable, and also used as the type for an expression. */ typedef enum ivl_variable_type_e ENUM_UNSIGNED_INT { IVL_VT_VOID = 0, /* Not used */ IVL_VT_NO_TYPE = 1, /* Place holder for missing/unknown type. */ IVL_VT_REAL = 2, IVL_VT_BOOL = 3, IVL_VT_LOGIC = 4, IVL_VT_STRING = 5, IVL_VT_DARRAY = 6, /* Array (esp. dynamic array) */ IVL_VT_CLASS = 7, /* SystemVerilog class instances */ IVL_VT_QUEUE = 8, /* SystemVerilog queue instances */ IVL_VT_VECTOR = IVL_VT_LOGIC /* For compatibility */ } ivl_variable_type_t; /* This is the type of the function to apply to a process. */ typedef int (*ivl_process_f)(ivl_process_t net, void*cd); /* This is the type of a function to apply to a scope. The ivl_scope_t parameter is the scope, and the cd parameter is client data that the user passes to the scanner. */ typedef int (ivl_scope_f)(ivl_scope_t net, void*cd); /* Attributes, which can be attached to various object types, have this form. */ typedef enum ivl_attribute_type_e { IVL_ATT_VOID = 0, IVL_ATT_STR, IVL_ATT_NUM } ivl_attribute_type_t; struct ivl_attribute_s { const char*key; ivl_attribute_type_t type; union val_ { const char*str; long num; } val; }; typedef const struct ivl_attribute_s*ivl_attribute_t; /* BRANCH * Branches are analog constructs, a pair of terminals that is used in * branch access functions. Terminal-1 is the reference node (The * "ground") for the purposes of the access function that accesses it. * * SEMANTIC NOTES * All the branches in an island are connected by terminals or by * expressions. The island is the connection of branches that must be * solved together. */ /* extern ivl_scope_t ivl_branch_scope(ivl_branch_t obj); */ extern ivl_nexus_t ivl_branch_terminal(ivl_branch_t obj, int idx); extern ivl_island_t ivl_branch_island(ivl_branch_t obj); /* DELAYPATH * Delaypath objects represent delay paths called out by a specify * block in the Verilog source file. The destination signal references * the path object, which in turn points to the source for the path. * * ivl_path_scope * This returns the scope of the delay path. This scope corresponds * to the scope of the specify-block that led to this path. * * ivl_path_source * This returns the nexus that is the source end of the delay * path. Transitions on the source are the start of the delay time * for this path. * * ivl_path_condit * This returns the nexus that tracks the condition for the * delay. If the delay path is unconditional, this returns nil. * ivl_path_is_condit * Is this a conditional structure? Needed for ifnone. * * ivl_path_source_posedge * ivl_path_source_negedge * These functions return true if the source is edge sensitive. */ extern ivl_scope_t ivl_path_scope(ivl_delaypath_t obj); extern ivl_nexus_t ivl_path_source(ivl_delaypath_t obj); extern uint64_t ivl_path_delay(ivl_delaypath_t obj, ivl_path_edge_t pt); extern ivl_nexus_t ivl_path_condit(ivl_delaypath_t obj); extern int ivl_path_is_condit(ivl_delaypath_t obj); extern int ivl_path_source_posedge(ivl_delaypath_t obj); extern int ivl_path_source_negedge(ivl_delaypath_t obj); /* DESIGN * When handed a design (ivl_design_t) there are a few things that you * can do with it. The Verilog program has one design that carries the * entire program. Use the design methods to iterate over the elements * of the design. * * ivl_design_delay_sel * Returns the tool delay selection: "MINIMUM", "TYPICAL" or "MAXIMUM"? * * ivl_design_flag * This function returns the string value of a named flag. Flags * come from the "-pkey=value" options to the iverilog command and * are stored in a map for this function. Given the key, this * function returns the value. * * The special key "-o" is the argument to the -o flag of the * command line (or the default if the -o flag is not used) and is * generally how the target learns the name of the output file. * * ivl_design_process * This function scans the processes (threads) in the design. It * calls the user supplied function on each of the processes until * one of the functors returns non-0 or all the processes are * scanned. This function will return 0, or the non-zero value that * was returned from the last scanned process. * * ivl_design_root (ANACHRONISM) * A design has a root named scope that is an instance of the top * level module in the design. This is a hook for naming the * design, or for starting the scope scan. * * ivl_design_roots * A design has some number of root scopes. These are the starting * points for structural elaboration. This function returns to the * caller a pointer to an ivl_scope_t array, and the size of the * array. * * ivl_design_time_precision * A design as a time precision. This is the size in seconds (a * signed power of 10) of a simulation tick. */ extern const char* ivl_design_delay_sel(ivl_design_t des); extern const char* ivl_design_flag(ivl_design_t des, const char*key); extern int ivl_design_process(ivl_design_t des, ivl_process_f fun, void*cd); extern ivl_scope_t ivl_design_root(ivl_design_t des); extern void ivl_design_roots(ivl_design_t des, ivl_scope_t **scopes, unsigned int *nscopes); extern int ivl_design_time_precision(ivl_design_t des); extern unsigned ivl_design_consts(ivl_design_t des); extern ivl_net_const_t ivl_design_const(ivl_design_t, unsigned idx); extern unsigned ivl_design_disciplines(ivl_design_t des); extern ivl_discipline_t ivl_design_discipline(ivl_design_t des, unsigned idx); /* LITERAL CONSTANTS * Literal constants are nodes with no input and a single constant * output. The form of the output depends on the type of the node. * The output is an array of 4-value bits, using a single char * value for each bit. The bits of the vector are in canonical (lsb * first) order for the width of the constant. * * ivl_const_type * The is the type of the node. * * ivl_const_bits * This returns a pointer to an array of constant characters, * each byte a '0', '1', 'x' or 'z'. The array is *not* nul * terminated. This value is only value if ivl_const_type is * IVL_VT_LOGIC or IVL_VT_BOOL. It returns nil otherwise. * * ivl_const_nex * Return the ivl_nexus_t of the output for the constant. * * ivl_const_scope * Return the scope this constant was defined in. * ivl_const_signed * Return true (!0) if the constant is a signed value, 0 otherwise. * * ivl_const_width * Return the width, in logical bits, of the constant. * * ivl_const_delay * T0 delay for a transition (0, 1 and Z). * * SEMANTIC NOTES * * The const_type of the literal constant must match the * ivl_signal_data_type if the signals that share the nexus of this * node. The compiler makes sure it is so, converting constant values * as needed. * * - IVL_VT_LOGIC * * - IVL_VT_REAL * Real valued constants have a width of 1. The value emitted to the * output is ivl_const_real. */ extern ivl_variable_type_t ivl_const_type(ivl_net_const_t net); extern const char* ivl_const_bits(ivl_net_const_t net); extern ivl_expr_t ivl_const_delay(ivl_net_const_t net, unsigned transition); extern ivl_nexus_t ivl_const_nex(ivl_net_const_t net); extern ivl_scope_t ivl_const_scope(ivl_net_const_t net); extern int ivl_const_signed(ivl_net_const_t net); extern unsigned ivl_const_width(ivl_net_const_t net); extern double ivl_const_real(ivl_net_const_t net); extern const char* ivl_const_file(ivl_net_const_t net); extern unsigned ivl_const_lineno(ivl_net_const_t net); /* extern ivl_nexus_t ivl_const_pin(ivl_net_const_t net, unsigned idx); */ /* extern unsigned ivl_const_pins(ivl_net_const_t net); */ /* DISCIPLINES * * Disciplines are Verilog-AMS construct. A discipline is a collection * of attributes that can be attached to a signal. * * FUNCTION SUMMARY * * ivl_discipline_name * This is the name of the discipline in the Verilog-AMS source. * * ivl_discipline_domain * This is the domain: continuous or discrete. * * SEMANTIC NOTES * * The discipline domain will not be IVL_DIS_NONE. The "none" domain * is a place-holder internally for incomplete parsing, and is also * available for code generators to use. */ extern const char*ivl_discipline_name(ivl_discipline_t net); extern ivl_dis_domain_t ivl_discipline_domain(ivl_discipline_t net); extern ivl_nature_t ivl_discipline_potential(ivl_discipline_t net); extern ivl_nature_t ivl_discipline_flow(ivl_discipline_t net); extern const char* ivl_nature_name(ivl_nature_t net); /* ENUMERATIONS * * Enumerations are a collections of symbolic names and vector * values. The enumeration has a base type, and a list of names and * values. * * FUNCTION SUMMARY * * ivl_enum_names * This is the number of enumeration names in the enum type. * * ivl_enum_name * Get the string name for an item in the enumeration * * ivl_enum_bits * Get the bits (lsb first) of the enumeration value. The width * of the enumeration should match the length of this string. Every * name also has bits that make up the value. * * ivl_enum_signed * Is the base type for the enum signed? * * ivl_enum_type * Get the data-type for the base type that the enum uses. This * will be either IVL_VT_BOOL or IVL_VT_LOGIC * * ivl_enum_width * Return the bit width of the base type for this enum type. * * SEMANTIC NOTES */ extern unsigned ivl_enum_names(ivl_enumtype_t net); extern const char*ivl_enum_name(ivl_enumtype_t net, unsigned idx); extern const char*ivl_enum_bits(ivl_enumtype_t net, unsigned idx); extern int ivl_enum_signed(ivl_enumtype_t net); extern ivl_variable_type_t ivl_enum_type(ivl_enumtype_t net); extern unsigned ivl_enum_width(ivl_enumtype_t net); extern const char*ivl_enum_file(ivl_enumtype_t net); extern unsigned ivl_enum_lineno(ivl_enumtype_t net); /* EVENTS * * Events are a unification of named events and implicit events * generated by the @ statements. * * FUNCTION SUMMARY * * ivl_event_name (Obsolete) * ivl_event_basename * Return the name of the event. The basename is the name within * the scope, as declared by the user or generated by elaboration. * * ivl_event_scope * All events exist within a scope. * * SEMANTICS NOTES * * Named events (i.e. event objects declared by the Verilog * declaration "event foo") are recognized by the fact that they have * no edge sources. The name of the event as given in the Verilog * source is available from the ivl_event_basename function. * * Named events are referenced in trigger statements. * * Named events have file and line number information. * * Edge events are created implicitly by the @(...) Verilog syntax to * watch for the correct type of edge for the functor being * watched. The nodes to watch are collected into groups based on the * type of edge to be watched for on that node. For example, nodes to * be watched for positive edges are accessed via the ivl_event_npos * and ivl_event_pos functions. */ extern const char* ivl_event_name(ivl_event_t net); extern const char* ivl_event_basename(ivl_event_t net); extern ivl_scope_t ivl_event_scope(ivl_event_t net); extern unsigned ivl_event_nany(ivl_event_t net); extern ivl_nexus_t ivl_event_any(ivl_event_t net, unsigned idx); extern unsigned ivl_event_nneg(ivl_event_t net); extern ivl_nexus_t ivl_event_neg(ivl_event_t net, unsigned idx); extern unsigned ivl_event_npos(ivl_event_t net); extern ivl_nexus_t ivl_event_pos(ivl_event_t net, unsigned idx); extern const char*ivl_event_file(ivl_event_t net); extern unsigned ivl_event_lineno(ivl_event_t net); /* EXPRESSIONS * * These methods operate on expression objects from the * design. Expressions mainly exist in behavioral code. The * ivl_expr_type() function returns the type of the expression node, * and the remaining functions access value bits of the expression. * * ivl_expr_signed * This method returns true (!= 0) if the expression node * represents a signed expression. It is possible for sub- * expressions to be unsigned even if a node is signed, but the * IVL core figures all this out for you. At any rate, this method * can be applied to any expression node. * * ivl_expr_sized * This method returns false (0) if the expression node does not * have a defined size. This is unusual, but may happen for * constant expressions. * * ivl_expr_type * Get the type of the expression node. Every expression node has a * type, which can affect how some of the other expression methods * operate on the node * * ivl_expr_value * Get the data type of the expression node. This uses the variable * type enum to express the type of the expression node. * * ivl_expr_net_type * This is used in some cases to carry more advanced type * descriptions. Over the long run, all type information will be * moved into the ivl_type_t type description method. * * ivl_expr_width * This method returns the bit width of the expression at this * node. It can be applied to any expression node, and returns the * *output* width of the expression node. * * ivl_expr_parameter * This function returns the ivl_parameter_t object that represents * this object, or 0 (nil) if it is not a parameter value. This * function allows the code generator to detect the case where the * expression is a parameter. This will normally only return a * non-nil value for constants. * * ivl_expr_opcode * IVL_EX_BINARY and IVL_EX_UNARY expression nodes include an * opcode from this table: * & -- AND * A -- NAND (~&) * X -- XNOR (~^) * * -- Multiply * * SEMANTIC NOTES * * - IVL_EX_ARRAY * This expression type is a special case of the IVL_EX_SIGNAL where * the target is an array (ivl_signal_t with an array_count) but there * is no index expression. This is used only in the special situation * where the array is passed to a system task/function. The function * ivl_expr_signal returns the ivl_signal_t of the array object, and * from that all the properties of the array can be determined. * * - IVL_EX_BINARY * * - IVL_EX_PROPERTY * This expression represents the property select from a class * type, for example "foo.property" where "foo" is a class handle and * "property" is the name of one of the properties of the class. The * ivl_expr_signal function returns the ivl_signal_t for "foo" and the * data_type for the signal will be IVL_VT_CLASS. * * The ivl_signal_net_type(sig) for the "foo" signal will be a class * type and from there you can get access to the type information. * * Elaboration reduces the properties of a class to a vector numbered * from 0 to the number of properties. The ivl_expr_property_idx() * function gets the index of the selected property into the property * table. That number can be passed to ivl_type_prop_*() functions to * get details about the property. * * If the property is an array, then the ivl_expr_oper1() function * returns the canonical expression for accessing the element of the * property. * * - IVL_EX_NEW * This expression takes one or two operands. The first operand, * returned by ivl_expr_oper1() is the number of elements to create * for the dynamic array. The second operand, if present, is returned * by the ivl_expr_oper2() function. If this returns a non-nil * expression, it is the initial value to be written to the elements * of the array. If the expression is an IVL_EX_ARRAY_PATTERN, then * this is the very special case of a list of values to be written to * array elements. * * - IVL_EX_SELECT * This expression takes two operands, oper1 is the expression to * select from, and oper2 is the selection base. The ivl_expr_width * value is the width of the bit/part select. The ivl_expr_oper1 value * is the base of a vector. The compiler has already figured out any * conversion from signal units to vector units, so the result of * ivl_expr_oper1 should range from 0 to ivl_expr_width(). * * This expression is also used to implement string substrings. If the * sub-expression (oper1) is IVL_VT_STRING, then the base expression * (oper2) is a character address, with 0 the first address of the * string, 1 the second, and so on. This is OPPOSITE how a part select * of a string cast to a vector works, to be aware. The size of the * expression is an even multiple of 8, and is 8 times the number of * characters to pick. * * - IVL_EX_SIGNAL * This expression references a signal vector. The ivl_expr_signal * function gets a handle for the signal that is referenced. The * signal may be an array (see the ivl_signal_array_count function) * that is addressed by the expression returned by the ivl_expr_oper1 * function. This expression returns a *canonical* address. The core * compiler already corrected the expression to account for index * bases. * * The ivl_expr_width function returns the vector width of the signal * word. The ivl_expr_value returns the data type of the word. * * Bit and part selects are not done here. The IVL_EX_SELECT * expression does bit/part selects on the word read from the signal. * * - IVL_EX_STRING * This expression refers to a string constant. The ivl_expr_string * function returns a pointer to the first byte of the string. The * compiler has translated it to a "vvp escaped string" which has * quoting and escapes eliminated. The string may contain octal * escapes (\) so that the string text returned by * ivl_expr_string will only contain graphical characters. It is up to * the target to change the escaped \NNN to the proper byte value when * using this string. No other escape sequences will appear in the * string. Quote (") and slash (\) characters will be delivered in * \NNN form. */ extern ivl_expr_type_t ivl_expr_type(ivl_expr_t net); extern ivl_type_t ivl_expr_net_type(ivl_expr_t net); extern ivl_variable_type_t ivl_expr_value(ivl_expr_t net); extern const char*ivl_expr_file(ivl_expr_t net); extern unsigned ivl_expr_lineno(ivl_expr_t net); /* IVL_EX_NUMBER */ extern const char* ivl_expr_bits(ivl_expr_t net); /* IVL_EX_BACCESS */ extern ivl_branch_t ivl_expr_branch(ivl_expr_t net); /* IVL_EX_UFUNC */ extern ivl_scope_t ivl_expr_def(ivl_expr_t net); /* IVL_EX_DELAY */ extern uint64_t ivl_expr_delay_val(ivl_expr_t net); /* IVL_EX_REALNUM */ extern double ivl_expr_dvalue(ivl_expr_t net); /* IVL_EX_ENUMTYPE */ extern ivl_enumtype_t ivl_expr_enumtype(ivl_expr_t net); /* IVL_EX_PROPERTY IVL_EX_SIGNAL IVL_EX_SFUNC IVL_EX_VARIABLE */ extern const char* ivl_expr_name(ivl_expr_t net); /* IVL_EX_BACCESS */ extern ivl_nature_t ivl_expr_nature(ivl_expr_t net); /* IVL_EX_BINARY IVL_EX_UNARY */ extern char ivl_expr_opcode(ivl_expr_t net); /* IVL_EX_BINARY IVL_EX_UNARY, IVL_EX_MEMORY IVL_EX_NEW IVL_EX_TERNARY */ extern ivl_expr_t ivl_expr_oper1(ivl_expr_t net); /* IVL_EX_BINARY IVL_EX_NEW IVL_EX_TERNARY */ extern ivl_expr_t ivl_expr_oper2(ivl_expr_t net); /* IVL_EX_TERNARY */ extern ivl_expr_t ivl_expr_oper3(ivl_expr_t net); /* and expression */ extern ivl_parameter_t ivl_expr_parameter(ivl_expr_t net); /* IVL_EX_ARRAY_PATTERN IVL_EX_CONCAT IVL_EX_UFUNC */ extern ivl_expr_t ivl_expr_parm(ivl_expr_t net, unsigned idx); /* IVL_EX_ARRAY_PATTERN IVL_EX_CONCAT IVL_EX_SFUNC IVL_EX_UFUNC */ extern unsigned ivl_expr_parms(ivl_expr_t net); /* IVL_EX_CONCAT */ extern unsigned ivl_expr_repeat(ivl_expr_t net); /* IVL_EX_SELECT */ extern ivl_select_type_t ivl_expr_sel_type(ivl_expr_t net); /* IVL_EX_EVENT */ extern ivl_event_t ivl_expr_event(ivl_expr_t net); /* IVL_EX_PROPERTY */ extern int ivl_expr_property_idx(ivl_expr_t net); /* IVL_EX_SCOPE */ extern ivl_scope_t ivl_expr_scope(ivl_expr_t net); /* IVL_EX_PROPERTY IVL_EX_SIGNAL */ extern ivl_signal_t ivl_expr_signal(ivl_expr_t net); /* any expression */ extern int ivl_expr_signed(ivl_expr_t net); /* any expression */ extern int ivl_expr_sized(ivl_expr_t net); /* IVL_EX_STRING */ extern const char* ivl_expr_string(ivl_expr_t net); /* IVL_EX_ULONG */ extern unsigned long ivl_expr_uvalue(ivl_expr_t net); /* any expression */ extern unsigned ivl_expr_width(ivl_expr_t net); extern const char* ivl_file_table_item(unsigned idx); extern unsigned ivl_file_table_index(const char *); extern unsigned ivl_file_table_size(void); /* ISLAND * * ivl_island_flag_set * ivl_island_flag_test * Allow the user to test or set a boolean flag associated with the * island. */ extern int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value); extern int ivl_island_flag_test(ivl_island_t net, unsigned flag); extern const char* ivl_logic_file(ivl_net_logic_t net); extern unsigned ivl_logic_lineno(ivl_net_logic_t net); /* LOGIC * These types and functions support manipulation of logic gates. The * ivl_logic_t enumeration identifies the various kinds of gates that * the ivl_net_logic_t can represent. The various functions then * provide access to the bits of information for a given logic device. * * The ivl_net_logic_t nodes are bit-slice devices. That means that * the device may have width (and therefore processes vectors) but * each bit slice of the width is independent. * * ivl_logic_type * This method returns the type of logic gate that the node * represents. The logic type implies the meaning of the various pins. * * ivl_logic_name (obsolete) * This method returns the complete name of the logic gate. Every * gate has a complete name (that includes the scope) even if the * Verilog source doesn't include one. The compiler will choose one * if necessary. * * ivl_logic_basename * This is the name of the gate without the scope part. * * ivl_logic_scope * This is the scope that directly contains the logic device. * * ivl_logic_pins * ivl_logic_pin * Return the nexus for the pin. If two pins are connected * together, then these values are the same. Use the nexus * functions to find other pins that are connected to this nexus. * * ivl_logic_width * This returns the width of the logic array. This does not affect * the number of pins, but implies the width of the vector at each * pin. * * ivl_logic_delay * Logic devices have a delay for each transition (0, 1 and Z). * * ivl_logic_attr (obsolete) * Return the value of a specific attribute, given the key name as * a string. If the key is not defined, then return 0 (null). * * ivl_logic_attr_cnt * ivl_logic_attr_val * These support iterating over logic attributes. The _cnt method * returns the number of attributes attached to the gate, and the * ivl_logic_attr_val returns the value of the attribute. * * SEMANTIC NOTES * The ivl_logic_width applies to all the pins of a logic device. If a * logic device has width, that means that it is actually an array of * logic devices that each process a bit slice of the * inputs/output. That implies that the widths of all the inputs and * the output must be identical. * * The ivl_logic_width and ivl_logic_pins are *not* related. A logic * device has a number of pins that is the number of inputs to a logic * array of identical gates, and the ivl_logic_width, is the width of * the vector into each input pin and out of the output pin. * * The output pin is pin-0. The ivl_logic_driveX functions return the * drive strengths for the output pin-0, and match the drive values * stored in the ivl_nexus_ptr_t object for the pin. * * Logic devices have a logic propagation delay. The delay can be any * expression, although the most common expression is an IVL_EX_NUMBER * for a number value. The expression already includes scaling for the * containing module, so the delay value is always taken to be in * simulation clock ticks. * * If the delay is present, then ivl_logic_delay returns a non-nil * object. If any of the three delays is present, then all three are * present, even if they are all the same. The compiler will translate * shorthands into a complete set of delay expressions. * * The ivl_logic_delay expression will always be an IVL_EX_NUMBER, an * IVL_EX_ULONG, or an IVL_EX_SIGNAL. These expressions can easily be * used in structural contexts. The compiler will take care of * elaborating more complex expressions to nets. * * - IVL_LO_PULLUP/IVL_LO_PULLDOWN * These devices are grouped as logic devices with zero inputs because * the outputs have the same characteristics as other logic * devices. They are special only in that they have zero inputs, and * their drivers typically have strength other than strong. * * - IVL_LO_UDP * User defined primitives (UDPs) are like any other logic devices, in * that they are bit-slice devices. If they have a width, then they * are repeated to accommodate that width, and that implies that the * output and all the inputs must have the same width. * * The IVL_LO_UDP represents instantiations of UDP devices. The * ivl_udp_t describes the implementation. */ extern const char* ivl_logic_name(ivl_net_logic_t net); extern const char* ivl_logic_basename(ivl_net_logic_t net); extern ivl_scope_t ivl_logic_scope(ivl_net_logic_t net); extern ivl_logic_t ivl_logic_type(ivl_net_logic_t net); extern ivl_nexus_t ivl_logic_pin(ivl_net_logic_t net, unsigned pin); extern unsigned ivl_logic_pins(ivl_net_logic_t net); extern ivl_udp_t ivl_logic_udp(ivl_net_logic_t net); extern ivl_expr_t ivl_logic_delay(ivl_net_logic_t net, unsigned transition); extern ivl_drive_t ivl_logic_drive0(ivl_net_logic_t net); extern ivl_drive_t ivl_logic_drive1(ivl_net_logic_t net); extern unsigned ivl_logic_width(ivl_net_logic_t net); extern unsigned ivl_logic_is_cassign(ivl_net_logic_t net); /* DEPRECATED */ extern const char* ivl_logic_attr(ivl_net_logic_t net, const char*key); extern unsigned ivl_logic_attr_cnt(ivl_net_logic_t net); extern ivl_attribute_t ivl_logic_attr_val(ivl_net_logic_t net, unsigned idx); /* UDP * These methods allow access to the ivl_udp_t definition of a UDP. * The UDP definition is accessed through the ivl_logic_udp method of * an ivl_net_logic_t object. * * ivl_udp_name * This returns the name of the definition of the primitive. * * ivl_udp_nin * This is the number of inputs for the UDP definition. * * ivl_udp_rows * ivl_udp_row * These methods give access to the rows that define the table of * the primitive. * * SEMANTIC NOTES * * - Combinational primitives * These devices have no edge dependencies, and have no table entry * for the current input value. These have ivl_udp_sequ return 0 * (false) and the length of each row is the number of inputs plus 1. * The first N characters correspond to the N inputs of the * device. The next character, the last character, is the output for * that row. * * - Sequential primitives * These devices allow edge transitions, and the rows are 1+N+1 * characters long. The first character is the current output, the * next N characters the current input and the last character is the * new output. * * The ivl_udp_init value is only valid if the device is * sequential. It is the initial value for the output of the storage * element. */ extern int ivl_udp_sequ(ivl_udp_t net); extern unsigned ivl_udp_nin(ivl_udp_t net); extern char ivl_udp_init(ivl_udp_t net); extern const char* ivl_udp_row(ivl_udp_t net, unsigned idx); extern unsigned ivl_udp_rows(ivl_udp_t net); extern const char* ivl_udp_name(ivl_udp_t net); extern const char* ivl_udp_file(ivl_udp_t net); extern unsigned ivl_udp_lineno(ivl_udp_t net); extern const char* ivl_udp_port(ivl_udp_t net, unsigned idx); extern const char* ivl_lpm_file(ivl_lpm_t net); extern unsigned ivl_lpm_lineno(ivl_lpm_t net); /* LPM * These functions support access to the properties of LPM * devices. LPM devices are a variety of devices that handle more * complex structural semantics. They are based on EIA LPM standard * devices, but vary to suite the technical situation. * * These are the functions that apply to all LPM devices: * * ivl_lpm_name (Obsolete) * ivl_lpm_basename * Return the name of the device. The name is the name of the * device with the scope part, and the basename is without the scope. * * ivl_lpm_delay * LPM devices have a delay for each transition (0, 1 and Z). * * ivl_lpm_scope * LPM devices exist within a scope. Return the scope that contains * this device. * * ivl_lpm_type * Return the ivl_lpm_type_t of the specific LPM device. * * ivl_lpm_width * Return the width of the LPM device. What this means depends on * the LPM type, but it generally has to do with the width of the * output data path. * * * These functions apply to a subset of the LPM devices, or may have * varying meaning depending on the device: * * ivl_lpm_base * The IVL_LPM_PART objects use this value as the base (first bit) * of the part select. The ivl_lpm_width is the size of the part. * * ivl_lpm_data * Return the input data nexus for device types that have input * vectors. The "idx" parameter selects which data input is selected. * * ivl_lpm_datab (ANACHRONISM) * This is the same as ivl_lpm_data(net,1), in other words the * second data input. Use the ivl_lpm_data method instead. * * ivl_lpm_q * Return the output data nexus for device types that have a single * output vector. This is most devices, it turns out. * * ivl_lpm_selects * This is the size of the select input for a LPM_MUX device, or the * address bus width of an LPM_RAM. * * ivl_lpm_signed * Arithmetic LPM devices may be signed or unsigned if there is a * distinction. For some devices this gives the signedness of the * output, but not all devices. * * ivl_lpm_size * In addition to a width, some devices have a size. The size is * often the number of inputs per out, i.e., the number of inputs * per bit for a MUX. * * ivl_lpm_trigger * SFUNC and UFUNC devices may have a trigger that forces the * function output to be re-evaluated. * * SEMANTIC NOTES * * - Concatenation (IVL_LPM_CONCAT) * These devices take vectors in and combine them to form a single * output the width specified by ivl_lpm_width. * * The ivl_lpm_q nexus is the output from the concatenation. * * The ivl_lpm_data function returns the connections for the inputs to * the concatenation. The ivl_lpm_size function returns the number of * inputs help by the device. * * - Divide (IVL_LPM_DIVIDE) * The divide operators take two inputs and generate an output. The * ivl_lpm_width returns the width of the result. The width of the * inputs are their own. * * - Multiply (IVL_LPM_MULT) * The multiply takes two inputs and generates an output. Unlike other * arithmetic nodes, the width only refers to the output. The inputs * have independent widths, to reflect the arithmetic truth that the * width of a general multiply is the sum of the widths of the * inputs. In fact, the compiler doesn't assure that the widths of the * inputs add up to the width of the output, but the possibility * exists. It is *not* an error for the sum of the input widths to be * more than the width of the output, although the possibility of * overflow exists at run time. * * The inputs are always treated as unsigned. If the expression is * supposed to be signed, elaboration will generate the necessary sign * extension, so the target need not (must not) consider signedness. * * - Power (IVL_LPM_POW) * The power takes two inputs and generates an output. Unlike other * arithmetic nodes, the width only refers to the output. The inputs * have independent widths, to reflect the arithmetic truth that the * width of a general power is the XXXX of the widths of the * inputs. * * Power may be signed. If so, the output should be sign extended * to fill in its result. * * - Part Select (IVL_LPM_PART_VP and IVL_LPM_PART_PV) * There are two part select devices, one that extracts a part from a * vector, and another that writes a part of a vector. The _VP is * Vector-to-Part, and _PV is Part-to-Vector. The _VP form is meant to * model part/bin selects in r-value expressions, where the _PV from * is meant to model part selects in l-value nets. * * In both cases, ivl_lpm_data(0) is the input pin, and ivl_lpm_q is the * output. In the case of the _VP device, the vector is input and the * part is the output. In the case of the _PV device, the part is the * input and the vector is the output. * * If the base of the part select is non-constant, then * ivl_lpm_data(1) is non-nil and is the select, or base, address of * the part. If this pin is nil, then the constant base is used * instead. * * Also in both cases, the width of the device is the width of the * part. In the _VP case, this is obvious as the output nexus has the * part width. In the _PV case, this is a little less obvious, but * still correct. The output being written to the wider vector is * indeed the width of the part, even though it is written to a wider * gate. The target will need to handle this case specially. * * - Bi-directional Part Select (IVL_LPM_PART_BI) * This is not exactly a part select but a bi-directional partial link * of two nexa with different widths. This is used to implement tran * devices and inout ports in certain cases. The device width is the * width of the part. The ivl_lpm_q is the part end, and the * ivl_lpm_data(0) is the non-part end. * * - Comparisons (IVL_LPM_CMP_GT/GE/EQ/NE/EEQ/NEE/EQX/EQZ) * These devices have two inputs, available by the ivl_lpm_data() * function, and one output available by the ivl_lpm_q function. The * output width is always 1, but the ivl_lpm_width() returns the width * of the inputs. Both inputs must have the same width. * * The CMP_GE and CMP_GT nodes may also be signed or unsigned, with * the obvious implications. The widths are matched by the compiler * (so the target need not worry about sign extension) but when doing * magnitude compare, the signedness does matter. In any case, the * result of the compare is always unsigned. * * The EQX and EQZ nodes are wildcard compares, where xz bits (EQX) or * z bits (EQZ) in the data(1) operand are treated as wildcards. no * bits in the data(0) operand are wild. This matches the * SystemVerilog convention for the ==? operator. * * - Mux Device (IVL_LPM_MUX) * The MUX device has a q output, a select input, and a number of data * inputs. The ivl_lpm_q output and the ivl_lpm_data inputs all have * the width from the ivl_lpm_width() method. The Select input, from * ivl_lpm_select, has the width ivl_lpm_selects(). * * The ivl_lpm_data() method returns the inputs of the MUX device. The * ivl_lpm_size() method returns the number of data inputs there * are. All the data inputs have the same width, the width of the * ivl_lpm_q output. The type of the device is divined from the * inputs and the Q. All the types must be exactly the same. * * - D-FlipFlop (IVL_LPM_FF) * This data is an edge sensitive register. The ivl_lpm_q output and * single ivl_lpm_data input are the same with, ivl_lpm_width. This * device carries a vector like other LPM devices. * * - Memory port (IVL_LPM_RAM) (deprecated in favor of IVL_LPM_ARRAY) * These are structural ports into a memory device. They represent * address/data ports of a memory device that the context can hook to * for read or write. Read devices have an ivl_lpm_q output port that * is the data being read. * * The ivl_lpm_memory function returns the ivl_memory_t for the memory * that the port access. The ivl_lpm_width for the port then must * match the ivl_memory_width of the memory device. * * Read or write, the ivl_lpm_select nexus is the address. The * ivl_lpm_selects function returns the vector width of the * address. The range of the address is always from 0 to the memory * size-1 -- the canonical form. It is up to the compiler to generate * offsets to correct for a range declaration. * * Read ports use the ivl_lpm_q as the data output, and write ports * use the ivl_lpm_data(0) as the input. In either case the width of * the vector matches the width of the memory itself. * * - Reduction operators (IVL_LPM_RE_*) * These devices have one input, a vector, and generate a single bit * result. The width from the ivl_lpm_width is the width of the input * vector. * * - Repeat Node (IVL_LPM_REPEAT) * This node takes as input a single vector, and outputs a single * vector. The ivl_lpm_width if this node is the width of the *output* * vector. The ivl_lpm_size() returns the number of times the input is * repeated to get the desired width. The ivl core assures that the * input vector is exactly ivl_lpm_width() / ivl_lpm_size() bits. * * - Sign Extend (IVL_LPM_SIGN_EXT) * This node takes a single input and generates a single output. The * input must be signed, and the output will be a vector sign extended * to the desired width. The ivl_lpm_width() value is the output * width, the input will be whatever it wants to be. * * - Shifts (IVL_LPM_SHIFTL/SHIFTR) * This node takes two inputs, a vector and a shift distance. The * ivl_lpm_data(0) nexus is the vector input, and the ivl_lpm_data(1) * the shift distance. The vector input is the same width as the * output, but the distance has its own width. * * The ivl_lpm_signed() flag means for IVL_LPM_SHIFTR that the right * shift is *signed*. For SHIFTL, then signed-ness is meaningless. * * - System function call (IVL_LPM_SFUNC) * This device represents a netlist call to a system function. The * inputs to the device are passed to a system function, and the * result is sent via the output. The ivl_lpm_q function returns the * output nexus. * * The ivl_lpm_size function returns the number of arguments, and the * ivl_lpm_data(net,N) returns the nexa for the argument. * * The ivl_lpm_string(net) function returns the name of the system * function (i.e. "$display") that was found in the source code. The * compiler does little checking of that name. * * The ivl_lpm_trigger function retrieves the trigger event that * indicates when the system function needs to be re-evaluated. If * there is no trigger event, the system function only needs to be * re-evaluated when a change is detected on its input ports. * * - User Function Call (IVL_LPM_UFUNC) * This device is special as it represents a call to a user defined * function (behavioral code) within a netlist. The inputs to the * function are connected to the net, as is the output. * * The function definition is associated with a scope, and the * ivl_lpm_define function returns the scope that is that definition. * See the ivl_scope_* functions for how to get at the actual * definition. * * As with many LPM nodes, the ivl_lpm_q function returns the nexus * for the signal function return value. The width of this nexus must * exactly match the width of the device from ivl_lpm_width. * * The ivl_lpm_data function retrieves the nexa for all the input * ports. The ivl_lpm_size function returns the number of inputs for * the device, and the ivl_lpm_data() function index argument selects * the port to retrieve. Each port is sized independently. * * The ivl_lpm_trigger function retrieves the trigger event that * indicates when the user function needs to be re-evaluated. If * there is no trigger event, the user function only needs to be * re-evaluated when a change is detected on its input ports. */ extern const char* ivl_lpm_name(ivl_lpm_t net); /* (Obsolete) */ extern const char* ivl_lpm_basename(ivl_lpm_t net); extern ivl_expr_t ivl_lpm_delay(ivl_lpm_t net, unsigned transition); extern ivl_scope_t ivl_lpm_scope(ivl_lpm_t net); extern int ivl_lpm_signed(ivl_lpm_t net); extern ivl_lpm_type_t ivl_lpm_type(ivl_lpm_t net); extern unsigned ivl_lpm_width(ivl_lpm_t net); extern ivl_event_t ivl_lpm_trigger(ivl_lpm_t net); /* IVL_LPM_FF */ extern ivl_nexus_t ivl_lpm_async_clr(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_async_set(ivl_lpm_t net); extern ivl_expr_t ivl_lpm_aset_value(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_sync_clr(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_sync_set(ivl_lpm_t net); extern ivl_expr_t ivl_lpm_sset_value(ivl_lpm_t net); /* IVL_LPM_ARRAY */ extern ivl_signal_t ivl_lpm_array(ivl_lpm_t net); /* IVL_LPM_PART IVL_LPM_SUBSTITUTE */ extern unsigned ivl_lpm_base(ivl_lpm_t net); /* IVL_LPM_FF */ extern unsigned ivl_lpm_negedge(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); /* IVL_LPM_UFUNC */ extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net); /* IVL_LPM_FF */ extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net); /* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE */ extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_POW IVL_LPM_SUB IVL_LPM_CMP_EQ IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */ extern ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_MULT IVL_LPM_PART IVL_LPM_POW IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE */ extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); /* IVL_LPM_MUX */ extern unsigned ivl_lpm_selects(ivl_lpm_t net); /* IVL_LPM_MUX */ extern ivl_nexus_t ivl_lpm_select(ivl_lpm_t net); /* IVL_LPM_CONCAT IVL_LPM_MUX IVL_LPM_REPEAT IVL_LPM_UFUNC */ extern unsigned ivl_lpm_size(ivl_lpm_t net); /* IVL_LPM_SFUNC */ extern const char*ivl_lpm_string(ivl_lpm_t net); /* LVAL * The l-values of assignments are concatenation of ivl_lval_t * objects. Each lvi_lval_t object is an assignment to a var or a * memory, through a bit select, part select or word select. * * Var lvals are things like assignments to a part select or a bit * select. Assignment to the whole variable is a special case of a * part select, as is a bit select with a constant expression. * * ivl_lval_width * The width of a vector that this lval can receive. This accounts * for the local part selecting I might to in the lval object, as * well as the target object width. * * ivl_lval_mux (* obsolete *) * * ivl_lval_nest * If the l-value is an object more complex than a variable, then * this returns the nested l-value (and ivl_lval_sig==0). * * ivl_lval_sig * If the l-value is a variable, this method returns the signal * object that is the target of the assign. * * ivl_lval_part_off * The part select of the signal is based here. This is the * canonical index of bit-0 of the part select. The return value is * an ivl_expr_t. If the return value is nil, then take the offset * as zero. Otherwise, evaluate the expression to get the offset. * * ivl_lval_idx * If the l-value is a memory, this method returns an * ivl_expr_t that represents the index expression. Otherwise, it * returns 0. * * ivl_lval_property_idx * If the l-value is a class object, this is the name of a property * to select from the object. If this property is not present (<0) * then the l-value represents the class object itself. * * SEMANTIC NOTES * The ivl_lval_width is not necessarily the same as the width of the * signal or memory word it represents. It is the width of the vector * it receives and assigns. This may be less than the width of the * signal (or even 1) if only a part of the l-value signal is to be * assigned. * * The ivl_lval_part_off is the canonical base of a part or * bit select. * * - Array words * If the l-value is an array, then ivl_lval_idx function will return * an expression that calculates the address of the array word. If * the referenced signal has more than one word, this expression must * be present. If the signal has exactly one word (it is not an array) * then the ivl_lval_idx expression must *not* be present. * * For array words, the ivl_lval_width is the width of the word. * * - Arrayed properties * If the l-value is a class property, then the ivl_lval_idx function * will return an expression if the property is in fact arrayed. The * expression is the canonical index for elements in the property. */ extern unsigned ivl_lval_width(ivl_lval_t net); extern ivl_expr_t ivl_lval_mux(ivl_lval_t net) __attribute__((deprecated)); /* XXXX Obsolete? */ extern ivl_expr_t ivl_lval_idx(ivl_lval_t net); extern ivl_expr_t ivl_lval_part_off(ivl_lval_t net); extern ivl_select_type_t ivl_lval_sel_type(ivl_lval_t net); extern int ivl_lval_property_idx(ivl_lval_t net); extern ivl_signal_t ivl_lval_sig(ivl_lval_t net); extern ivl_lval_t ivl_lval_nest(ivl_lval_t net); /* NEXUS * connections of signals and nodes is handled by single-bit * nexus. These functions manage the ivl_nexus_t object. They also * manage the ivl_nexus_ptr_t objects that are closely related to the * nexus. * * ivl_nexus_name * Each nexus is given a name, typically derived from the signals * connected to it, but completely made up if need be. The name of * every nexus is unique. * * ivl_nexus_ptrs * This function returns the number of pointers that are held by * the nexus. It should always return at least 1. The pointer * proper is accessed by index. * * ivl_nexus_ptr * Return a nexus pointer given the nexus and an index. * * ivl_nexus_set_private * ivl_nexus_get_private * The target module often needs to associate data with a nexus for * later use when the nexus is encountered associated with a * device. These methods allow the code generator to store to or * retrieve from a nexus a void* of private data. This pointer is * guaranteed to be 0 before the target module is invoked. * * Once an ivl_nexus_ptr_t is selected by the ivl_nexus_ptr method, * the properties of the pointer can be accessed by the following * methods: * * ivl_nexus_ptr_pin * This returns the pin number of the device where this nexus * points. It is the bit within the signal or logic device that is * connected to the nexus. * * If the target is an LPM device, then this value is zero, and it * is up to the application to find the pin that refers to this * nexus. The problem is that LPM devices do not have a pinout per * se, the pins all have specific names. * * ivl_nexus_ptr_con * If this is a pointer to a magic constant device, then this * returns the net_const object. * * ivl_nexus_ptr_drive0 * ivl_nexus_ptr_drive1 * These are the 0 and 1 strength values for the devices. For most * devices, these values are fixed by the description in the * original source, with the default as IVL_DR_STRONG. For pins * that are input only, drive0 and drive1 are both IVL_DR_HiZ. * * The strength of strength-aware devices (such as nmos devices) * does not really matter, as long as the output is not * IVL_DR_HiZ. Testing for HiZ drivers is how code generators * detect inputs. * * ivl_nexus_ptr_log * If the target object is an ivl_net_logic_t, this method returns * the object. Otherwise, this method returns 0. * * ivl_nexus_ptr_lpm * If the target object is an ivl_lpm_t, this method returns the * object. Otherwise, this method returns 0. * * ivl_nexus_ptr_sig * If the target object is an ivl_signal_t, this method returns the * object. If the target is not a signal, this method returns 0. * * SEMANTIC NOTES * All the device pins that connect to a nexus have the same * type. That means, for example, that vector pins have the same * width. The compiler will insure this is so. */ extern const char* ivl_nexus_name(ivl_nexus_t net) __attribute__((deprecated)); extern unsigned ivl_nexus_ptrs(ivl_nexus_t net); extern ivl_nexus_ptr_t ivl_nexus_ptr(ivl_nexus_t net, unsigned idx); extern void ivl_nexus_set_private(ivl_nexus_t net, void*data); extern void* ivl_nexus_get_private(ivl_nexus_t net); extern ivl_drive_t ivl_nexus_ptr_drive0(ivl_nexus_ptr_t net); extern ivl_drive_t ivl_nexus_ptr_drive1(ivl_nexus_ptr_t net); extern unsigned ivl_nexus_ptr_pin(ivl_nexus_ptr_t net); extern ivl_branch_t ivl_nexus_ptr_branch(ivl_nexus_ptr_t net); extern ivl_net_const_t ivl_nexus_ptr_con(ivl_nexus_ptr_t net); extern ivl_net_logic_t ivl_nexus_ptr_log(ivl_nexus_ptr_t net); extern ivl_lpm_t ivl_nexus_ptr_lpm(ivl_nexus_ptr_t net); extern ivl_switch_t ivl_nexus_ptr_switch(ivl_nexus_ptr_t net); extern ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net); /* PARAMETER * Parameters are named constants associated with a scope. The user * may set in the Verilog source the value of parameters, and that * leads to ivl_parameter_t objects contained in the ivl_scope_t * objects. * * Parameters are essentially named constants. These constant values * can be accessed by looking at the scope (using ivl_scope_param) or * they can be discovered when they are used, via the * ivl_expr_parameter function. The fact that a constant has a name * (i.e. is a parameter) does not otherwise impose on the value or * interpretation of the constant expression so far as ivl_target is * concerned. The target may need this information, or may choose to * completely ignore it. * * ivl_parameter_basename * return the name of the parameter. * * ivl_parameter_scope * Return the scope of the parameter. The parameter name is only * unique within its scope. * * ivl_parameter_expr * Return the value of the parameter. This should be a simple * constant expression, an IVL_EX_STRING or IVL_EX_NUMBER. * * ivl_parameter_msb * ivl_parameter_lsb * Returns the MSB and LSB for the parameter. For a parameter without * a range the value is zero based and the width of the expression is * used to determine the MSB. * * ivl_parameter_width * return |MSB - LSB| + 1 * * ivl_parameter_signed * Returns if the parameter was declared to be signed. * * ivl_parameter_local * Return whether parameter was local (localparam, implicit genvar etc) * or not. * * ivl_parameter_file * ivl_parameter_lineno * Returns the file and line where this parameter is defined */ extern const char* ivl_parameter_basename(ivl_parameter_t net); extern ivl_scope_t ivl_parameter_scope(ivl_parameter_t net); extern ivl_expr_t ivl_parameter_expr(ivl_parameter_t net); extern int ivl_parameter_msb(ivl_parameter_t net); extern int ivl_parameter_lsb(ivl_parameter_t net); extern unsigned ivl_parameter_width(ivl_parameter_t net); extern int ivl_parameter_signed(ivl_parameter_t net); extern int ivl_parameter_local(ivl_parameter_t net); extern const char* ivl_parameter_file(ivl_parameter_t net); extern unsigned ivl_parameter_lineno(ivl_parameter_t net); /* SCOPE * Scopes of various sort have these properties. Use these methods to * access them. Scopes come to exist in the elaborated design * generally when a module is instantiated, though they also come from * named blocks, tasks and functions. * * - module instances (IVL_SCT_MODULE) * A module instance scope may contain events, logic gates, lpm * nodes, signals, and possibly children. The children are further * instances, or function/task scopes. Module instances do *not* * contain a definition. * * - function scopes (IVL_SCT_FUNCTION) * These scopes represent functions. A function may not be a root, * so it is contained within a module instance scope. A function is * required to have a definition (in the form of a statement) and a * signal (IVL_SIG_REG) that is its return value. * * A single function scope is created each time the module with the * definition is instantiated. * * * - task scopes (IVL_SCT_TASK) * [...] * * ivl_scope_attr_cnt * ivl_scope_attr_val * A scope may have attributes attached to it. These functions * allow the target to access the attributes values. * * ivl_scope_children * A scope may in turn contain other scopes. This method iterates * through all the child scopes of a given scope. If the function * returns any value other than 0, the iteration stops and the * method returns that value. Otherwise, iteration continues until * the children run out. * * If the scope has no children, this method will return 0 and * otherwise do nothing. * * ivl_scope_childs * ivl_scope_child * This is an alternative way of getting at the childs scopes of a * given scope. * * ivl_scope_def * Task definition scopes carry a task definition, in the form of * a statement. This method accesses that definition. The * ivl_scope_def function must return a statement for scopes that * are type FUNCTION or TASK, and must return nil otherwise. * * ivl_scope_def_file * ivl_scope_def_lineno * Returns the file and line where this scope is defined. * * ivl_scope_enumerate * ivl_scope_enumerates * Scopes have 0 or more enumeration types in them. * * ivl_scope_event * ivl_scope_events * Scopes have 0 or more event objects in them. * * ivl_scope_file * ivl_scope_lineno * Returns the instantiation file and line for this scope. * * ivl_scope_is_auto * Is the task or function declared to be automatic? * * ivl_scope_is_cell * Is the module defined to be a cell? * * ivl_scope_var * ivl_scope_vars * REMOVED * * ivl_scope_log * ivl_scope_logs * Scopes have 0 or more logic devices in them. A logic device is * represented by ivl_logic_t. * * ivl_scope_lpm * ivl_scope_lpms * Scopes have 0 or more LPM devices in them. These functions access * those devices. * * ivl_scope_name * ivl_scope_basename * Every scope has a hierarchical name. This name is also a prefix * of all the names of objects contained within the scope. The * ivl_scope_basename is the name of the scope without the included * hierarchy. * * ivl_scope_param * ivl_scope_params * A scope has zero or more named parameters. These parameters have * a name and an expression value. * * ivl_scope_parent * If this is a non-root scope, then the parent is the scope that * contains this scope. Otherwise, the parent is nil. * * ivl_scope_port * ivl_scope_ports * Scopes that are functions or tasks have ports defined by * signals. These methods access the ports by name. * * If this scope represents a function, then the ports list * includes the return value, as port 0. The remaining ports are * the input ports in order. * * ivl_scope_sig * ivl_scope_sigs * Scopes have 0 or more signals in them. These signals are * anything that can become and ivl_signal_t, include synthetic * signals generated by the compiler. * * ivl_scope_time_precision * Scopes have their own intrinsic time precision, typically from * the timescale compiler directive. This method returns the * precision as a signed power of 10 value. * * ivl_scope_time_units * Scopes have their own intrinsic time units, typically from the * timescale compiler directive. This method returns the units as a * signed power of 10 value. * * ivl_scope_type * ivl_scope_tname * Scopes have a type and a type name. For example, if a scope is * an instance of module foo, its type is IVL_SCT_MODULE and its * type name is "foo". This is different from the instance name * returned by ivl_scope_name above. */ extern unsigned ivl_scope_attr_cnt(ivl_scope_t net); extern ivl_attribute_t ivl_scope_attr_val(ivl_scope_t net, unsigned idx); extern int ivl_scope_children(ivl_scope_t net, ivl_scope_f func, void*cd); extern ivl_statement_t ivl_scope_def(ivl_scope_t net); extern const char* ivl_scope_def_file(ivl_scope_t net); extern unsigned ivl_scope_def_lineno(ivl_scope_t net); extern size_t ivl_scope_childs(ivl_scope_t net); extern ivl_scope_t ivl_scope_child(ivl_scope_t net, size_t idx); extern unsigned ivl_scope_classes(ivl_scope_t net); extern ivl_type_t ivl_scope_class(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_enumerates(ivl_scope_t net); extern ivl_enumtype_t ivl_scope_enumerate(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_events(ivl_scope_t net); extern ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx); extern const char* ivl_scope_file(ivl_scope_t net); extern unsigned ivl_scope_is_auto(ivl_scope_t net); extern unsigned ivl_scope_is_cell(ivl_scope_t net); extern unsigned ivl_scope_lineno(ivl_scope_t net); extern unsigned ivl_scope_logs(ivl_scope_t net); extern ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_lpms(ivl_scope_t net); extern ivl_lpm_t ivl_scope_lpm(ivl_scope_t, unsigned idx); extern const char* ivl_scope_name(ivl_scope_t net); extern const char* ivl_scope_basename(ivl_scope_t net); extern unsigned ivl_scope_params(ivl_scope_t net); extern ivl_parameter_t ivl_scope_param(ivl_scope_t net, unsigned idx); extern ivl_scope_t ivl_scope_parent(ivl_scope_t net); extern unsigned ivl_scope_mod_module_ports(ivl_scope_t net); extern const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx ); extern ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx ); extern unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx ); extern unsigned ivl_scope_ports(ivl_scope_t net); extern ivl_signal_t ivl_scope_port(ivl_scope_t net, unsigned idx); extern ivl_nexus_t ivl_scope_mod_port(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_sigs(ivl_scope_t net); extern ivl_signal_t ivl_scope_sig(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_switches(ivl_scope_t net); extern ivl_switch_t ivl_scope_switch(ivl_scope_t net, unsigned idx); extern ivl_scope_type_t ivl_scope_type(ivl_scope_t net); extern const char* ivl_scope_tname(ivl_scope_t net); extern int ivl_scope_time_precision(ivl_scope_t net); extern int ivl_scope_time_units(ivl_scope_t net); /* SIGNALS * Signals are named things in the Verilog source, like wires and * regs, and also named things that are created as temporaries during * certain elaboration or optimization steps. A signal may also be a * port of a module or task. * * Signals have a name (obviously) and types. A signal may also be * signed or unsigned. * * ivl_signal_nex * This is the nexus of the signal. This is used for managing * connections to the rest of the net. There is exactly one pin for * each word of a signal. Each word may in turn be a vector. The * word address is the zero-based index for the word. It is up to * the context to translate different bases to the canonical address. * * ivl_signal_array_base * ivl_signal_array_count * ivl_signal_array_addr_swapped * The signal may be arrayed. If so, the array_count is >1. Each * word of the array has its own nexus. The array_base is the * address in the Verilog source for the canonical zero word. This * may be negative, positive or zero. The array addresses may be * reversed/swapped. * * Note that arraying of the signal into words is distinct from the * vectors. The width of a signal is the width of a WORD. * * ivl_signal_dimensions * The signal may be an array (of vectors) in which case this * function returns >0, the number of dimensions of the array. * * ivl_signal_discipline * If the signal has been declared with a domain (Verilog-AMS) then * this function will return a non-nil ivl_discipline_t. * * ivl_signal_msb (deprecated) * ivl_signal_lsb (deprecated) * ivl_signal_packed_dimensions * ivl_signal_packed_msb * ivl_signal_packed_lsb * ivl_signal_width * These functions return the msb and lsb packed indices. The * packed dimensions are declared differently from array * dimensions, like so: * reg [4:1][7:0] sig... * which has two packed dimensions. The [4:1] dimension is the * first, and so forth. If the signal is a scalar, it has 0 * dimension. * * The ivl_signal_msb/ivl_signal_lsb functions are deprecated * versions that only work with variables that have less than two * dimensions. They will return msb==lsb==0 for scalars. * * ivl_signal_port * If the signal is a port to a module, this function returns the * port direction. If the signal is not a port, it returns * IVL_SIP_NONE. * * ivl_signal_signed * A signal, which is a vector, may be signed. In Verilog 2000, any * net or variable may be signed. This function returns true if the * signal is signed. * * ivl_signal_local * A signal that was generated by the compiler as a place holder is * marked as local. * * ivl_signal_forced_net * Return whether the signal is a net that is the subject of a force * statement. * * ivl_signal_type * Return the type of the signal, i.e., reg, wire, tri0, etc. * * ivl_signal_data_type * Return the data type of the signal, i.e. logic, real, bool, * etc. All the signals connected to a nexus should have the same * data type * * ivl_signal_npath * ivl_signal_path * This function returns the delay path object for the signal. The * delay path has this signal as the output, the source is attached * to the delay path itself. * * ivl_signal_name (DEPRECATED) * This function returns the fully scoped hierarchical name for the * signal. The name refers to the entire vector that is the signal. * * NOTE: This function is deprecated. The hierarchical name is too * vague a construct when escaped names can have . characters in * them. Do no use this function in new code, it will disappear. * * ivl_signal_basename * This function returns the name of the signal, without the scope * information. This is the tail of the signal name. Since Verilog * has an escape syntax, this name can contain any ASCII * characters, except NULL or white space. The leading \ and * trailing ' ' of escaped names in Verilog source are not part of * the name, so not included here. * * ivl_signal_attr * Icarus Verilog supports attaching attributes to signals, with * the attribute value (a string) associated with a key. This * function returns the attribute value for the given key. If the * key does not exist, the function returns 0. * * ivl_signal_file * ivl_signal_lineno * Returns the file and line where this signal is defined. */ extern ivl_scope_t ivl_signal_scope(ivl_signal_t net); extern ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word); extern int ivl_signal_array_base(ivl_signal_t net); extern unsigned ivl_signal_array_count(ivl_signal_t net); extern unsigned ivl_signal_array_addr_swapped(ivl_signal_t net); extern unsigned ivl_signal_dimensions(ivl_signal_t net); extern ivl_discipline_t ivl_signal_discipline(ivl_signal_t net); extern unsigned ivl_signal_packed_dimensions(ivl_signal_t net); extern int ivl_signal_packed_msb(ivl_signal_t net, unsigned dim); extern int ivl_signal_packed_lsb(ivl_signal_t net, unsigned dim); extern int ivl_signal_msb(ivl_signal_t net) __attribute__((deprecated)); extern int ivl_signal_lsb(ivl_signal_t net) __attribute__((deprecated)); extern unsigned ivl_signal_width(ivl_signal_t net); extern ivl_signal_port_t ivl_signal_port(ivl_signal_t net); extern int ivl_signal_module_port_index(ivl_signal_t net); extern int ivl_signal_signed(ivl_signal_t net); extern int ivl_signal_integer(ivl_signal_t net); extern int ivl_signal_local(ivl_signal_t net); extern unsigned ivl_signal_forced_net(ivl_signal_t net); extern unsigned ivl_signal_npath(ivl_signal_t net); extern ivl_delaypath_t ivl_signal_path(ivl_signal_t net, unsigned idx); extern ivl_signal_type_t ivl_signal_type(ivl_signal_t net); extern ivl_variable_type_t ivl_signal_data_type(ivl_signal_t net); extern ivl_type_t ivl_signal_net_type(ivl_signal_t net); extern const char* ivl_signal_name(ivl_signal_t net); extern const char* ivl_signal_basename(ivl_signal_t net); extern const char* ivl_signal_attr(ivl_signal_t net, const char*key); extern const char* ivl_signal_file(ivl_signal_t net); extern unsigned ivl_signal_lineno(ivl_signal_t net); extern unsigned ivl_signal_attr_cnt(ivl_signal_t net); extern ivl_attribute_t ivl_signal_attr_val(ivl_signal_t net, unsigned idx); /* ivl_nexus_t ivl_signal_pin(ivl_signal_t net, unsigned idx); */ /* unsigned ivl_signal_pins(ivl_signal_t net); */ /* * These functions get information about a process. A process is * an initial or always block within the original Verilog source, that * is translated into a type and a single statement. (The statement * may be a compound statement.) * * ivl_process_type * ivl_process_analog * The ivl_process_type function returns the type of the process, * an "initial" or "always" statement. The ivl_process_analog * returns true if the process is analog. * * ivl_process_scope * A process is placed in a scope. The statement within the process * operates within the scope of the process unless there are calls * outside the scope. * * The ivl_process_stmt function gets the statement that forms the * process. See the statement related functions for how to manipulate * statements. * * Processes can have attributes attached to them. the attr_cnt and * attr_val methods return those attributes. */ extern ivl_process_type_t ivl_process_type(ivl_process_t net); extern int ivl_process_analog(ivl_process_t net); extern ivl_scope_t ivl_process_scope(ivl_process_t net); extern ivl_statement_t ivl_process_stmt(ivl_process_t net); extern unsigned ivl_process_attr_cnt(ivl_process_t net); extern ivl_attribute_t ivl_process_attr_val(ivl_process_t net, unsigned idx); extern const char* ivl_process_file(ivl_process_t net); extern unsigned ivl_process_lineno(ivl_process_t net); /* * These functions manage statements of various type. This includes * all the different kinds of statements (as enumerated in * ivl_statement_type_t) that might occur in behavioral code. * * The ivl_statement_type() function returns the type code for the * statement. This is the major type, and implies which of the later * functions are applicable to the statement. * * the ivl_statement_file() and _lineno() functions return the source * file and line number of the statement in the Verilog source. This * information is useful for diagnostic information. */ extern ivl_statement_type_t ivl_statement_type(ivl_statement_t net); extern const char* ivl_stmt_file(ivl_statement_t net); extern unsigned ivl_stmt_lineno(ivl_statement_t net); /* * The following functions retrieve specific single values from the * statement. These values are the bits of data and parameters that * make up the statement. Many of these functions apply to more than * one type of statement, so the comment in front of them tells which * statement types can be passed to the function. * * FUNCTION SUMMARY: * * ivl_stmt_block_scope * If the block is named, then there is a scope associated with * this. The code generator may need to know this in order to * handle disable statements. * * ivl_stmt_events * ivl_stmt_nevent * Statements that have event arguments (TRIGGER and WAIT) make * those event objects available through these methods. * * ivl_stmt_lval * ivl_stmt_lvals * Return the number of l-values for an assignment statement, or * the specific l-value. If there is more than 1 l-value, then the * l-values are presumed to be vector values concatenated together * from msb (idx==0) to lsb. * * ivl_stmt_rval * Return the rval expression of the assignment. This is the value * that is to be calculated and assigned to the l-value in all the * assignment statements. * * ivl_stmt_sub_stmt * Some statements contain a single, subordinate statement. An * example is the IVL_ST_WAIT, which contains the statement to be * executed after the wait completes. This method retrieves that * sub-statement. * * SEMANTIC NOTES: * * - Assignments: IVL_ST_ASSIGN, IVL_ST_ASSIGN_NB, IVL_CASSIGN, IVL_ST_FORCE * * The assignments support ivl_stmt_rval to get the r-value expression * that is to be assign to the l-value, and ivl_stmt_lval[s] to get * the l-value that receives the value. The compiler has already made * sure that the types (l-value and r-value) are compatible. * * If the l-value is a vector, then the compiler also makes sure the * expression width of the r-values matches. It handles padding or * operator sizing as needed to get the width exactly right. * * The blocking and non-blocking assignments may also have an internal * delay. These are of the form "lval = # rval;" and is * the internal delay expression. (It is internal because it is inside * the statement.) The ivl_stmt_delay_expr function returns the * expression for the delay, or nil if there is no delay expression. * * The blocking assignment (IVL_ST_ASSIGN) may have an associated * opcode, that can be extracted from ivl_stmt_opcode(). This opcode * is the compressed operator used it statements like this: * foo += * The ivl_stmt_opcode() returns null (0) if this is not a compressed * assignment statement. * * - IVL_ST_CASSIGN * This reflects a procedural continuous assignment to an l-value. The * l-value is the same as any other assignment (use ivl_stmt_lval). * * The value to be assigned is an ivl_expr_t retrieved by the * ivl_stmt_rval function. The run time is expected to calculate the * value of the expression at the assignment, then continuous assign * that constant value. If the expression is non-constant, the code * generator is supposed to know what to do about that, too. * * - IVL_ST_CONTRIB * This is an analog contribution statement. The ivl_stmt_lexp * function returns the l-value expression which is guaranteed to be a * branch access expression. The ivl_stmt_rval returns the r-value * expression for the assignment. * * - IVL_ST_DELAY, IVL_ST_DELAYX * These statement types are delay statements. They are a way to * attach a delay to a statement. The ivl_stmt_sub_stmt() function * gets the statement to be executed after the delay. If this is * IVL_ST_DELAY, then the ivl_stmt_delay_val function gets the * constant delay. If this is IVL_ST_DELAYX, then the * ivl_stmt_delay_expr gets the expression of the delay. In this case, * the expression is not necessarily constant. * * Whether constant or calculated, the resulting delay is in units of * simulation ticks. The compiler has already taken care of converting * the delay to the time scale/precision of the scope. * * - IVL_ST_FORCE * This is very much like IVL_ST_CASSIGN, but adds that l-values can * include nets (tri, wire, etc). Memory words are restricted from * force l-values, and also non-constant bit or part selects. The * compiler will assure these constraints are met. * * - IVL_ST_TRIGGER * This represents the "-> name" statement that sends a trigger to a * named event. The ivl_stmt_nevent function should always return 1, * and the ivl_stmt_events(net,0) function returns the target event, * as an ivl_event_t. The only behavior of this statement is to send a * "trigger" to the target event. * * - IVL_ST_WAIT * This is the edge sensitive wait (for event) statement. The * statement contains an array of events that are to be tested, and a * single statement that is to be executed when any of the array of * events triggers. * * the ivl_stmt_events function accesses the array of events to wait * for, and the ivl_stmt_sub_stmt function gets the sub-statement, * which may be null, that is to be executed when an event * triggers. The statement waits even if the sub-statement is nul. */ /* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */ extern unsigned ivl_stmt_block_count(ivl_statement_t net); /* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */ extern ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net); /* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */ extern ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, unsigned i); /* IVL_ST_UTASK IVL_ST_DISABLE */ extern ivl_scope_t ivl_stmt_call(ivl_statement_t net); /* IVL_ST_CASE,IVL_ST_CASER,IVL_ST_CASEX,IVL_ST_CASEZ */ extern unsigned ivl_stmt_case_count(ivl_statement_t net); /* IVL_ST_CASE,IVL_ST_CASER,IVL_ST_CASEX,IVL_ST_CASEZ */ extern ivl_expr_t ivl_stmt_case_expr(ivl_statement_t net, unsigned i); /* IVL_ST_CASE,IVL_ST_CASER,IVL_ST_CASEX,IVL_ST_CASEZ */ extern ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned i); /* IVL_ST_CONDIT IVL_ST_CASE IVL_ST_REPEAT IVL_ST_WHILE */ extern ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net); /* IVL_ST_CONDIT */ extern ivl_statement_t ivl_stmt_cond_false(ivl_statement_t net); /* IVL_ST_CONDIT */ extern ivl_statement_t ivl_stmt_cond_true(ivl_statement_t net); /* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_DELAYX */ extern ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net); /* IVL_ST_DELAY */ extern uint64_t ivl_stmt_delay_val(ivl_statement_t net); /* IVL_ST_WAIT IVL_ST_TRIGGER */ extern unsigned ivl_stmt_nevent(ivl_statement_t net); extern ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx); /* IVL_ST_CONTRIB */ extern ivl_expr_t ivl_stmt_lexp(ivl_statement_t net); /* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN IVL_ST_DEASSIGN IVL_ST_FORCE IVL_ST_RELEASE */ extern ivl_lval_t ivl_stmt_lval(ivl_statement_t net, unsigned idx); /* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN IVL_ST_DEASSIGN IVL_ST_FORCE IVL_ST_RELEASE */ extern unsigned ivl_stmt_lvals(ivl_statement_t net); /* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN */ extern unsigned ivl_stmt_lwidth(ivl_statement_t net); /* IVL_ST_STASK */ extern const char* ivl_stmt_name(ivl_statement_t net); /* IVL_ST_ASSIGN */ extern char ivl_stmt_opcode(ivl_statement_t net); /* IVL_ST_STASK */ extern ivl_expr_t ivl_stmt_parm(ivl_statement_t net, unsigned idx); /* IVL_ST_STASK */ extern unsigned ivl_stmt_parm_count(ivl_statement_t net); /* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN IVL_ST_CONTRIB IVL_ST_FORCE */ extern ivl_expr_t ivl_stmt_rval(ivl_statement_t net); /* IVL_ST_STASK */ extern ivl_sfunc_as_task_t ivl_stmt_sfunc_as_task(ivl_statement_t net); /* IVL_ST_DELAY, IVL_ST_DELAYX, IVL_ST_FOREVER, IVL_ST_REPEAT IVL_ST_WAIT, IVL_ST_WHILE */ extern ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net); /* SWITCHES * * The switches represent the tran devices in the design. * * FUNCTION SUMMARY * * ivl_switch_type * Return the enumerated value that is the type of the switch. * * ivl_switch_basename * This is the name given to the device in the source code. * * ivl_switch_a * ivl_switch_b * The a and b ports are the two ports of the switch. * * ivl_switch_enable * If the device has an enable (tranifX) then this is the enable * port. * * SEMANTIC NOTES * The a/b ports can be any type, but the types must exactly * match, including vector widths. The enable must be a scalar. * * The IVL_SW_TRAN_VP is an exception to the above. In this case, * the B side may be a different size, and the a side will have a * a fixed width. The unused bits are padded to Z on the A side. */ extern ivl_switch_type_t ivl_switch_type(ivl_switch_t net); extern ivl_scope_t ivl_switch_scope(ivl_switch_t net); extern const char*ivl_switch_basename(ivl_switch_t net); extern ivl_nexus_t ivl_switch_a(ivl_switch_t net); extern ivl_nexus_t ivl_switch_b(ivl_switch_t net); extern ivl_nexus_t ivl_switch_enable(ivl_switch_t net); extern ivl_island_t ivl_switch_island(ivl_switch_t net); /* These are only support for IVL_SW_TRAN_VP switches. */ extern unsigned ivl_switch_width(ivl_switch_t net); extern unsigned ivl_switch_part(ivl_switch_t net); extern unsigned ivl_switch_offset(ivl_switch_t net); extern ivl_expr_t ivl_switch_delay(ivl_switch_t net, unsigned transition); /* Not implemented yet extern unsigned ivl_switch_attr_cnt(ivl_switch_t net); extern ivl_attribute_t ivl_switch_attr_val(ivl_switch_t net, unsigned idx); *** */ extern const char* ivl_switch_file(ivl_switch_t net); extern unsigned ivl_switch_lineno(ivl_switch_t net); /* TYPES * * ivl_type_base * This returns the base type for the type. See the * ivl_variable_type_t definition for the various base types. * * ivl_type_element * Return the type of the element of an array. This is only valid * for array types. * * ivl_type_signed * Return TRUE if the type represents a signed packed vector or * signed atomic type, and FALSE otherwise. * * SEMANTIC NOTES * * Class types have names and properties. */ extern ivl_variable_type_t ivl_type_base(ivl_type_t net); extern ivl_type_t ivl_type_element(ivl_type_t net); extern unsigned ivl_type_packed_dimensions(ivl_type_t net); extern int ivl_type_packed_lsb(ivl_type_t net, unsigned dim); extern int ivl_type_packed_msb(ivl_type_t net, unsigned dim); extern int ivl_type_signed(ivl_type_t net); extern const char* ivl_type_name(ivl_type_t net); extern int ivl_type_properties(ivl_type_t net); extern const char* ivl_type_prop_name(ivl_type_t net, int idx); extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx); #if defined(__MINGW32__) || defined (__CYGWIN32__) # define DLLEXPORT __declspec(dllexport) #else # define DLLEXPORT #endif extern DLLEXPORT int target_design(ivl_design_t des); extern DLLEXPORT const char* target_query(const char*key); /* target_design The "target_design" function is called once after the whole design is processed and available to the target. The target doesn't return from this function until it is finished with the design. The return value of this function should normally be zero. If the code generator detects errors, however, then the code generator returns a positive number to indicate the approximate number of errors detected (before it gave up.) Return values <0 are reserved for system and infrastructure errors. This function is implemented in the loaded target, and not in the ivl core. This function is how the target module is invoked. */ typedef int (*target_design_f)(ivl_design_t des); typedef const char* (*target_query_f) (const char*key); _END_DECL #undef ENUM_UNSIGNED_INT #endif /* IVL_ivl_target_H */ iverilog-10_1/ivl_target.txt000066400000000000000000000026641265551621300162530ustar00rootroot00000000000000 Icarus Verilog LOADABLE TARGET API (ivl_target) Copyright 2002 Stephen Williams The ivl_target API is the interface available to modules that the Icarus Verilog compiler loads to act as a code generator. The API provides an interface to the elaborated, possibly synthesized, design for code generators that are intended to write netlist files or executable programs. The functions and types of the API are summarized in the ivl_target.h header file. This document describes how the functions and types of the API are used to access and interpret the netlist of the design. LPM DEVICES All LPM devices support a small set of common LPM functions, as described in the ivl_target header file. The ivl_lpm_t object has a type enumerated by ivl_lpm_type_t, and that type is accessible via the ivl_lpm_type function. The following are type specific aspects of LPM devices. * IVL_LPM_UFUNC This LPM represents a user defined function. It is a way to connect behavioral code into a structural network. The UFUNC device has a vector output and a set of inputs. The ivl_lpm_define function returns the definition as an ivl_scope_t object. The output vector is accessible through the ivl_lpm_q, and the output has the width defined by ivl_lpm_width. This similar to most every other LPM device with outputs. There are ivl_lpm_size() input ports, each with the width ivl_lpm_data2_width(). The actual nexus is indexed by ivl_lpm_data2(). iverilog-10_1/ivl_target_priv.h000066400000000000000000000055511265551621300167210ustar00rootroot00000000000000#ifndef IVL_ivl_target_priv_H #define IVL_ivl_target_priv_H /* * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "ivl_target.h" # include # include # include # include # include class NetScope; /* * This header has declarations related to the ivl_target.h API that * are not to be exported outside of the core via the ivl_target.h * interface. * * (NOTE: A lot of similar definitions exist in the t-dll.h header * file. That is a legacy from an earlier time before the * ivl_target_priv.h header file was started, and those definitions * should gradually be moved over to this header file.) */ /* * This is the root of a design, from the ivl_target point of few. The * ivl_target API uses this as the root for getting at everything else * in the design. */ struct ivl_design_s { int time_precision; ivl_process_t threads_; // Keep arrays of root scopes. std::map classes; std::map root_tasks; std::vector packages; std::vector roots; // This is used to implement the ivl_design_roots function. std::vector root_scope_list; // Keep an array of constants objects. std::vector consts; // Keep a handy array of all of the disciplines in the design. std::valarray disciplines; const class Design*self; }; /* * A branch is a pair of terminals. The elaborator assures that the * terminals have compatible disciplines. */ struct ivl_branch_s { ivl_nexus_t pins[2]; ivl_island_t island; }; /* * Information about islands. Connected branches within a net are * collected into islands. Branches that are purely ddiscrete do not * have disciplines and do not belong to islands. */ struct ivl_island_s { ivl_discipline_t discipline; // user accessible flags. They are initially false, always. std::vector flags; }; extern std::ostream& operator << (std::ostream&o, ivl_drive_t str); #endif /* IVL_ivl_target_priv_H */ iverilog-10_1/ivlpp/000077500000000000000000000000001265551621300144745ustar00rootroot00000000000000iverilog-10_1/ivlpp/Makefile.in000066400000000000000000000044361265551621300165500ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) # any later version. In order to redistribute the software in # binary form, you will need a Picture Elements Binary Software # 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = main.o lexor.o all: ivlpp@EXEEXT@ check: all clean: rm -f *.o lexor.c ivlpp@EXEEXT@ distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=ivlpp/$@ ivlpp@EXEEXT@: $O $(CC) $(LDFLAGS) $O -o ivlpp@EXEEXT@ @EXTRALIBS@ lexor.c: $(srcdir)/lexor.lex $(LEX) -t $< > $@ install: all installdirs $(libdir)/ivl$(suffix)/ivlpp@EXEEXT@ $(libdir)/ivl$(suffix)/ivlpp@EXEEXT@: ivlpp@EXEEXT@ $(INSTALL_PROGRAM) ./ivlpp@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/ivlpp@EXEEXT@" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/ivlpp@EXEEXT@" lexor.o: lexor.c globals.h main.o: main.c globals.h $(srcdir)/../version_base.h ../version_tag.h iverilog-10_1/ivlpp/globals.h000066400000000000000000000035721265551621300162770ustar00rootroot00000000000000#ifndef IVL_globals_H #define IVL_globals_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include extern void reset_lexor(FILE*out, char*paths[]); extern void destroy_lexor(void); extern void load_precompiled_defines(FILE*src); extern void define_macro(const char*name, const char*value, int keyword, int argc); extern void free_macros(void); extern void dump_precompiled_defines(FILE*out); /* These variables contain the include directories to be searched when an include directive in encountered. */ extern char**include_dir; extern unsigned include_cnt; /* Program to use for VHDL processing. */ extern char*vhdlpp_path; /* vhdlpp work directory */ extern char*vhdlpp_work; extern char**vhdlpp_libdir; extern unsigned vhdlpp_libdir_cnt; extern int relative_include; /* This flag is true if #line directives are to be generated. */ extern int line_direct_flag; extern unsigned error_count; extern FILE *depend_file; extern char dep_mode; extern int verbose_flag; /* This is the entry to the lexer. */ extern int yylex(void); #endif /* IVL_globals_H */ iverilog-10_1/ivlpp/ivlpp.txt000066400000000000000000000121611265551621300163700ustar00rootroot00000000000000 Copyright (c) 1999 Stephen Williams (steve@icarus.com) This source code is free software; you can redistribute it and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. THE IVL PREPROCESSOR The ivlpp command is a Verilog preprocessor that handles file inclusion and macro substitution. The program runs separate from the actual compiler so as to ease the task of the compiler proper, and provides a means of preprocessing files off-line. USAGE: ivlpp [options] The parameter is the name of the file to be read and preprocessed. The resulting output is sent to standard output. The valid options include: -Dname[=value] Predefine the symbol ``name'' to have the specified value. If the value is not specified, then ``1'' is used. This is mostly of use for controlling conditional compilation. This option does *not* override existing `define directives in the source file. -F Read ivlpp options from a FLAGS FILE. This is not the same as a file list. This file contains flags, not source files. There may be multiple flags files. -f Read ivlpp input files from a file list. There can be no more than one file list. -I Add a directory to the include path. Normally, only "." is in the search path. The -I flag causes other directories to be searched for a named file. There may be as many -I flags as needed. -L Generate `line directives. The ivl compiler understands these directives and uses them to keep track of the current line of the original source file. This makes error messages more meaningful. -o Send the output to the named file, instead of to standard output. -v Print version and copyright information before processing input files. -V Print version and copyright information, then exit WITHOUT processing any input files. FLAGS FILE A flags file contains flags for use by ivlpp. This is a convenient way for programs to pass complex sets of flags to the ivlpp program. Blank lines and lines that start with "#" are ignored. The latter can be used as comment lines. All other lines are flag lines. Leading and trailing white space are removed before the lines are interpreted. Other lines have the simple format: : The colon character separates a key from the value. The supported keys, with their corresponding values, are: D:name= This is exactly the same as the "-Dname=" described above. I: This is exactly the same as "-I". relative include: The can be "true" or "false". This enables "relative includes" nesting behavior. vhdlpp: Give the path to the vhdlpp program. This program is used to process VHDL input files. LOCATING INCLUDED FILES The ivlpp preprocessor implements the `include directives by substituting the contents of the included file in place of the line with the `include directive. The name that the programmer specifies is a file name. Normally, the preprocessor looks in the current working directory for the named file. However, the ``-I'' flags can be used to specify a path of directories to search for named include files. The current directory will be searched first, followed by all the include directories in the order that the -I flag appears. The exception to this process is include files that have a name that starts with the '/' character. These file names are ``rooted names'' and must be in the rooted location specified. GENERATED LINE DIRECTIVES Compilers generally try to print along with their error messages the file and line number where the error occurred. Icarus Verilog is no exception. However, if a separate preprocessor is actually selecting and opening files, then the line numbers counted by the compiler proper will not reflect the actual line numbers in the source file. To handle this situation, the preprocessor can generate line directives. These directives are lines of the form: `line where is the file name in double-quotes and is the line number in the file. The parser changes the filename and line number counters in such a way that the next line is line number in the file named . For example: `line 6 "foo.vl" 0 // I am on line 6 in file foo.vl. The preprocessor generates a `line directive every time it switches files. That includes starting an included file (`line 1 "foo.vlh" 1) or returning to the including file. iverilog-10_1/ivlpp/lexor.lex000066400000000000000000001560351265551621300163510ustar00rootroot00000000000000%option prefix="yy" %{ /* * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include # include # include "globals.h" # include "ivl_alloc.h" static void output_init(void); #define YY_USER_INIT output_init() static void def_start(void); static void def_add_arg(void); static void def_finish(void); static void def_undefine(void); static void do_define(void); static int def_is_done(void); static void def_continue(void); static int is_defined(const char*name); static int macro_needs_args(const char*name); static void macro_start_args(void); static void macro_add_to_arg(int is_whitespace); static void macro_finish_arg(void); static void do_expand(int use_args); static const char* do_magic(const char*name); static const char* macro_name(void); static void include_filename(void); static void do_include(void); static int load_next_input(void); struct include_stack_t { char* path; /* If the current input is from a file, this member is set. */ FILE* file; int (*file_close)(FILE*); /* If we are reparsing a macro expansion, file is 0 and this * member points to the string in progress */ char* str; char* orig_str; int stringify_flag; unsigned lineno; YY_BUFFER_STATE yybs; struct include_stack_t* next; /* A single line comment can be associated with this include. */ char* comment; }; static unsigned get_line(struct include_stack_t* isp); static const char *get_path(struct include_stack_t* isp); static void emit_pathline(struct include_stack_t* isp); /* * The file_queue is a singly-linked list of the files that were * listed on the command line/file list. */ static struct include_stack_t* file_queue = 0; static int do_expand_stringify_flag = 0; /* * The istack is the inclusion stack. */ static struct include_stack_t* istack = 0; static struct include_stack_t* standby = 0; /* * Keep a stack of active ifdef, so that I can report errors * when there are missing endifs. */ struct ifdef_stack_t { char* path; unsigned lineno; struct ifdef_stack_t* next; }; static struct ifdef_stack_t* ifdef_stack = 0; static void ifdef_enter(void) { struct ifdef_stack_t*cur; cur = (struct ifdef_stack_t*) calloc(1, sizeof(struct ifdef_stack_t)); if (istack->path) cur->path = strdup(istack->path); cur->lineno = istack->lineno; cur->next = ifdef_stack; ifdef_stack = cur; } static void ifdef_leave(void) { struct ifdef_stack_t* cur; assert(ifdef_stack); cur = ifdef_stack; ifdef_stack = cur->next; /* If either path is from a non-file context e.g.(macro expansion) * we assume that the non-file part is from this file. */ if (istack->path != NULL && cur->path != NULL && strcmp(istack->path,cur->path) != 0) { fprintf(stderr, "%s:%u: warning: This `endif matches an ifdef " "in another file.\n", istack->path, istack->lineno+1); fprintf(stderr, "%s:%u: This is the odd matched `ifdef.\n", cur->path, cur->lineno+1); } free(cur->path); free(cur); } #define YY_INPUT(buf,result,max_size) do { \ if (istack->file) { \ size_t rc = fread(buf, 1, max_size, istack->file); \ result = (rc == 0) ? YY_NULL : rc; \ } else { \ if (*istack->str == 0) \ result = YY_NULL; \ else { \ buf[0] = *istack->str++; \ result = 1; \ } \ } \ } while (0) static int comment_enter = 0; static int pragma_enter = 0; static int string_enter = 0; static int ma_parenthesis_level = 0; %} %option stack %option nounput %option noinput %option noyy_top_state %option noyywrap %x PPINCLUDE %x DEF_NAME %x DEF_ARG %x DEF_SEP %x DEF_TXT %x MA_START %x MA_ADD %x CCOMMENT %x IFCCOMMENT %x PCOMENT %x CSTRING %x ERROR_LINE %x IFDEF_FALSE %s IFDEF_TRUE %x IFDEF_SUPR W [ \t\b\f]+ /* The grouping parentheses are necessary for compatibility with * older versions of flex (at least 2.5.31); they are supposed to * be implied, according to the flex manual. */ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) %% "//"[^\r\n]* { ECHO; } /* detect multiline, c-style comments, passing them directly to the * output. This is necessary to allow for ignoring directives that * are included within the comments. */ "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); ECHO; } [^\r\n] { ECHO; } \n\r | \r\n | \n | \r { istack->lineno += 1; fputc('\n', yyout); } "*/" { BEGIN(comment_enter); ECHO; } /* Detect and pass multiline pragma comments. As with C-style * comments, pragma comments are passed through, and preprocessor * directives contained within are ignored. Contains macros are * expanded, however. */ "(*"{W}?")" { ECHO; } "(*" { pragma_enter = YY_START; BEGIN(PCOMENT); ECHO; } [^\r\n] { ECHO; } \n\r | \r\n | \n | \r { istack->lineno += 1; fputc('\n', yyout); } "*)" { BEGIN(pragma_enter); ECHO; } `{keywords} { emit_pathline(istack); error_count += 1; fprintf(stderr, "error: macro names cannot be directive keywords " "('%s'); replaced with nothing.\n", yytext); } `[a-zA-Z][a-zA-Z0-9_$]* { if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0); } /* `" overrides the usual lexical meaning of " and `\`" indicates that the expansion should include the escape sequence \". */ `\" { fputc('"', yyout); } `\\`\" { fprintf(yyout, "\\\""); } /* Strings do not contain preprocessor directives, but can expand * macros. If that happens, they get expanded in the context of the * string. */ \" { string_enter = YY_START; BEGIN(CSTRING); ECHO; } \\\\ | \\\" | \\` { ECHO; } \r\n | \n\r | \n | \r { fputc('\n', yyout); } \" { BEGIN(string_enter); ECHO; } . { ECHO; } /* This set of patterns matches the include directive and the name * that follows it. when the directive ends, the do_include function * performs the include operation. */ ^{W}?`include { yy_push_state(PPINCLUDE); } `{keywords} { emit_pathline(istack); error_count += 1; fprintf(stderr, "error: macro names cannot be directive keywords " "('%s'); replaced with nothing.\n", yytext); } `[a-zA-Z][a-zA-Z0-9_]* { if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0); } \"[^\"]*\" { include_filename(); } [ \t\b\f] { ; } /* Catch single-line comments that share the line with an include * directive. And while I'm at it, I might as well preserve the * comment in the output stream. This will be printed after the * file has been included. */ "//"[^\r\n]* { standby->comment = strdup(yytext); } /* These finish the include directive (EOF or EOL) so I revert the * lexor state and execute the inclusion. */ /* There is a bug in flex <= 2.5.34 that prevents the continued action '|' * from working properly when the final action is associated with <>. * Therefore, the action is repeated. */ \r\n | \n\r | \n | \r { istack->lineno += 1; yy_pop_state(); do_include(); } <> { istack->lineno += 1; yy_pop_state(); do_include(); } /* Anything that is not matched by the above is an error of some * sort. Print an error message and absorb the rest of the line. */ . { emit_pathline(istack); fprintf(stderr, "error: malformed `include directive. Did you quote the file name?\n"); error_count += 1; BEGIN(ERROR_LINE); } /* Detect the define directive, and match the name. If followed by a * '(', collect the formal arguments. Consume any white space, then * go into DEF_TXT mode and collect the defined value. */ `define{W} { yy_push_state(DEF_NAME); } {keywords}{W}? { emit_pathline(istack); error_count += 1; BEGIN(ERROR_LINE); fprintf(stderr, "error: malformed `define directive: macro names " "cannot be directive keywords\n"); } [a-zA-Z_][a-zA-Z0-9_$]*"("{W}? { BEGIN(DEF_ARG); def_start(); } [a-zA-Z_][a-zA-Z0-9_$]*{W}? { BEGIN(DEF_TXT); def_start(); } /* define arg: = */ [a-zA-Z_][a-zA-Z0-9_$]*{W}*"="[^,\)]*{W}? { BEGIN(DEF_SEP); def_add_arg(); } /* define arg: */ [a-zA-Z_][a-zA-Z0-9_$]*{W}? { BEGIN(DEF_SEP); def_add_arg(); } ","{W}? { BEGIN(DEF_ARG); } ")"{W}? { BEGIN(DEF_TXT); } "//"[^\r\n]* { ECHO; } "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); ECHO; } {W} {} (\n|"\r\n"|"\n\r"|\r){W}? { istack->lineno += 1; fputc('\n', yyout); } . { emit_pathline(istack); fprintf(stderr, "error: malformed `define directive.\n"); error_count += 1; BEGIN(ERROR_LINE); } .*[^\r\n] { do_define(); } (\n|"\r\n"|"\n\r"|\r) { if (def_is_done()) { def_finish(); yy_pop_state(); } else { def_continue(); } istack->lineno += 1; fputc('\n', yyout); } /* If the define is terminated by an EOF, then finish the define * whether there was a continuation or not. */ <> { def_finish(); istack->lineno += 1; fputc('\n', yyout); yy_pop_state(); if (!load_next_input()) yyterminate(); } `undef{W}[a-zA-Z_][a-zA-Z0-9_$]*{W}?.* { def_undefine(); } /* Detect conditional compilation directives, and parse them. If I * find the name defined, switch to the IFDEF_TRUE state and stay * there until I get an `else or `endif. Otherwise, switch to the * IFDEF_FALSE state and start tossing data. * * Handle suppressed `ifdef with an additional suppress start * condition that stacks on top of the IFDEF_FALSE so that output is * not accidentally turned on within nested ifdefs. */ `ifdef{W}[a-zA-Z_][a-zA-Z0-9_$]* { char* name = strchr(yytext, '`'); assert(name); name += 6; name += strspn(name, " \t\b\f"); ifdef_enter(); if (is_defined(name)) yy_push_state(IFDEF_TRUE); else yy_push_state(IFDEF_FALSE); } `ifndef{W}[a-zA-Z_][a-zA-Z0-9_$]* { char* name = strchr(yytext, '`'); assert(name); name += 7; name += strspn(name, " \t\b\f"); ifdef_enter(); if (!is_defined(name)) yy_push_state(IFDEF_TRUE); else yy_push_state(IFDEF_FALSE); } `ifdef{W} | `ifndef{W} { ifdef_enter(); yy_push_state(IFDEF_SUPR); } `elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { BEGIN(IFDEF_SUPR); } `elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { char* name = strchr(yytext, '`'); assert(name); name += 6; name += strspn(name, " \t\b\f"); if (is_defined(name)) BEGIN(IFDEF_TRUE); else BEGIN(IFDEF_FALSE); } `elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { } `else { BEGIN(IFDEF_SUPR); } `else { BEGIN(IFDEF_TRUE); } `else {} "//"[^\r\n]* {} "/*" { comment_enter = YY_START; BEGIN(IFCCOMMENT); } [^\r\n] {} \n\r | \r\n | \n | \r { istack->lineno += 1; fputc('\n', yyout); } "*/" { BEGIN(comment_enter); } [^\r\n] { } \n\r | \r\n | \n | \r { istack->lineno += 1; fputc('\n', yyout); } `endif { ifdef_leave(); yy_pop_state(); } `ifdef { error_count += 1; fprintf(stderr, "%s:%u: `ifdef without a macro name - ignored.\n", istack->path, istack->lineno+1); } `ifndef { error_count += 1; fprintf(stderr, "%s:%u: `ifndef without a macro name - ignored.\n", istack->path, istack->lineno+1); } `elsif { error_count += 1; fprintf(stderr, "%s:%u: `elsif without a macro name - ignored.\n", istack->path, istack->lineno+1); } `elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { error_count += 1; fprintf(stderr, "%s:%u: `elsif without a matching `ifdef - ignored.\n", istack->path, istack->lineno+1); } `else { error_count += 1; fprintf(stderr, "%s:%u: `else without a matching `ifdef - ignored.\n", istack->path, istack->lineno+1); } `endif { error_count += 1; fprintf(stderr, "%s:%u: `endif without a matching `ifdef - ignored.\n", istack->path, istack->lineno+1); } `{keywords} { emit_pathline(istack); error_count += 1; fprintf(stderr, "error: macro names cannot be directive keywords " "('%s'); replaced with nothing.\n", yytext); } /* This pattern notices macros and arranges for them to be replaced. */ `[a-zA-Z_][a-zA-Z0-9_$]* { if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0); } /* Stringified version of macro expansion. If the sequence `` is * encountered inside a macro definition, we use the SystemVerilog * handling of ignoring it so that identifiers can be constructed * from arguments. If istack->file is NULL, we are reading text * produced from a macro, so use SystemVerilog's handling; * otherwise, use the special Icarus handling. */ ``[a-zA-Z_][a-zA-Z0-9_$]* { if (istack->file == NULL) fprintf(yyout, "%s", yytext+2); else { assert(do_expand_stringify_flag == 0); do_expand_stringify_flag = 1; fputc('"', yyout); if (macro_needs_args(yytext+2)) yy_push_state(MA_START); else do_expand(0); } } `` { if (istack->file != NULL) ECHO; } \( { BEGIN(MA_ADD); macro_start_args(); } {W} {} (\n|"\r\n"|"\n\r"|\r){W}? { istack->lineno += 1; fputc('\n', yyout); } . { emit_pathline(istack); fprintf(stderr, "error: missing argument list for `%s.\n", macro_name()); error_count += 1; yy_pop_state(); yyless(0); } \"[^\"\n\r]*\" { macro_add_to_arg(0); } \"[^\"\n\r]* { emit_pathline(istack); fprintf(stderr, "error: unterminated string.\n"); error_count += 1; BEGIN(ERROR_LINE); } '[^\n\r]' { macro_add_to_arg(0); } {W} { macro_add_to_arg(1); } "(" { macro_add_to_arg(0); ma_parenthesis_level++; } "," { if (ma_parenthesis_level > 0) macro_add_to_arg(0); else macro_finish_arg(); } ")" { if (ma_parenthesis_level > 0) { macro_add_to_arg(0); ma_parenthesis_level--; } else { macro_finish_arg(); yy_pop_state(); do_expand(1); } } (\n|"\r\n"|"\n\r"|\r){W}? { macro_add_to_arg(1); istack->lineno += 1; fputc('\n', yyout); } . { macro_add_to_arg(0); } "//"[^\r\n]* { ECHO; } "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); ECHO; } /* Any text that is not a directive just gets passed through to the * output. Very easy. */ [^\r\n] { ECHO; } \n\r | \r\n | \n | \r { istack->lineno += 1; fputc('\n', yyout); } /* Absorb the rest of the line when a broken directive is detected. */ [^\r\n]* { yy_pop_state(); } (\n|"\r\n"|"\n\r"|\r) { yy_pop_state(); istack->lineno += 1; fputc('\n', yyout); } <> { if (!load_next_input()) yyterminate(); } %% /* Defined macros are kept in this table for convenient lookup. As * `define directives are matched (and the do_define() function * called) the tree is built up to match names with values. If a * define redefines an existing name, the new value it taken. */ struct define_t { char* name; char* value; int keyword; /* keywords don't get rescanned for fresh values. */ int argc; char** defaults; int magic; /* 1 for 'magic' macros like __FILE__ and __LINE__. magic * macros cannot be undefined. magic macros are expanded * by do_magic. N.B. DON'T set a magic macro with * argc > 1 or with keyword true. */ struct define_t* left; struct define_t* right; struct define_t* up; }; static struct define_t* def_table = 0; /* * magic macros */ static struct define_t def_FILE; static struct define_t def_LINE = { .name = "__LINE__", .value = "__LINE__", .keyword = 0, .argc = 1, .magic = 1, .left = &def_FILE, .right = 0, .up = 0 }; static struct define_t def_FILE = { .name = "__FILE__", .value = "__FILE__", .keyword = 0, .argc = 1, .magic = 1, .left = 0, .right = 0, .up = &def_LINE }; static struct define_t* magic_table = &def_LINE; /* * helper function for def_lookup */ static struct define_t* def_lookup_internal(const char*name, struct define_t*cur) { if (cur == 0) return 0; assert(cur->up == 0); while (cur) { int cmp = strcmp(name, cur->name); if (cmp == 0) return cur; cur = (cmp < 0) ? cur->left : cur->right; } return 0; } static struct define_t* def_lookup(const char*name) { // first, try a magic macro if(name[0] == '_' && name[1] == '_' && name[2] != '\0') { struct define_t* result = def_lookup_internal(name, magic_table); if(result) { return result; } } // either there was no matching magic macro, or we didn't try looking // look for a normal macro return def_lookup_internal(name, def_table); } static int is_defined(const char*name) { return def_lookup(name) != 0; } /* * The following variables are used to temporarily hold the name and * formal arguments of a macro definition as it is being parsed. As * for C program arguments, def_argc counts the arguments (including * the macro name), the value returned by def_argv(0) points to the * macro name, the value returned by def_argv(1) points to the first * argument, and so on. * * These variables are also used for storing the actual arguments when * a macro is instantiated. */ #define MAX_DEF_ARG 256 /* allows argument IDs to be stored in a single char */ #define DEF_BUF_CHUNK 256 /* * During parse of macro definitions, and the name/arguments in * particular, keep the names and name lengths in a compact stretch of * memory. Note that we do not keep the argument names once the * definition is fully processed, because arguments are always * positional and the definition string is replaced with position * tokens. */ static char* def_buf = 0; static int def_buf_size = 0; static int def_buf_free = 0; static int def_argc = 0; static int def_argo[MAX_DEF_ARG]; /* offset of first character of arg name */ static int def_argl[MAX_DEF_ARG]; /* lengths of arg names. */ static int def_argd[MAX_DEF_ARG]; /* Offset of default value */ /* * Return a pointer to the start of argument 'arg'. Returned pointers * may go stale after a call to def_buf_grow_to_fit. */ static /* inline */ char* def_argv(int arg) { return def_buf + def_argo[arg]; } static void check_for_max_args(void) { if (def_argc == MAX_DEF_ARG) { emit_pathline(istack); fprintf(stderr, "error: too many macro arguments - aborting\n"); exit(1); } } static void def_buf_grow_to_fit(int length) { while (length >= def_buf_free) { def_buf_size += DEF_BUF_CHUNK; def_buf_free += DEF_BUF_CHUNK; def_buf = realloc(def_buf, def_buf_size); assert(def_buf != 0); } } static void def_start(void) { def_buf_free = def_buf_size; def_argc = 0; def_add_arg(); } static void def_add_arg(void) { int length = yyleng; check_for_max_args(); /* Remove trailing white space and, if necessary, opening brace. */ while (isspace((int)yytext[length - 1])) length--; /* This can happen because we are also processing "argv[0]", the macro name, as a pseudo-argument. The lexor will match that as name(, so chop off the ( here. */ if (yytext[length - 1] == '(') length--; yytext[length] = 0; char*arg = yytext; char*val; int val_length = 0; /* Break into ARG = value. This happens if the source specifies a default value for the formal argument. In that case, the lexor will match the whole thing as the argument and it is up to us to chop it up to name and value. */ if ( (val=strchr(arg,'=')) ) { *val++ = 0; while (*val && isspace(*val)) val += 1; val_length = strlen(val); while (val_length>0 && isspace(val[val_length-1])) { val_length -= 1; val[val_length] = 0; } /* Strip white space from between arg and "=". */ length = strlen(arg); while (length>0 && isspace(arg[length-1])) { length -= 1; arg[length] = 0; } } /* Make sure there's room in the buffer for the new argument. */ def_buf_grow_to_fit(length); /* Store the new argument name. */ def_argl[def_argc] = length; def_argo[def_argc] = def_buf_size - def_buf_free; strcpy(def_argv(def_argc), arg); def_buf_free -= length + 1; /* If there is a default text, then stash it away as well. */ if (val) { def_buf_grow_to_fit(val_length); def_argd[def_argc] = def_buf_size - def_buf_free; strcpy(def_buf+def_argd[def_argc], val); def_buf_free -= val_length + 1; } else { def_argd[def_argc] = 0; } def_argc += 1; } void define_macro(const char* name, const char* value, int keyword, int argc) { int idx; struct define_t* def; def = malloc(sizeof(struct define_t)); def->name = strdup(name); def->value = strdup(value); def->keyword = keyword; def->argc = argc; def->magic = 0; def->left = 0; def->right = 0; def->up = 0; def->defaults = calloc(argc, sizeof(char*)); for (idx = 0 ; idx < argc ; idx += 1) { if (def_argd[idx] == 0) { def->defaults[idx] = 0; } else { def->defaults[idx] = strdup(def_buf+def_argd[idx]); } } if (def_table == 0) { def_table = def; } else { struct define_t* cur = def_table; while (1) { int cmp = strcmp(def->name, cur->name); if (cmp == 0) { free(cur->value); cur->value = def->value; free(def->name); free(def); break; } else if (cmp < 0) { if (cur->left != 0) { cur = cur->left; } else { cur->left = def; def->up = cur; break; } } else { if (cur->right != 0) { cur = cur->right; } else { cur->right = def; def->up = cur; break; } } } } } static void free_macro(struct define_t* def) { int idx; if (def == 0) return; free_macro(def->left); free_macro(def->right); free(def->name); free(def->value); for (idx = 0 ; idx < def->argc ; idx += 1) free(def->defaults[idx]); free(def->defaults); free(def); } void free_macros(void) { free_macro(def_table); } /* * The do_define function accumulates the defined value in these * variables. When the define is over, the def_finish() function * executes the define and clears this text. The define_continue_flag * is set if do_define detects that the definition is to be continued * on the next line. */ static char* define_text = 0; static size_t define_cnt = 0; static int define_continue_flag = 0; /* * The do_magic function puts the expansions of magic macros into * this buffer and returns its address. It reallocs as needed to * fit its whole expansion. Because of this, do_magic is * -NOT REENTRANT-. It is called from do_expand, which strdups * or otherwise copies the result before doing any recursion, so * I don't anticipate any problems. */ static char* magic_text = 0; static size_t magic_cnt = 0; /* * Define a special character code used to mark the insertion point * for arguments in the macro text. This should be a character that * will not occur in the Verilog source code. */ #define ARG_MARK '\a' #define _STR1(x) #x #define _STR2(x) _STR1(x) static int is_id_char(char c) { return isalnum((int)c) || c == '_' || c == '$'; } /* * Find an argument, but only if it is not directly preceded by something * that would make it part of another simple identifier ([a-zA-Z0-9_$]). */ static char *find_arg(char*ptr, char*head, char*arg) { char *cp = ptr; size_t len = strlen(arg); while (1) { /* Look for a candidate match, just return if none is found. */ cp = strstr(cp, arg); if (!cp) break; /* Verify that this match is not in the middle of another identifier. */ if ((cp != head && is_id_char(cp[-1])) || is_id_char(cp[len])) { cp++; continue; } break; } return cp; } /* * Collect the definition. Normally, this returns 0. If there is a * continuation, then return 1 and this function may be called again * to collect another line of the definition. */ static void do_define(void) { char* cp; char* head; char* tail; int added_cnt; int arg; define_continue_flag = 0; /* Look for comments in the definition, and remove them. The * "//" style comments go to the end of the line and terminate * the definition, but the multi-line comments are simply cut * out, and the define continues. */ cp = strchr(yytext, '/'); while (cp && *cp) { if (cp[1] == '/') { *cp = 0; break; } if (cp[1] == '*') { tail = strstr(cp+2, "*/"); if (tail == 0) { *cp = 0; fprintf(stderr, "%s:%u: Unterminated comment in define\n", istack->path, istack->lineno+1 ); break; } memmove(cp, tail+2, strlen(tail+2)+1); continue; } cp = strchr(cp+1, '/'); } /* Trim trailing white space. */ cp = yytext + strlen(yytext); while (cp > yytext) { if (!isspace((int)cp[-1])) break; cp -= 1; *cp = 0; } /* Detect the continuation sequence. If I find it, remove it * and the white space that precedes it, then replace all that * with a single newline. */ if ((cp > yytext) && (cp[-1] == '\\')) { cp -= 1; cp[0] = 0; while ((cp > yytext) && isspace((int)cp[-1])) { cp -= 1; *cp = 0; } *cp++ = '\n'; *cp = 0; define_continue_flag = 1; } /* Accumulate this text into the define_text string. */ define_text = realloc(define_text, define_cnt + (cp-yytext) + 1); head = &define_text[define_cnt]; strcpy(head, yytext); define_cnt += cp-yytext; tail = &define_text[define_cnt]; /* If the text for a macro with arguments contains occurrences * of ARG_MARK, issue an error message and suppress the macro. */ if ((def_argc > 1) && strchr(head, ARG_MARK)) { emit_pathline(istack); error_count += 1; def_argc = 0; fprintf(stderr, "error: implementation restriction - " "macro text may not contain a %s character\n", _STR2(ARG_MARK)); } /* Look for formal argument names in the definition, and replace * each occurrence with the sequence ARG_MARK,'\ddd' where ddd is * the formal argument index number. */ added_cnt = 0; for (arg = 1; arg < def_argc; arg++) { int argl = def_argl[arg]; cp = find_arg(head, head, def_argv(arg)); while (cp && *cp) { added_cnt += 2 - argl; if (added_cnt > 0) { char* base = define_text; define_cnt += added_cnt; define_text = realloc(define_text, define_cnt + 1); head = &define_text[head - base]; tail = &define_text[tail - base]; cp = &define_text[cp - base]; added_cnt = 0; } memmove(cp+2, cp+argl, tail-(cp+argl)+1); tail += 2 - argl; *cp++ = ARG_MARK; *cp++ = arg; cp = find_arg(cp, head, def_argv(arg)); } } define_cnt += added_cnt; } /* * Return true if the definition text is done. This is the opposite of * the define_continue_flag. */ static int def_is_done(void) { return !define_continue_flag; } /* * Reset the define_continue_flag. */ static void def_continue(void) { define_continue_flag = 0; } /* * After some number of calls to do_define, this function is called to * assigned value to the parsed name. If there is no value, then * assign the string "" (empty string.) */ static void def_finish(void) { define_continue_flag = 0; if (def_argc <= 0) return; if (!define_text) { define_macro(def_argv(0), "", 0, def_argc); } else { define_macro(def_argv(0), define_text, 0, def_argc); free(define_text); define_text = 0; define_cnt = 0; } def_argc = 0; } static void def_undefine(void) { struct define_t* cur; struct define_t* tail; int idx; /* def_buf is used to store the macro name. Make sure there is * enough space. */ def_buf_grow_to_fit(yyleng); sscanf(yytext, "`undef %s", def_buf); cur = def_lookup(def_buf); if (cur == 0) return; if (cur->magic) return; if (cur->up == 0) { if ((cur->left == 0) && (cur->right == 0)) { def_table = 0; } else if (cur->left == 0) { def_table = cur->right; if (cur->right) cur->right->up = 0; } else if (cur->right == 0) { assert(cur->left); def_table = cur->left; def_table->up = 0; } else { tail = cur->left; while (tail->right) tail = tail->right; tail->right = cur->right; tail->right->up = tail; def_table = cur->left; def_table->up = 0; } } else if (cur->left == 0) { if (cur->up->left == cur) { cur->up->left = cur->right; } else { assert(cur->up->right == cur); cur->up->right = cur->right; } if (cur->right) cur->right->up = cur->up; } else if (cur->right == 0) { assert(cur->left); if (cur->up->left == cur) { cur->up->left = cur->left; } else { assert(cur->up->right == cur); cur->up->right = cur->left; } cur->left->up = cur->up; } else { tail = cur->left; assert(cur->left && cur->right); while (tail->right) tail = tail->right; tail->right = cur->right; tail->right->up = tail; if (cur->up->left == cur) { cur->up->left = cur->left; } else { assert(cur->up->right == cur); cur->up->right = cur->left; } cur->left->up = cur->up; } free(cur->name); free(cur->value); for (idx = 0 ; idx < cur->argc ; idx += 1) free(cur->defaults[idx]); free(cur->defaults); free(cur); } /* * When a macro is instantiated in the source, macro_needs_args() is * used to look up the name and return whether it is a macro that * takes arguments. A pointer to the macro descriptor is stored in * cur_macro so that do_expand() doesn't need to look it up again. */ static struct define_t* cur_macro = 0; static int macro_needs_args(const char*text) { cur_macro = def_lookup(text); if (cur_macro) { return (cur_macro->argc > 1); } else { emit_pathline(istack); fprintf(stderr, "warning: macro %s undefined (and assumed null) at this point.\n", text); return 0; } } static const char* macro_name(void) { return cur_macro ? cur_macro->name : ""; } static void macro_start_args(void) { /* The macro name can be found via cur_macro, so create a null * entry for arg 0. This will be used by macro_finish_arg() to * calculate the buffer location for arg 1. */ def_buf_free = def_buf_size - 1; def_buf[0] = 0; def_argo[0] = 0; def_argl[0] = 0; def_argc = 1; } static void macro_add_to_arg(int is_white_space) { char* tail; int length = yyleng; check_for_max_args(); /* Replace any run of white space with a single space */ if (is_white_space) { yytext[0] = ' '; yytext[1] = 0; length = 1; } /* Make sure there's room in the buffer for the new argument. */ def_buf_grow_to_fit(length); /* Store the new text. */ tail = &def_buf[def_buf_size - def_buf_free]; strcpy(tail, yytext); def_buf_free -= length; } static void macro_finish_arg(void) { int offs; char* head; char* tail; check_for_max_args(); offs = def_argo[def_argc-1] + def_argl[def_argc-1] + 1; head = &def_buf[offs]; tail = &def_buf[def_buf_size - def_buf_free]; /* Eat any leading and trailing white space. */ if ((head < tail) && (*head == ' ')) { offs++; head++; } if ((tail > head) && (*(tail-1) == ' ')) { def_buf_free++; tail--; } *tail = 0; def_argo[def_argc] = offs; def_argl[def_argc] = tail - head; def_buf_free -= 1; def_argc++; } /* * The following variables are used to hold macro expansions that are * built dynamically using supplied arguments. Buffer space is allocated * as the macro is expanded, and is only released once the expansion has * been reparsed. This means that the buffer acts as a stack for nested * macro expansions. * * The expansion buffer is only used for macros with arguments - the * text for simple macros can be taken directly from the macro table. */ #define EXP_BUF_CHUNK 256 static char*exp_buf = 0; static int exp_buf_size = 0; static int exp_buf_free = 0; static void exp_buf_grow_to_fit(int length) { while (length >= exp_buf_free) { exp_buf_size += EXP_BUF_CHUNK; exp_buf_free += EXP_BUF_CHUNK; exp_buf = realloc(exp_buf, exp_buf_size); } } static void expand_using_args(void) { char* head; char* tail; char* dest; int arg; int length; if (def_argc > cur_macro->argc) { emit_pathline(istack); fprintf(stderr, "error: too many arguments for `%s\n", cur_macro->name); return; } while (def_argc < cur_macro->argc) { if (cur_macro->defaults[def_argc]) { def_argl[def_argc] = 0; def_argc += 1; continue; } emit_pathline(istack); fprintf(stderr, "error: too few arguments for `%s\n", cur_macro->name); return; } assert(def_argc == cur_macro->argc); head = cur_macro->value; tail = head; while (*tail) { if (*tail != ARG_MARK) { tail++; } else { arg = tail[1]; assert(arg < def_argc); char*use_argv; int use_argl; if (def_argl[arg] == 0 && cur_macro->defaults[arg]) { use_argv = cur_macro->defaults[arg]; use_argl = strlen(use_argv); } else { use_argv = def_argv(arg); use_argl = def_argl[arg]; } length = (tail - head) + use_argl; exp_buf_grow_to_fit(length); dest = &exp_buf[exp_buf_size - exp_buf_free]; memcpy(dest, head, tail - head); dest += tail - head; memcpy(dest, use_argv, use_argl); exp_buf_free -= length; head = tail + 2; tail = head; } } length = tail - head; exp_buf_grow_to_fit(length); dest = &exp_buf[exp_buf_size - exp_buf_free]; memcpy(dest, head, length + 1); exp_buf_free -= length + 1; } /* * When a macro use is discovered in the source, this function is * used to emit the substitution in its place. */ static void do_expand(int use_args) { if (cur_macro) { struct include_stack_t*isp; int head = 0; const char *cp; unsigned escapes = 0; char *str_buf = 0; if (cur_macro->keyword) { fprintf(yyout, "%s", cur_macro->value); if (do_expand_stringify_flag) { do_expand_stringify_flag = 0; fputc('"', yyout); } return; } if (use_args) { int tail = 0; head = exp_buf_size - exp_buf_free; expand_using_args(); tail = exp_buf_size - exp_buf_free; exp_buf_free += tail - head; if (tail == head) return; } isp = (struct include_stack_t*) calloc(1, sizeof(struct include_stack_t)); isp->stringify_flag = do_expand_stringify_flag; do_expand_stringify_flag = 0; if (use_args) { isp->str = &exp_buf[head]; } else if(cur_macro->magic) { // cast const char * to char * to suppress warning, since we won't // be modifying isp->str in place. isp->str = (char*)do_magic(cur_macro->name); } else { isp->str = cur_macro->value; } /* Escape some characters if we are making a string version. */ for (cp = isp->str; (cp = strpbrk(cp, "\"\\")); cp += 1, escapes += 1); if (escapes && isp->stringify_flag) { unsigned idx = 0; str_buf = (char *) malloc(strlen(isp->str)+3*escapes+1); for (cp = isp->str; *cp; cp += 1) { if (*cp == '"') { str_buf[idx] = '\\'; str_buf[idx+1] = '0'; str_buf[idx+2] = '4'; str_buf[idx+3] = '2'; idx += 4; continue; } if (*cp == '\\') { str_buf[idx] = '\\'; str_buf[idx+1] = '1'; str_buf[idx+2] = '3'; str_buf[idx+3] = '4'; idx += 4; continue; } str_buf[idx] = *cp; idx += 1; } str_buf[idx] = 0; idx += 1; exp_buf_free -= idx; isp->str = str_buf; } else isp->str = strdup(isp->str); isp->orig_str = isp->str; isp->next = istack; istack->yybs = YY_CURRENT_BUFFER; istack = isp; yy_switch_to_buffer(yy_create_buffer(istack->file, YY_BUF_SIZE)); } else { if (do_expand_stringify_flag) { do_expand_stringify_flag = 0; fputc('"', yyout); } } } /* * Expand the magic macro named name. Return a buffer containing the expansion. */ static const char* do_magic(const char*name) { size_t desired_cnt = 0; if(!magic_text) magic_text = malloc(24); // unimportant initial size if(!strcmp(name, "__LINE__")) { // istack->lineno is unsigned. the largest it could be is 64 bits. // 2^64 is between 10^19 and 10^20. So the decimal representation of // lineno can't possibly be longer than 23 bytes. I'm generous but // bytes are cheap and this is nobody's critical path. desired_cnt = 24; if(magic_cnt < desired_cnt) { magic_text = realloc(magic_text, desired_cnt); assert(magic_text); magic_cnt = desired_cnt; } int actual_len = snprintf(magic_text, desired_cnt, "%u", get_line(istack)); assert(actual_len >= 0); assert((unsigned) actual_len < desired_cnt); return magic_text; } else if(!strcmp(name, "__FILE__")) { const char *path = get_path(istack); if(path) { desired_cnt = strlen(path)+2+1; // two quotes and a null if(magic_cnt < desired_cnt) { magic_text = realloc(magic_text, desired_cnt); assert(magic_text); magic_cnt = desired_cnt; } int actual_len = snprintf(magic_text, desired_cnt, "\"%s\"", path); assert(actual_len >= 0); assert((unsigned) actual_len < (unsigned)desired_cnt); return magic_text; } } // if we get here, then either there was no magic macro with the requested // name, or for some reason (an error?) we want to return an empty string assert(magic_cnt > 0); magic_text[0] = '\0'; return magic_text; } /* * Include file handling works by keeping an include stack of the * files that are opened and being processed. The first item on the * stack is the current file being scanned. If I get to an include * statement, * * open the new file, * save the current buffer context, * create a new buffer context, * and push the new file information. * * When the file runs out, it is closed and the buffer is deleted * If after popping the current file information there is another * file on the stack, that file's buffer context is restored and * parsing resumes. */ static void output_init(void) { if (line_direct_flag) { fprintf(yyout, "`line 1 \"%s\" 0\n", istack->path); } } static void include_filename(void) { if(standby) { emit_pathline(istack); fprintf(stderr, "error: malformed `include directive. Extra junk on line?\n"); exit(1); } standby = malloc(sizeof(struct include_stack_t)); standby->path = strdup(yytext+1); standby->path[strlen(standby->path)-1] = 0; standby->lineno = 0; standby->comment = NULL; } static void do_include(void) { /* standby is defined by include_filename() */ if (standby->path[0] == '/') { if ((standby->file = fopen(standby->path, "r"))) { standby->file_close = fclose; goto code_that_switches_buffers; } } else { unsigned idx, start = 1; char path[4096]; char *cp; struct include_stack_t* isp; /* Add the current path to the start of the include_dir list. */ isp = istack; while(isp && (isp->path == NULL)) isp = isp->next; assert(isp); strcpy(path, isp->path); cp = strrchr(path, '/'); /* I may need the relative path for a planned warning even when * we are not in relative mode, so for now keep it around. */ if (cp != 0) { *cp = '\0'; include_dir[0] = strdup(path); if (relative_include) start = 0; } for (idx = start ; idx < include_cnt ; idx += 1) { sprintf(path, "%s/%s", include_dir[idx], standby->path); if ((standby->file = fopen(path, "r"))) { standby->file_close = fclose; /* Free the original path before we overwrite it. */ free(standby->path); standby->path = strdup(path); goto code_that_switches_buffers; } } } emit_pathline(istack); fprintf(stderr, "Include file %s not found\n", standby->path); exit(1); code_that_switches_buffers: /* Clear the current files path from the search list. */ free(include_dir[0]); include_dir[0] = 0; if (depend_file) { if (dep_mode == 'p') { fprintf(depend_file, "I %s\n", standby->path); } else if (dep_mode != 'm') { fprintf(depend_file, "%s\n", standby->path); } } if (line_direct_flag) { fprintf(yyout, "\n`line 1 \"%s\" 1\n", standby->path); } standby->next = istack; standby->stringify_flag = 0; istack->yybs = YY_CURRENT_BUFFER; istack = standby; standby = 0; yy_switch_to_buffer(yy_create_buffer(istack->file, YY_BUF_SIZE)); } /* * walk the include stack until we find an entry with a valid pathname, * and return the line from that entry for use in an error message. * This is the real file and line in which the outermost macro was used. */ static unsigned get_line(struct include_stack_t* isp) { while(isp && (isp->path == NULL)) isp = isp->next; assert(isp); return isp->lineno+1; } /* * walk the include stack until we find an entry with a valid pathname, * and return the path from that entry for use in an error message. * This is the real file and line in which the outermost macro was used. */ static const char* get_path(struct include_stack_t* isp) { while(isp && (isp->path == NULL)) isp = isp->next; assert(isp); return isp->path; } /* walk the include stack until we find an entry with a valid pathname, * and print the file and line from that entry for use in an error message. * The istack entries created by do_expand() for macro expansions do not * contain pathnames. This finds instead the real file in which the outermost * macro was used. */ static void emit_pathline(struct include_stack_t* isp) { while(isp && (isp->path == NULL)) isp = isp->next; assert(isp); fprintf(stderr, "%s:%u: ", isp->path, isp->lineno+1); } static void lexor_done(void) { while (ifdef_stack) { struct ifdef_stack_t*cur = ifdef_stack; ifdef_stack = cur->next; fprintf(stderr, "%s:%u: error: This `ifdef lacks an `endif.\n", cur->path, cur->lineno+1); free(cur->path); free(cur); error_count += 1; } } /* * Use this function to open a source file that is to be * processed. Do NOT use this function for opening include files, * instead only use this file for opening base source files. */ static void open_input_file(struct include_stack_t*isp) { char*cp; int is_vhdl = 0; unsigned idx; isp->file = 0; /* look for a suffix for the input file. If the suffix indicates that this is a VHDL source file, then invoke vhdlpp to get a data stream. */ cp = strrchr(isp->path, '.'); if (cp && vhdlpp_path) { if (strcmp(cp, ".vhd") == 0) { is_vhdl = 1; } else if (strcmp(cp, ".vhdl") == 0) { is_vhdl = 1; } } if (is_vhdl == 0) { isp->file = fopen(isp->path, "r"); isp->file_close = fclose; return; } size_t cmdlen = strlen(vhdlpp_path); cmdlen += strlen(isp->path); cmdlen += 8+strlen(vhdlpp_work); size_t liblen = 1; char*libs = strdup(""); for (idx = 0 ; idx < vhdlpp_libdir_cnt ; idx += 1) { size_t next_len = 6 + strlen(vhdlpp_libdir[idx]); libs = realloc(libs, liblen+next_len); snprintf(libs+liblen-1, next_len, " -L\"%s\"", vhdlpp_libdir[idx]); liblen = strlen(libs) + 1; } cmdlen += liblen; char*cmd = malloc(cmdlen); snprintf(cmd, cmdlen, "%s -w\"%s\"%s %s", vhdlpp_path, vhdlpp_work, libs, isp->path); if (verbose_flag) fprintf(stderr, "Invoke vhdlpp: %s\n", cmd); isp->file = popen(cmd, "r"); isp->file_close = pclose; free(libs); free(cmd); return; } /* * The load_next_input() function is called by the lexical analyzer * when the current file runs out. When the EOF of the current input * file is matched, this function figures out if this is the end of an * included file (in which case the including file is resumed) or the * end of a base file, in which case the next base source file is * opened. */ static int load_next_input(void) { int line_mask_flag = 0; struct include_stack_t* isp = istack; istack = isp->next; /* Delete the current input buffers, and free the cell. */ yy_delete_buffer(YY_CURRENT_BUFFER); /* If there was a comment for this include print it before we * return to the previous input stream. This technically belongs * to the previous stream, but it should not create any problems * since it is only a comment. */ if (isp->comment) { fprintf(yyout, "%s\n", isp->comment); free(isp->comment); isp->comment = NULL; } if (isp->file) { free(isp->path); assert(isp->file_close); isp->file_close(isp->file); } else { /* If I am printing line directives and I just finished * macro substitution, I should terminate the line and * arrange for a new directive to be printed. */ if (line_direct_flag && istack && istack->path && isp->lineno) { fprintf(yyout, "\n"); } else line_mask_flag = 1; free(isp->orig_str); } if (isp->stringify_flag) fputc('"', yyout); free(isp); /* If I am out of include stack, the main input is * done. Look for another file to process in the input * queue. If none are there, give up. Otherwise, open the file * and continue parsing. */ if (istack == 0) { if (file_queue == 0) { lexor_done(); return 0; } istack = file_queue; file_queue = file_queue->next; istack->next = 0; istack->lineno = 0; open_input_file(istack); if (istack->file == 0) { perror(istack->path); error_count += 1; return 0; } if (line_direct_flag) { fprintf(yyout, "\n`line 1 \"%s\" 0\n", istack->path); } if (depend_file) { if (dep_mode == 'p') { fprintf(depend_file, "M %s\n", istack->path); } else if (dep_mode != 'i') { fprintf(depend_file, "%s\n", istack->path); } } /* This works around an issue in flex yyrestart() where it * uses yyin to create a new buffer when one does not exist. * I would have assumed that it would use the file argument. * The problem is that we have deleted the buffer and freed * yyin (isp->file) above. */ yyin = 0; yyrestart(istack->file); return 1; } /* Otherwise, resume the input buffer that is the new stack * top. If I need to print a line directive, do so. */ yy_switch_to_buffer(istack->yybs); if (line_direct_flag && istack->path && !line_mask_flag) { fprintf(yyout, "\n`line %u \"%s\" 2\n", istack->lineno+1, istack->path); } return 1; } /* * The dump_precompiled_defines() and load_precompiled_defines() * functions dump/load macro definitions to/from a file. The defines * are in the form: * * ::: * * for easy extraction. The value is already precompiled to handle * macro substitution. The is the number of bytes in the * . This is necessary because the value may contain arbitrary * text, including ':' and \n characters. * * Each record is terminated by a \n character. */ static void do_dump_precompiled_defines(FILE* out, struct define_t* table) { if (!table->keyword) fprintf(out, "%s:%d:%zd:%s\n", table->name, table->argc, strlen(table->value), table->value); if (table->left) do_dump_precompiled_defines(out, table->left); if (table->right) do_dump_precompiled_defines(out, table->right); } void dump_precompiled_defines(FILE* out) { if (def_table) do_dump_precompiled_defines(out, def_table); } void load_precompiled_defines(FILE* src) { char*buf = malloc(4096); size_t buf_len = 4096; int ch; while ((ch = fgetc(src)) != EOF) { char* cp = buf; char* name = 0; int argc = 0; size_t len = 0; *cp++ = ch; while ((ch = fgetc(src)) != EOF && ch != ':') { *cp++ = ch; assert( (size_t)(cp-buf) < buf_len ); } if (ch != ':') return; /* Terminate the name string. */ *cp++ = 0; assert( (size_t)(cp-buf) < buf_len ); /* Read the argc number. (this doesn't need buffer space) */ while (isdigit(ch = fgetc(src))) argc = 10*argc + ch-'0'; if (ch != ':') return; /* Read the value len (this doesn't need buffer space) */ while (isdigit(ch = fgetc(src))) len = 10*len + ch-'0'; if (ch != ':') return; /* Save the name, and start the buffer over. */ name = strdup(buf); /* We now know how big the value should be, so if necessary, reallocate the buffer to be sure we can hold it. */ if ((len+4) >= buf_len) { buf = realloc(buf, len+8); assert(buf); } cp = buf; while (len > 0) { ch = fgetc(src); if (ch == EOF) { free(name); return; } *cp++ = ch; len -= 1; } *cp++ = 0; ch = fgetc(src); if (ch != '\n') { free(name); free(buf); return; } define_macro(name, buf, 0, argc); free(name); } } /* * This function initializes the whole process. The first file is * opened, and the lexor is initialized. The include stack is cleared * and ready to go. */ void reset_lexor(FILE* out, char* paths[]) { unsigned idx; struct include_stack_t* isp; struct include_stack_t* tail = 0; isp = malloc(sizeof(struct include_stack_t)); isp->next = 0; isp->path = strdup(paths[0]); open_input_file(isp); isp->str = 0; isp->lineno = 0; isp->stringify_flag = 0; isp->comment = NULL; if (isp->file == 0) { perror(paths[0]); exit(1); } if (depend_file) { if (dep_mode == 'p') { fprintf(depend_file, "M %s\n", paths[0]); } else if (dep_mode != 'i') { fprintf(depend_file, "%s\n", paths[0]); } } yyout = out; yyrestart(isp->file); assert(istack == 0); istack = isp; /* Now build up a queue of all the remaining file names, so * that load_next_input() can pull them when needed. */ for (idx = 1 ; paths[idx] ; idx += 1) { isp = malloc(sizeof(struct include_stack_t)); isp->path = strdup(paths[idx]); isp->file = 0; isp->str = 0; isp->next = 0; isp->lineno = 0; isp->stringify_flag = 0; isp->comment = NULL; if (tail) tail->next = isp; else file_queue = isp; tail = isp; } } /* * Modern version of flex (>=2.5.9) can clean up the scanner data. */ void destroy_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif free(def_buf); free(exp_buf); } iverilog-10_1/ivlpp/main.c000066400000000000000000000320001265551621300155570ustar00rootroot00000000000000const char COPYRIGHT[] = "Copyright (c) 1999-2011,2015 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "version_base.h" # include "version_tag.h" const char NOTICE[] = " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; # include # include # include # include # include #if defined(HAVE_GETOPT_H) # include #endif # include "globals.h" # include "ivl_alloc.h" #if defined(__MINGW32__) && !defined(HAVE_GETOPT_H) extern int getopt(int argc, char*argv[], const char*fmt); extern int optind; extern const char*optarg; #endif /* Path to the dependency file, if there is one. */ char *dep_path = NULL; /* Dependency file output mode */ char dep_mode = 'a'; /* verbose flag */ int verbose_flag = 0; /* Path to vhdlpp */ char *vhdlpp_path = 0; /* vhdlpp work directory */ char *vhdlpp_work = 0; char**vhdlpp_libdir = 0; unsigned vhdlpp_libdir_cnt = 0; /* * Keep in source_list an array of pointers to file names. The array * is terminated by a pointer to null. */ static char**source_list = 0; static unsigned source_cnt = 0; void add_source_file(const char*name) { if (source_list == 0) { source_list = calloc(2, sizeof(char*)); source_list[0] = strdup(name); source_list[1] = 0; source_cnt = 1; } else { source_list = realloc(source_list, sizeof(char*) * (source_cnt+2)); source_list[source_cnt+0] = strdup(name); source_list[source_cnt+1] = 0; source_cnt += 1; } } char**include_dir = 0; unsigned include_cnt = 0; int relative_include = 0; int line_direct_flag = 0; unsigned error_count = 0; FILE *depend_file = NULL; static int flist_read_flags(const char*path) { char line_buf[2048]; FILE*fd = fopen(path, "r"); if (fd == 0) { fprintf(stderr, "%s: unable to open for reading.\n", path); return -1; } while (fgets(line_buf, sizeof line_buf, fd) != 0) { /* Skip leading white space. */ char*cp = line_buf + strspn(line_buf, " \t\r\b\f"); /* Remove trailing white space. */ char*tail = cp + strlen(cp); char*arg; while (tail > cp) { if (! isspace((int)tail[-1])) break; tail -= 1; tail[0] = 0; } /* Skip empty lines */ if (*cp == 0) continue; /* Skip comment lines */ if (cp[0] == '#') continue; /* The arg points to the argument to the keyword. */ arg = strchr(cp, ':'); if (arg) *arg++ = 0; if (strcmp(cp,"D") == 0) { char*val = strchr(arg, '='); const char *valo = "1"; if (val) { *val++ = 0; valo = val; } define_macro(arg, valo, 0, 0); } else if (strcmp(cp,"I") == 0) { include_dir = realloc(include_dir, (include_cnt+1)*sizeof(char*)); include_dir[include_cnt] = strdup(arg); include_cnt += 1; } else if (strcmp(cp,"keyword") == 0) { char*buf = malloc(strlen(arg) + 2); buf[0] = '`'; strcpy(buf+1, optarg); define_macro(optarg, buf, 1, 0); free(buf); } else if ((strcmp(cp,"Ma") == 0) || (strcmp(cp,"Mi") == 0) || (strcmp(cp,"Mm") == 0) || (strcmp(cp,"Mp") == 0)) { if (dep_path) { fprintf(stderr, "duplicate -M flag.\n"); } else { dep_mode = cp[1]; dep_path = strdup(arg); } } else if (strcmp(cp,"relative include") == 0) { if (strcmp(arg, "true") == 0) { relative_include = 1; } else { relative_include = 0; } } else if (strcmp(cp,"vhdlpp") == 0) { if (vhdlpp_path) { fprintf(stderr, "Ignore multiple vhdlpp flags\n"); } else { vhdlpp_path = strdup(arg); } } else if (strcmp(cp,"vhdlpp-work") == 0) { if (vhdlpp_work) { fprintf(stderr, "Ignore duplicate vhdlpp-work flags\n"); } else { vhdlpp_work = strdup(arg); } } else if (strcmp(cp,"vhdlpp-libdir") == 0) { vhdlpp_libdir = realloc(vhdlpp_libdir, (vhdlpp_libdir_cnt+1)*sizeof(char*)); vhdlpp_libdir[vhdlpp_libdir_cnt] = strdup(arg); vhdlpp_libdir_cnt += 1; } else { fprintf(stderr, "%s: Invalid keyword %s\n", path, cp); } } fclose(fd); return 0; } /* * This function reads from a file a list of file names. Each name * starts with the first non-space character, and ends with the last * non-space character. Spaces in the middle are OK. */ static int flist_read_names(const char*path) { char line_buf[2048]; FILE*fd = fopen(path, "r"); if (fd == 0) { fprintf(stderr, "%s: unable to open for reading.\n", path); return 1; } while (fgets(line_buf, sizeof line_buf, fd) != 0) { char*cp = line_buf + strspn(line_buf, " \t\r\b\f"); char*tail = cp + strlen(cp); while (tail > cp) { if (! isspace((int)tail[-1])) break; tail -= 1; tail[0] = 0; } if (cp < tail) add_source_file(cp); } fclose(fd); return 0; } int main(int argc, char*argv[]) { int opt, idx; unsigned lp; const char*flist_path = 0; unsigned flag_errors = 0; char*out_path = 0; FILE*out; char*precomp_out_path = 0; FILE*precomp_out = NULL; /* Define preprocessor keywords that I plan to just pass. */ /* From 1364-2005 Chapter 19. */ define_macro("begin_keywords", "`begin_keywords", 1, 0); define_macro("celldefine", "`celldefine", 1, 0); define_macro("default_nettype", "`default_nettype", 1, 0); define_macro("end_keywords", "`end_keywords", 1, 0); define_macro("endcelldefine", "`endcelldefine", 1, 0); define_macro("line", "`line", 1, 0); define_macro("nounconnected_drive", "`nounconnected_drive", 1, 0); define_macro("pragma", "`pragma", 1, 0); define_macro("resetall", "`resetall", 1, 0); define_macro("timescale", "`timescale", 1, 0); define_macro("unconnected_drive", "`unconnected_drive", 1, 0); /* From 1364-2005 Annex D. */ define_macro("default_decay_time", "`default_decay_time", 1, 0); define_macro("default_trireg_strength", "`default_trireg_strength", 1, 0); define_macro("delay_mode_distributed", "`delay_mode_distributed", 1, 0); define_macro("delay_mode_path", "`delay_mode_path", 1, 0); define_macro("delay_mode_unit", "`delay_mode_unit", 1, 0); define_macro("delay_mode_zero", "`delay_mode_zero", 1, 0); /* From other places. */ define_macro("disable_portfaults", "`disable_portfaults", 1, 0); define_macro("enable_portfaults", "`enable_portfaults", 1, 0); define_macro("endprotect", "`endprotect", 1, 0); define_macro("nosuppress_faults", "`nosuppress_faults", 1, 0); define_macro("protect", "`protect", 1, 0); define_macro("suppress_faults", "`suppress_faults", 1, 0); define_macro("uselib", "`uselib", 1, 0); include_cnt = 2; include_dir = malloc(include_cnt*sizeof(char*)); include_dir[0] = 0; /* 0 is reserved for the current files path. */ include_dir[1] = strdup("."); while ((opt=getopt(argc, argv, "F:f:K:Lo:p:P:vV")) != EOF) switch (opt) { case 'F': flist_read_flags(optarg); break; case 'f': if (flist_path) { fprintf(stderr, "%s: duplicate -f flag\n", argv[0]); flag_errors += 1; } flist_path = optarg; break; case 'K': { char*buf = malloc(strlen(optarg) + 2); buf[0] = '`'; strcpy(buf+1, optarg); define_macro(optarg, buf, 1, 0); free(buf); break; } case 'L': line_direct_flag = 1; break; case 'o': if (out_path) { fprintf(stderr, "duplicate -o flag.\n"); } else { out_path = optarg; } break; case 'p': if (precomp_out_path) { fprintf(stderr, "duplicate -p flag.\n"); } else { precomp_out_path = optarg; } break; case 'P': { FILE*src = fopen(optarg, "rb"); if (src == 0) { perror(optarg); exit(1); } load_precompiled_defines(src); fclose(src); break; } case 'v': fprintf(stderr, "Icarus Verilog Preprocessor version " VERSION " (" VERSION_TAG ")\n\n"); fprintf(stderr, "%s\n\n", COPYRIGHT); fputs(NOTICE, stderr); verbose_flag = 1; break; case 'V': fprintf(stdout, "Icarus Verilog Preprocessor version " VERSION " (" VERSION_TAG ")\n\n"); fprintf(stdout, "%s\n", COPYRIGHT); fputs(NOTICE, stdout); return 0; default: flag_errors += 1; break; } if (flag_errors) { fprintf(stderr, "\nUsage: %s [-v][-L][-F][-f] ...\n" " -F - Get defines and includes from file\n" " -f - Read the sources listed in the file\n" " -K - Define a keyword macro that I just pass\n" " -L - Emit line number directives\n" " -o - Send the output to \n" " -p - Write precompiled defines to \n" " -P - Read precompiled defines from \n" " -v - Verbose\n" " -V - Print version information and quit\n", argv[0]); return flag_errors; } /* Collect the file names on the command line in the source file list, then if there is a file list, read more file names from there. */ for (idx = optind ; idx < argc ; idx += 1) add_source_file(argv[idx]); if (flist_path) { int rc = flist_read_names(flist_path); if (rc != 0) return rc; } /* Figure out what to use for an output file. Write to stdout if no path is specified. */ if (out_path) { out = fopen(out_path, "w"); if (out == 0) { perror(out_path); exit(1); } } else { out = stdout; } if (precomp_out_path) { precomp_out = fopen(precomp_out_path, "wb"); if (precomp_out == 0) { if (out_path) fclose(out); perror(precomp_out_path); exit(1); } } if (dep_path) { depend_file = fopen(dep_path, "a"); if (depend_file == 0) { if (out_path) fclose(out); if (precomp_out) fclose(precomp_out); perror(dep_path); exit(1); } } if (source_cnt == 0) { fprintf(stderr, "%s: No input files given.\n", argv[0]); if (out_path) fclose(out); if (depend_file) fclose(depend_file); if (precomp_out) fclose(precomp_out); return 1; } if (vhdlpp_work == 0) { vhdlpp_work = strdup("ivl_vhdl_work"); } /* Pass to the lexical analyzer the list of input file, and start scanning. */ reset_lexor(out, source_list); if (yylex()) { if (out_path) fclose(out); if (depend_file) fclose(depend_file); if (precomp_out) fclose(precomp_out); return -1; } destroy_lexor(); if (depend_file) fclose(depend_file); if (precomp_out) { dump_precompiled_defines(precomp_out); fclose(precomp_out); } if (out_path) fclose(out); /* Free the source and include directory lists. */ for (lp = 0; lp < source_cnt; lp += 1) { free(source_list[lp]); } free(source_list); for (lp = 0; lp < include_cnt; lp += 1) { free(include_dir[lp]); } free(include_dir); /* Free the VHDL library directories, the path and work directory. */ for (lp = 0; lp < vhdlpp_libdir_cnt; lp += 1) { free(vhdlpp_libdir[lp]); } free(vhdlpp_libdir); free(vhdlpp_path); free(vhdlpp_work); free_macros(); return error_count; } iverilog-10_1/lexor.lex000066400000000000000000001304611265551621300152120ustar00rootroot00000000000000%option prefix="VL" %option never-interactive %option nounput %{ /* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" //# define YYSTYPE lexval # include # include "compiler.h" # include "parse_misc.h" # include "parse_api.h" # include "parse.h" # include # include # include "lexor_keyword.h" # include "discipline.h" # include # define YY_USER_INIT reset_lexor(); # define yylval VLlval # define YY_NO_INPUT /* * Lexical location information is passed in the yylloc variable to th * parser. The file names, strings, are kept in a list so that I can * re-use them. The set_file_name function will return a pointer to * the name as it exists in the list (and delete the passed string.) * If the name is new, it will be added to the list. */ extern YYLTYPE yylloc; char* strdupnew(char const *str) { return str ? strcpy(new char [strlen(str)+1], str) : 0; } static const char* set_file_name(char*text) { perm_string path = filename_strings.make(text); delete[]text; /* Check this file name with the list of library file names. If there is a match, then turn on the pform_library_flag. This is how the parser knows that modules declared in this file are library modules. */ pform_library_flag = library_file_map[path]; return path; } void reset_lexor(); static void line_directive(); static void line_directive2(); verinum*make_unsized_binary(const char*txt); verinum*make_undef_highz_dec(const char*txt); verinum*make_unsized_dec(const char*txt); verinum*make_unsized_octal(const char*txt); verinum*make_unsized_hex(const char*txt); static int dec_buf_div2(char *buf); static void process_timescale(const char*txt); static void process_ucdrive(const char*txt); static list keyword_mask_stack; static int comment_enter; static bool in_module = false; static bool in_UDP = false; bool in_celldefine = false; UCDriveType uc_drive = UCD_NONE; /* * The parser sometimes needs to indicate to the lexor that the next * identifier needs to be understood in the context of a package. The * parser feeds back that left context with calls to the * lex_in_package_scope. */ static PPackage* in_package_scope = 0; void lex_in_package_scope(PPackage*pkg) { in_package_scope = pkg; } %} %x CCOMMENT %x PCOMMENT %x LCOMMENT %x CSTRING %s UDPTABLE %x PPTIMESCALE %x PPUCDRIVE %x PPDEFAULT_NETTYPE %x PPBEGIN_KEYWORDS %s EDGES %x REAL_SCALE W [ \t\b\f\r]+ S [afpnumkKMGT] TU [munpf] %% /* Recognize the various line directives. */ ^"#line"[ \t]+.+ { line_directive(); } ^[ \t]?"`line"[ \t]+.+ { line_directive2(); } [ \t\b\f\r] { ; } \n { yylloc.first_line += 1; } /* C++ style comments start with / / and run to the end of the current line. These are very easy to handle. The meta-comments format is a little more tricky to handle, but do what we can. */ /* The lexor detects "// synthesis translate_on/off" meta-comments, we handle them here by turning on/off a flag. The pform uses that flag to attach implicit attributes to "initial" and "always" statements. */ "//"{W}*"synthesis"{W}+"translate_on"{W}*\n { pform_mc_translate_on(true); } "//"{W}*"synthesis"{W}+"translate_off"{W}*\n { pform_mc_translate_on(false); } "//" { comment_enter = YY_START; BEGIN(LCOMMENT); } . { yymore(); } \n { yylloc.first_line += 1; BEGIN(comment_enter); } /* The contents of C-style comments are ignored, like white space. */ "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); } . { ; } \n { yylloc.first_line += 1; } "*/" { BEGIN(comment_enter); } "(*" { return K_PSTAR; } "*)" { return K_STARP; } ".*" { return K_DOTSTAR; } "<<" { return K_LS; } "<<<" { return K_LS; /* Note: Functionally, <<< is the same as <<. */} ">>" { return K_RS; } ">>>" { return K_RSS; } "**" { return K_POW; } "<=" { return K_LE; } ">=" { return K_GE; } "=>" { return K_EG; } "+=>"|"-=>" { /* * Resolve the ambiguity between the += assignment * operator and +=> polarity edge path operator * * +=> should be treated as two separate tokens '+' and * '=>' (K_EG), therefore we only consume the first * character of the matched pattern i.e. either + or - * and push back the rest of the matches text (=>) in * the input stream. */ yyless(1); return yytext[0]; } "*>" { return K_SG; } "==" { return K_EQ; } "!=" { return K_NE; } "===" { return K_CEQ; } "!==" { return K_CNE; } "||" { return K_LOR; } "&&" { return K_LAND; } "&&&" { return K_TAND; } "~|" { return K_NOR; } "~^" { return K_NXOR; } "^~" { return K_NXOR; } "~&" { return K_NAND; } "->" { return K_TRIGGER; } "+:" { return K_PO_POS; } "-:" { return K_PO_NEG; } "<+" { return K_CONTRIBUTE; } "+=" { return K_PLUS_EQ; } "-=" { return K_MINUS_EQ; } "*=" { return K_MUL_EQ; } "/=" { return K_DIV_EQ; } "%=" { return K_MOD_EQ; } "&=" { return K_AND_EQ; } "|=" { return K_OR_EQ; } "^=" { return K_XOR_EQ; } "<<=" { return K_LS_EQ; } ">>=" { return K_RS_EQ; } "<<<=" { return K_LS_EQ; } ">>>=" { return K_RSS_EQ; } "++" { return K_INCR; } "--" {return K_DECR; } "'{" { return K_LP; } "::" { return K_SCOPE_RES; } /* Watch out for the tricky case of (*). Cannot parse this as "(*" and ")", but since I know that this is really ( * ), replace it with "*" and return that. */ "("{W}*"*"{W}*")" { return '*'; } "]" { BEGIN(0); return yytext[0]; } [}{;:\[\],()#=.@&!?<>%|^~+*/-] { return yytext[0]; } \" { BEGIN(CSTRING); } \\\\ { yymore(); /* Catch \\, which is a \ escaping itself */ } \\\" { yymore(); /* Catch \", which is an escaped quote */ } \n { BEGIN(0); yylval.text = strdupnew(yytext); VLerror(yylloc, "Missing close quote of string."); yylloc.first_line += 1; return STRING; } \" { BEGIN(0); yylval.text = strdupnew(yytext); yylval.text[strlen(yytext)-1] = 0; return STRING; } . { yymore(); } /* The UDP Table is a unique lexical environment. These are most tokens that we can expect in a table. */ \(\?0\) { return '_'; } \(\?1\) { return '+'; } \(\?[xX]\) { return '%'; } \(\?\?\) { return '*'; } \(01\) { return 'r'; } \(0[xX]\) { return 'Q'; } \(b[xX]\) { return 'q'; } \(b0\) { return 'f'; /* b0 is 10|00, but only 10 is meaningful */} \(b1\) { return 'r'; /* b1 is 11|01, but only 01 is meaningful */} \(0\?\) { return 'P'; } \(10\) { return 'f'; } \(1[xX]\) { return 'M'; } \(1\?\) { return 'N'; } \([xX]0\) { return 'F'; } \([xX]1\) { return 'R'; } \([xX]\?\) { return 'B'; } [bB] { return 'b'; } [lL] { return 'l'; /* IVL extension */ } [hH] { return 'h'; /* IVL extension */ } [fF] { return 'f'; } [rR] { return 'r'; } [xX] { return 'x'; } [nN] { return 'n'; } [pP] { return 'p'; } [01\?\*\-:;] { return yytext[0]; } "01" { return K_edge_descriptor; } "0x" { return K_edge_descriptor; } "0z" { return K_edge_descriptor; } "10" { return K_edge_descriptor; } "1x" { return K_edge_descriptor; } "1z" { return K_edge_descriptor; } "x0" { return K_edge_descriptor; } "x1" { return K_edge_descriptor; } "z0" { return K_edge_descriptor; } "z1" { return K_edge_descriptor; } [a-zA-Z_][a-zA-Z0-9$_]* { int rc = lexor_keyword_code(yytext, yyleng); switch (rc) { case IDENTIFIER: yylval.text = strdupnew(yytext); if (strncmp(yylval.text,"PATHPULSE$", 10) == 0) rc = PATHPULSE_IDENTIFIER; break; case K_edge: BEGIN(EDGES); break; case K_module: case K_macromodule: in_module = true; break; case K_endmodule: in_module = false; break; case K_primitive: in_UDP = true; break; case K_endprimitive: in_UDP = false; break; case K_table: BEGIN(UDPTABLE); break; /* Translate these to checks if we already have or are * outside the declaration region. */ case K_timeunit: if (have_timeunit_decl) rc = K_timeunit_check; break; case K_timeprecision: if (have_timeprec_decl) rc = K_timeprecision_check; break; default: yylval.text = 0; break; } /* Special case: If this is part of a scoped name, then check the package for identifier details. For example, if the source file is foo::bar, the parse.y will note the PACKAGE_IDENTIFIER and "::" token and mark the "in_package_scope" variable. Then this lexor will see the identifier here and interpret it in the package scope. */ if (in_package_scope) { if (rc == IDENTIFIER) { if (data_type_t*type = pform_test_type_identifier(in_package_scope, yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; rc = TYPE_IDENTIFIER; } } in_package_scope = 0; return rc; } /* If this identifier names a discipline, then return this as a DISCIPLINE_IDENTIFIER and return the discipline as the value instead. */ if (rc == IDENTIFIER && gn_verilog_ams_flag) { perm_string tmp = lex_strings.make(yylval.text); map::iterator cur = disciplines.find(tmp); if (cur != disciplines.end()) { delete[]yylval.text; yylval.discipline = (*cur).second; rc = DISCIPLINE_IDENTIFIER; } } /* If this identifier names a previously declared package, then return this as a PACKAGE_IDENTIFIER instead. */ if (rc == IDENTIFIER && gn_system_verilog()) { if (PPackage*pkg = pform_test_package_identifier(yylval.text)) { delete[]yylval.text; yylval.package = pkg; rc = PACKAGE_IDENTIFIER; } } /* If this identifier names a previously declared type, then return this as a TYPE_IDENTIFIER instead. */ if (rc == IDENTIFIER && gn_system_verilog()) { if (data_type_t*type = pform_test_type_identifier(yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; rc = TYPE_IDENTIFIER; } } return rc; } \\[^ \t\b\f\r\n]+ { yylval.text = strdupnew(yytext+1); if (gn_system_verilog()) { if (PPackage*pkg = pform_test_package_identifier(yylval.text)) { delete[]yylval.text; yylval.package = pkg; return PACKAGE_IDENTIFIER; } } if (gn_system_verilog()) { if (data_type_t*type = pform_test_type_identifier(yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; return TYPE_IDENTIFIER; } } return IDENTIFIER; } \$([a-zA-Z0-9$_]+) { /* The 1364-1995 timing checks. */ if (strcmp(yytext,"$hold") == 0) return K_Shold; if (strcmp(yytext,"$nochange") == 0) return K_Snochange; if (strcmp(yytext,"$period") == 0) return K_Speriod; if (strcmp(yytext,"$recovery") == 0) return K_Srecovery; if (strcmp(yytext,"$setup") == 0) return K_Ssetup; if (strcmp(yytext,"$setuphold") == 0) return K_Ssetuphold; if (strcmp(yytext,"$skew") == 0) return K_Sskew; if (strcmp(yytext,"$width") == 0) return K_Swidth; /* The new 1364-2001 timing checks. */ if (strcmp(yytext,"$fullskew") == 0) return K_Sfullskew; if (strcmp(yytext,"$recrem") == 0) return K_Srecrem; if (strcmp(yytext,"$removal") == 0) return K_Sremoval; if (strcmp(yytext,"$timeskew") == 0) return K_Stimeskew; if (strcmp(yytext,"$attribute") == 0) return KK_attribute; yylval.text = strdupnew(yytext); return SYSTEM_IDENTIFIER; } \'[sS]?[dD][ \t]*[0-9][0-9_]* { yylval.number = make_unsized_dec(yytext); return BASED_NUMBER; } \'[sS]?[dD][ \t]*[xzXZ?]_* { yylval.number = make_undef_highz_dec(yytext); return BASED_NUMBER; } \'[sS]?[bB][ \t]*[0-1xzXZ?][0-1xzXZ?_]* { yylval.number = make_unsized_binary(yytext); return BASED_NUMBER; } \'[sS]?[oO][ \t]*[0-7xzXZ?][0-7xzXZ?_]* { yylval.number = make_unsized_octal(yytext); return BASED_NUMBER; } \'[sS]?[hH][ \t]*[0-9a-fA-FxzXZ?][0-9a-fA-FxzXZ?_]* { yylval.number = make_unsized_hex(yytext); return BASED_NUMBER; } \'[01xzXZ] { if (generation_flag < GN_VER2005_SV) { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: " << "Using SystemVerilog 'N bit vector. Use at least " << "-g2005-sv to remove this warning." << endl; } generation_t generation_save = generation_flag; generation_flag = GN_VER2005_SV; yylval.number = make_unsized_binary(yytext); generation_flag = generation_save; return UNBASED_NUMBER; } /* Decimal numbers are the usual. But watch out for the UDPTABLE mode, where there are no decimal numbers. Reject the match if we are in the UDPTABLE state. */ [0-9][0-9_]* { if (YY_START==UDPTABLE) { REJECT; } else { yylval.number = make_unsized_dec(yytext); based_size = yylval.number->as_ulong(); return DEC_NUMBER; } } /* This rule handles scaled time values for SystemVerilog. */ [0-9][0-9_]*(\.[0-9][0-9_]*)?{TU}?s { if(generation_flag & (GN_VER2005_SV | GN_VER2009 | GN_VER2012)) { yylval.text = strdupnew(yytext); return TIME_LITERAL; } else REJECT; } /* These rules handle the scaled real literals from Verilog-AMS. The value is a number with a single letter scale factor. If verilog-ams is not enabled, then reject this rule. If it is enabled, then collect the scale and use it to scale the value. */ [0-9][0-9_]*\.[0-9][0-9_]*/{S} { if (!gn_verilog_ams_flag) REJECT; BEGIN(REAL_SCALE); yymore(); } [0-9][0-9_]*/{S} { if (!gn_verilog_ams_flag) REJECT; BEGIN(REAL_SCALE); yymore(); } {S} { size_t token_len = strlen(yytext); char*tmp = new char[token_len + 5]; int scale = 0; strcpy(tmp, yytext); switch (tmp[token_len-1]) { case 'a': scale = -18; break; /* atto- */ case 'f': scale = -15; break; /* femto- */ case 'p': scale = -12; break; /* pico- */ case 'n': scale = -9; break; /* nano- */ case 'u': scale = -6; break; /* micro- */ case 'm': scale = -3; break; /* milli- */ case 'k': scale = 3; break; /* kilo- */ case 'K': scale = 3; break; /* kilo- */ case 'M': scale = 6; break; /* mega- */ case 'G': scale = 9; break; /* giga- */ case 'T': scale = 12; break; /* tera- */ default: assert(0); break; } snprintf(tmp+token_len-1, 5, "e%d", scale); yylval.realtime = new verireal(tmp); delete[]tmp; BEGIN(0); return REALTIME; } [0-9][0-9_]*\.[0-9][0-9_]*([Ee][+-]?[0-9][0-9_]*)? { yylval.realtime = new verireal(yytext); return REALTIME; } [0-9][0-9_]*[Ee][+-]?[0-9][0-9_]* { yylval.realtime = new verireal(yytext); return REALTIME; } /* Notice and handle the `timescale directive. */ ^{W}?`timescale { BEGIN(PPTIMESCALE); } .* { process_timescale(yytext); } \n { if (in_module) { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " "`timescale directive can not be inside a module " "definition." << endl; error_count += 1; } yylloc.first_line += 1; BEGIN(0); } /* Notice and handle the `celldefine and `endcelldefine directives. */ ^{W}?`celldefine{W}? { in_celldefine = true; } ^{W}?`endcelldefine{W}? { in_celldefine = false; } /* Notice and handle the resetall directive. */ ^{W}?`resetall{W}? { if (in_module) { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " "`resetall directive can not be inside a module " "definition." << endl; error_count += 1; } else if (in_UDP) { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " "`resetall directive can not be inside a UDP " "definition." << endl; error_count += 1; } else { pform_set_default_nettype(NetNet::WIRE, yylloc.text, yylloc.first_line); in_celldefine = false; uc_drive = UCD_NONE; pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); } } /* Notice and handle the `unconnected_drive directive. */ ^{W}?`unconnected_drive { BEGIN(PPUCDRIVE); } .* { process_ucdrive(yytext); } \n { if (in_module) { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " "`unconnected_drive directive can not be inside a " "module definition." << endl; error_count += 1; } yylloc.first_line += 1; BEGIN(0); } ^{W}?`nounconnected_drive{W}? { if (in_module) { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " "`nounconnected_drive directive can not be inside a " "module definition." << endl; error_count += 1; } uc_drive = UCD_NONE; } /* These are directives that I do not yet support. I think that IVL should handle these, not an external preprocessor. */ /* From 1364-2005 Chapter 19. */ ^{W}?`pragme{W}?.* { } /* From 1364-2005 Annex D. */ ^{W}?`default_decay_time{W}?.* { } ^{W}?`default_trireg_strength{W}?.* { } ^{W}?`delay_mode_distributed{W}?.* { } ^{W}?`delay_mode_path{W}?.* { } ^{W}?`delay_mode_unit{W}?.* { } ^{W}?`delay_mode_zero{W}?.* { } /* From other places. */ ^{W}?`disable_portfaults{W}?.* { } ^{W}?`enable_portfaults{W}?.* { } `endprotect { } ^{W}?`nosuppress_faults{W}?.* { } `protect { } ^{W}?`suppress_faults{W}?.* { } ^{W}?`uselib{W}?.* { } ^{W}?`begin_keywords{W}? { BEGIN(PPBEGIN_KEYWORDS); } \"[a-zA-Z0-9 -\.]*\".* { keyword_mask_stack.push_front(lexor_keyword_mask); char*word = yytext+1; char*tail = strchr(word, '"'); tail[0] = 0; if (strcmp(word,"1364-1995") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995; } else if (strcmp(word,"1364-2001") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001 |GN_KEYWORDS_1364_2001_CONFIG; } else if (strcmp(word,"1364-2001-noconfig") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001; } else if (strcmp(word,"1364-2005") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001 |GN_KEYWORDS_1364_2001_CONFIG |GN_KEYWORDS_1364_2005; } else if (strcmp(word,"1800-2005") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001 |GN_KEYWORDS_1364_2001_CONFIG |GN_KEYWORDS_1364_2005 |GN_KEYWORDS_1800_2005; } else if (strcmp(word,"1800-2009") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001 |GN_KEYWORDS_1364_2001_CONFIG |GN_KEYWORDS_1364_2005 |GN_KEYWORDS_1800_2005 |GN_KEYWORDS_1800_2009; } else if (strcmp(word,"1800-2012") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001 |GN_KEYWORDS_1364_2001_CONFIG |GN_KEYWORDS_1364_2005 |GN_KEYWORDS_1800_2005 |GN_KEYWORDS_1800_2009 |GN_KEYWORDS_1800_2012; } else if (strcmp(word,"VAMS-2.3") == 0) { lexor_keyword_mask = GN_KEYWORDS_1364_1995 |GN_KEYWORDS_1364_2001 |GN_KEYWORDS_1364_2001_CONFIG |GN_KEYWORDS_1364_2005 |GN_KEYWORDS_VAMS_2_3; } else { fprintf(stderr, "%s:%d: Ignoring unknown keywords string: %s\n", yylloc.text, yylloc.first_line, word); } BEGIN(0); } .* { fprintf(stderr, "%s:%d: Malformed keywords specification: %s\n", yylloc.text, yylloc.first_line, yytext); BEGIN(0); } ^{W}?`end_keywords{W}?.* { if (!keyword_mask_stack.empty()) { lexor_keyword_mask = keyword_mask_stack.front(); keyword_mask_stack.pop_front(); } else { fprintf(stderr, "%s:%d: Mismatched end_keywords directive\n", yylloc.text, yylloc.first_line); } } /* Notice and handle the default_nettype directive. The lexor detects the default_nettype keyword, and the second part of the rule collects the rest of the line and processes it. We only need to look for the first work, and interpret it. */ `default_nettype{W}? { BEGIN(PPDEFAULT_NETTYPE); } .* { NetNet::Type net_type; size_t wordlen = strcspn(yytext, " \t\f\r\n"); yytext[wordlen] = 0; /* Add support for other wire types and better error detection. */ if (strcmp(yytext,"wire") == 0) { net_type = NetNet::WIRE; } else if (strcmp(yytext,"tri") == 0) { net_type = NetNet::TRI; } else if (strcmp(yytext,"tri0") == 0) { net_type = NetNet::TRI0; } else if (strcmp(yytext,"tri1") == 0) { net_type = NetNet::TRI1; } else if (strcmp(yytext,"wand") == 0) { net_type = NetNet::WAND; } else if (strcmp(yytext,"triand") == 0) { net_type = NetNet::TRIAND; } else if (strcmp(yytext,"wor") == 0) { net_type = NetNet::WOR; } else if (strcmp(yytext,"trior") == 0) { net_type = NetNet::TRIOR; } else if (strcmp(yytext,"none") == 0) { net_type = NetNet::NONE; } else { cerr << yylloc.text << ":" << yylloc.first_line << ": error: Net type " << yytext << " is not a valid (or supported)" << " default net type." << endl; net_type = NetNet::WIRE; error_count += 1; } pform_set_default_nettype(net_type, yylloc.text, yylloc.first_line); } \n { yylloc.first_line += 1; BEGIN(0); } /* These are directives that are not supported by me and should have been handled by an external preprocessor such as ivlpp. */ ^{W}?`define{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `define not supported. Use an external preprocessor." << endl; } ^{W}?`else{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `else not supported. Use an external preprocessor." << endl; } ^{W}?`elsif{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `elsif not supported. Use an external preprocessor." << endl; } ^{W}?`endif{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `endif not supported. Use an external preprocessor." << endl; } ^{W}?`ifdef{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `ifdef not supported. Use an external preprocessor." << endl; } ^{W}?`ifndef{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `ifndef not supported. Use an external preprocessor." << endl; } ^`include{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `include not supported. Use an external preprocessor." << endl; } ^`undef{W}?.* { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: `undef not supported. Use an external preprocessor." << endl; } `{W} { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " << "Stray tic (`) here. Perhaps you put white space" << endl; cerr << yylloc.text << ":" << yylloc.first_line << ": : " << "between the tic and preprocessor directive?" << endl; error_count += 1; } . { return yytext[0]; } /* Final catchall. something got lost or mishandled. */ /* XXX Should we tell the user something about the lexical state? */ <*>.|\n { cerr << yylloc.text << ":" << yylloc.first_line << ": error: unmatched character ("; if (isprint(yytext[0])) cerr << yytext[0]; else cerr << "hex " << hex << ((unsigned char) yytext[0]); cerr << ")" << endl; error_count += 1; } %% /* * The UDP state table needs some slightly different treatment by the * lexor. The level characters are normally accepted as other things, * so the parser needs to switch my mode when it believes in needs to. */ void lex_end_table() { BEGIN(INITIAL); } static unsigned truncate_to_integer_width(verinum::V*bits, unsigned size) { if (size <= integer_width) return size; verinum::V pad = bits[size-1]; if (pad == verinum::V1) pad = verinum::V0; for (unsigned idx = integer_width; idx < size; idx += 1) { if (bits[idx] != pad) { yywarn(yylloc, "Unsized numeric constant truncated to integer width."); break; } } return integer_width; } verinum*make_unsized_binary(const char*txt) { bool sign_flag = false; bool single_flag = false; const char*ptr = txt; assert(*ptr == '\''); ptr += 1; if (tolower(*ptr) == 's') { sign_flag = true; ptr += 1; } assert((tolower(*ptr) == 'b') || (generation_flag >= GN_VER2005_SV)); if (tolower(*ptr) == 'b') { ptr += 1; } else { assert(sign_flag == false); single_flag = true; } while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) ptr += 1; unsigned size = 0; for (const char*idx = ptr ; *idx ; idx += 1) if (*idx != '_') size += 1; if ((based_size > 0) && (size > based_size)) yywarn(yylloc, "extra digits given for sized binary constant."); verinum::V*bits = new verinum::V[size]; unsigned idx = size; while (*ptr) { switch (ptr[0]) { case '0': bits[--idx] = verinum::V0; break; case '1': bits[--idx] = verinum::V1; break; case 'z': case 'Z': case '?': bits[--idx] = verinum::Vz; break; case 'x': case 'X': bits[--idx] = verinum::Vx; break; case '_': break; default: fprintf(stderr, "%c\n", ptr[0]); assert(0); } ptr += 1; } if (gn_strict_expr_width_flag && (based_size == 0)) size = truncate_to_integer_width(bits, size); verinum*out = new verinum(bits, size, false); out->has_sign(sign_flag); out->is_single(single_flag); delete[]bits; return out; } verinum*make_unsized_octal(const char*txt) { bool sign_flag = false; const char*ptr = txt; assert(*ptr == '\''); ptr += 1; if (tolower(*ptr) == 's') { sign_flag = true; ptr += 1; } assert(tolower(*ptr) == 'o'); ptr += 1; while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) ptr += 1; unsigned size = 0; for (const char*idx = ptr ; *idx ; idx += 1) if (*idx != '_') size += 3; if (based_size > 0) { int rem = based_size % 3; if (rem != 0) based_size += 3 - rem; if (size > based_size) yywarn(yylloc, "extra digits given for sized octal constant."); } verinum::V*bits = new verinum::V[size]; unsigned idx = size; while (*ptr) { unsigned val; switch (ptr[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': val = *ptr - '0'; bits[--idx] = (val&4) ? verinum::V1 : verinum::V0; bits[--idx] = (val&2) ? verinum::V1 : verinum::V0; bits[--idx] = (val&1) ? verinum::V1 : verinum::V0; break; case 'x': case 'X': bits[--idx] = verinum::Vx; bits[--idx] = verinum::Vx; bits[--idx] = verinum::Vx; break; case 'z': case 'Z': case '?': bits[--idx] = verinum::Vz; bits[--idx] = verinum::Vz; bits[--idx] = verinum::Vz; break; case '_': break; default: assert(0); } ptr += 1; } if (gn_strict_expr_width_flag && (based_size == 0)) size = truncate_to_integer_width(bits, size); verinum*out = new verinum(bits, size, false); out->has_sign(sign_flag); delete[]bits; return out; } verinum*make_unsized_hex(const char*txt) { bool sign_flag = false; const char*ptr = txt; assert(*ptr == '\''); ptr += 1; if (tolower(*ptr) == 's') { sign_flag = true; ptr += 1; } assert(tolower(*ptr) == 'h'); ptr += 1; while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) ptr += 1; unsigned size = 0; for (const char*idx = ptr ; *idx ; idx += 1) if (*idx != '_') size += 4; if (based_size > 0) { int rem = based_size % 4; if (rem != 0) based_size += 4 - rem; if (size > based_size) yywarn(yylloc, "extra digits given for sized hex constant."); } verinum::V*bits = new verinum::V[size]; unsigned idx = size; while (*ptr) { unsigned val; switch (ptr[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': val = *ptr - '0'; bits[--idx] = (val&8) ? verinum::V1 : verinum::V0; bits[--idx] = (val&4) ? verinum::V1 : verinum::V0; bits[--idx] = (val&2) ? verinum::V1 : verinum::V0; bits[--idx] = (val&1) ? verinum::V1 : verinum::V0; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': val = tolower(*ptr) - 'a' + 10; bits[--idx] = (val&8) ? verinum::V1 : verinum::V0; bits[--idx] = (val&4) ? verinum::V1 : verinum::V0; bits[--idx] = (val&2) ? verinum::V1 : verinum::V0; bits[--idx] = (val&1) ? verinum::V1 : verinum::V0; break; case 'x': case 'X': bits[--idx] = verinum::Vx; bits[--idx] = verinum::Vx; bits[--idx] = verinum::Vx; bits[--idx] = verinum::Vx; break; case 'z': case 'Z': case '?': bits[--idx] = verinum::Vz; bits[--idx] = verinum::Vz; bits[--idx] = verinum::Vz; bits[--idx] = verinum::Vz; break; case '_': break; default: assert(0); } ptr += 1; } if (gn_strict_expr_width_flag && (based_size == 0)) size = truncate_to_integer_width(bits, size); verinum*out = new verinum(bits, size, false); out->has_sign(sign_flag); delete[]bits; return out; } /* Divide the integer given by the string by 2. Return the remainder bit. */ static int dec_buf_div2(char *buf) { int partial; int len = strlen(buf); char *dst_ptr; int pos; partial = 0; pos = 0; /* dst_ptr overwrites buf, but all characters that are overwritten were already used by the reader. */ dst_ptr = buf; while(buf[pos] == '0') ++pos; for(; pos= 2){ *dst_ptr = partial/2 + '0'; partial = partial & 1; ++dst_ptr; } else{ *dst_ptr = '0'; ++dst_ptr; } } // If result of division was zero string, it should remain that way. // Don't eat the last zero... if (dst_ptr == buf){ *dst_ptr = '0'; ++dst_ptr; } *dst_ptr = 0; return partial; } /* Support a single x, z or ? as a decimal constant (from 1364-2005). */ verinum* make_undef_highz_dec(const char* ptr) { bool signed_flag = false; assert(*ptr == '\''); /* The number may have decorations of the form 'sd, possibly with space between the d and the . Also, the 's' is optional, and marks the number as signed. */ ptr += 1; if (tolower(*ptr) == 's') { signed_flag = true; ptr += 1; } assert(tolower(*ptr) == 'd'); ptr += 1; while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) ptr += 1; /* Process the code. */ verinum::V* bits = new verinum::V[1]; switch (*ptr) { case 'x': case 'X': bits[0] = verinum::Vx; break; case 'z': case 'Z': case '?': bits[0] = verinum::Vz; break; default: assert(0); } ptr += 1; while (*ptr == '_') ptr += 1; assert(*ptr == 0); verinum*out = new verinum(bits, 1, false); out->has_sign(signed_flag); delete[]bits; return out; } /* * Making a decimal number is much easier than the other base numbers * because there are no z or x values to worry about. It is much * harder than other base numbers because the width needed in bits is * hard to calculate. */ verinum*make_unsized_dec(const char*ptr) { char buf[4096]; bool signed_flag = false; unsigned idx; if (ptr[0] == '\'') { /* The number has decorations of the form 'sd, possibly with space between the d and the . Also, the 's' is optional, and marks the number as signed. */ ptr += 1; if (tolower(*ptr) == 's') { signed_flag = true; ptr += 1; } assert(tolower(*ptr) == 'd'); ptr += 1; while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) ptr += 1; } else { /* ... or an undecorated decimal number is passed it. These numbers are treated as signed decimal. */ assert(isdigit(*ptr)); signed_flag = true; } /* Copy the digits into a buffer that I can use to do in-place decimal divides. */ idx = 0; while ((idx < sizeof buf) && (*ptr != 0)) { if (*ptr == '_') { ptr += 1; continue; } buf[idx++] = *ptr++; } if (idx == sizeof buf) { fprintf(stderr, "Ridiculously long" " decimal constant will be truncated!\n"); idx -= 1; } buf[idx] = 0; unsigned tmp_size = idx * 4 + 1; verinum::V *bits = new verinum::V[tmp_size]; idx = 0; while (idx < tmp_size) { int rem = dec_buf_div2(buf); bits[idx++] = (rem == 1) ? verinum::V1 : verinum::V0; } assert(strcmp(buf, "0") == 0); /* Now calculate the minimum number of bits needed to represent this unsigned number. */ unsigned size = tmp_size; while ((size > 1) && (bits[size-1] == verinum::V0)) size -= 1; /* Now account for the signedness. Don't leave a 1 in the high bit if this is a signed number. */ if (signed_flag && (bits[size-1] == verinum::V1)) { size += 1; assert(size <= tmp_size); } /* Since we never have the real number of bits that a decimal number represents we do not check for extra bits. */ // if (based_size > 0) { } if (gn_strict_expr_width_flag && (based_size == 0)) size = truncate_to_integer_width(bits, size); verinum*res = new verinum(bits, size, false); res->has_sign(signed_flag); delete[]bits; return res; } /* * Convert the string to a time unit or precision. * Returns true on failure. */ static bool get_timescale_const(const char *&cp, int &res, bool is_unit) { /* Check for the 1 digit. */ if (*cp != '1') { if (is_unit) { VLerror(yylloc, "Invalid `timescale unit constant " "(1st digit)"); } else { VLerror(yylloc, "Invalid `timescale precision constant " "(1st digit)"); } return true; } cp += 1; /* Check the number of zeros after the 1. */ res = strspn(cp, "0"); if (res > 2) { if (is_unit) { VLerror(yylloc, "Invalid `timescale unit constant " "(number of zeros)"); } else { VLerror(yylloc, "Invalid `timescale precision constant " "(number of zeros)"); } return true; } cp += res; /* Skip any space between the digits and the scaling string. */ cp += strspn(cp, " \t"); /* Now process the scaling string. */ if (strncmp("s", cp, 1) == 0) { res -= 0; cp += 1; return false; } else if (strncmp("ms", cp, 2) == 0) { res -= 3; cp += 2; return false; } else if (strncmp("us", cp, 2) == 0) { res -= 6; cp += 2; return false; } else if (strncmp("ns", cp, 2) == 0) { res -= 9; cp += 2; return false; } else if (strncmp("ps", cp, 2) == 0) { res -= 12; cp += 2; return false; } else if (strncmp("fs", cp, 2) == 0) { res -= 15; cp += 2; return false; } if (is_unit) { VLerror(yylloc, "Invalid `timescale unit scale"); } else { VLerror(yylloc, "Invalid `timescale precision scale"); } return true; } /* * process either a pull0 or a pull1. */ static void process_ucdrive(const char*txt) { UCDriveType ucd = UCD_NONE; const char*cp = txt + strspn(txt, " \t"); /* Skip the space after the `unconnected_drive directive. */ if (cp == txt) { VLerror(yylloc, "Space required after `unconnected_drive " "directive."); return; } /* Check for the pull keyword. */ if (strncmp("pull", cp, 4) != 0) { VLerror(yylloc, "pull required for `unconnected_drive " "directive."); return; } cp += 4; if (*cp == '0') ucd = UCD_PULL0; else if (*cp == '1') ucd = UCD_PULL1; else { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " "`unconnected_drive does not support 'pull" << *cp << "'." << endl; error_count += 1; return; } cp += 1; /* Verify that only space and/or a single line comment is left. */ cp += strspn(cp, " \t"); if (strncmp(cp, "//", 2) != 0 && (size_t)(cp-yytext) != strlen(yytext)) { VLerror(yylloc, "Invalid `unconnected_drive directive (extra " "garbage after precision)."); return; } uc_drive = ucd; } /* * The timescale parameter has the form: * " xs / xs" */ static void process_timescale(const char*txt) { const char*cp = txt + strspn(txt, " \t"); /* Skip the space after the `timescale directive. */ if (cp == txt) { VLerror(yylloc, "Space required after `timescale directive."); return; } int unit = 0; int prec = 0; /* Get the time units. */ if (get_timescale_const(cp, unit, true)) return; /* Skip any space after the time units, the '/' and any * space after the '/'. */ cp += strspn(cp, " \t"); if (*cp != '/') { VLerror(yylloc, "`timescale separator '/' appears to be missing."); return; } cp += 1; cp += strspn(cp, " \t"); /* Get the time precision. */ if (get_timescale_const(cp, prec, false)) return; /* Verify that only space and/or a single line comment is left. */ cp += strspn(cp, " \t"); if (strncmp(cp, "//", 2) != 0 && (size_t)(cp-yytext) != strlen(yytext)) { VLerror(yylloc, "Invalid `timescale directive (extra garbage " "after precision)."); return; } /* The time unit must be greater than or equal to the precision. */ if (unit < prec) { VLerror(yylloc, "error: `timescale unit must not be less than " "the precision."); return; } pform_set_timescale(unit, prec, yylloc.text, yylloc.first_line); } int yywrap() { return 1; } /* * The line directive matches lines of the form #line "foo" N and * calls this function. Here I parse out the file name and line * number, and change the yylloc to suite. */ static void line_directive() { char *cpr; /* Skip any leading space. */ char *cp = strchr(yytext, '#'); /* Skip the #line directive. */ assert(strncmp(cp, "#line", 5) == 0); cp += 5; /* Skip the space after the #line directive. */ cp += strspn(cp, " \t"); /* Find the starting " and skip it. */ char*fn_start = strchr(cp, '"'); if (cp != fn_start) { VLerror(yylloc, "Invalid #line directive (file name start)."); return; } fn_start += 1; /* Find the last ". */ char*fn_end = strrchr(fn_start, '"'); if (!fn_end) { VLerror(yylloc, "Invalid #line directive (file name end)."); return; } /* Copy the file name and assign it to yylloc. */ char*buf = new char[fn_end-fn_start+1]; strncpy(buf, fn_start, fn_end-fn_start); buf[fn_end-fn_start] = 0; /* Skip the space after the file name. */ cp = fn_end; cp += 1; cpr = cp; cpr += strspn(cp, " \t"); if (cp == cpr) { VLerror(yylloc, "Invalid #line directive (missing space after " "file name)."); delete[] buf; return; } cp = cpr; /* Get the line number and verify that it is correct. */ unsigned long lineno = strtoul(cp, &cpr, 10); if (cp == cpr) { VLerror(yylloc, "Invalid line number for #line directive."); delete[] buf; return; } cp = cpr; /* Verify that only space is left. */ cpr += strspn(cp, " \t"); if ((size_t)(cpr-yytext) != strlen(yytext)) { VLerror(yylloc, "Invalid #line directive (extra garbage after " "line number)."); delete[] buf; return; } /* Now we can assign the new values to yyloc. */ yylloc.text = set_file_name(buf); yylloc.first_line = lineno; } /* * The line directive matches lines of the form `line N "foo" M and * calls this function. Here I parse out the file name and line * number, and change the yylloc to suite. M is ignored. */ static void line_directive2() { char *cpr; /* Skip any leading space. */ char *cp = strchr(yytext, '`'); /* Skip the `line directive. */ assert(strncmp(cp, "`line", 5) == 0); cp += 5; /* strtoul skips leading space. */ unsigned long lineno = strtoul(cp, &cpr, 10); if (cp == cpr) { VLerror(yylloc, "Invalid line number for `line directive."); return; } lineno -= 1; cp = cpr; /* Skip the space between the line number and the file name. */ cpr += strspn(cp, " \t"); if (cp == cpr) { VLerror(yylloc, "Invalid `line directive (missing space after " "line number)."); return; } cp = cpr; /* Find the starting " and skip it. */ char*fn_start = strchr(cp, '"'); if (cp != fn_start) { VLerror(yylloc, "Invalid `line directive (file name start)."); return; } fn_start += 1; /* Find the last ". */ char*fn_end = strrchr(fn_start, '"'); if (!fn_end) { VLerror(yylloc, "Invalid `line directive (file name end)."); return; } /* Skip the space after the file name. */ cp = fn_end + 1; cpr = cp; cpr += strspn(cp, " \t"); if (cp == cpr) { VLerror(yylloc, "Invalid `line directive (missing space after " "file name)."); return; } cp = cpr; /* Check that the level is correct, we do not need the level. */ if (strspn(cp, "012") != 1) { VLerror(yylloc, "Invalid level for `line directive."); return; } cp += 1; /* Verify that only space and/or a single line comment is left. */ cp += strspn(cp, " \t"); if (strncmp(cp, "//", 2) != 0 && (size_t)(cp-yytext) != strlen(yytext)) { VLerror(yylloc, "Invalid `line directive (extra garbage after " "level)."); return; } /* Copy the file name and assign it and the line number to yylloc. */ char*buf = new char[fn_end-fn_start+1]; strncpy(buf, fn_start, fn_end-fn_start); buf[fn_end-fn_start] = 0; yylloc.text = set_file_name(buf); yylloc.first_line = lineno; } extern FILE*vl_input; void reset_lexor() { yyrestart(vl_input); yylloc.first_line = 1; /* Announce the first file name. */ yylloc.text = set_file_name(strdupnew(vl_file.c_str())); } /* * Modern version of flex (>=2.5.9) can clean up the scanner data. */ void destroy_lexor() { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif } iverilog-10_1/lexor_keyword.gperf000066400000000000000000000370671265551621300173010ustar00rootroot00000000000000/* * We need this to prevent -Wextra (-W) from complaining that the mask and * tokenType values are not initialized for the empty table entries. */ %define initializer-suffix ,0,0 %language=C++ %define class-name Lkwd %{ /* Command-line: gperf -o -i 7 -C -k '1-4,6,9,$' -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf */ #include "config.h" #include "parse_misc.h" #include "parse.h" #include #include "lexor_keyword.h" #include "compiler.h" %} struct lexor_keyword { const char*name; int mask; int tokenType; }; %% above, GN_KEYWORDS_VAMS_2_3, K_above abs, GN_KEYWORDS_VAMS_2_3, K_abs absdelay, GN_KEYWORDS_VAMS_2_3, K_absdelay abstol, GN_KEYWORDS_VAMS_2_3, K_abstol accept_on, GN_KEYWORDS_1800_2009, K_accept_on access, GN_KEYWORDS_VAMS_2_3, K_access acos, GN_KEYWORDS_VAMS_2_3, K_acos acosh, GN_KEYWORDS_VAMS_2_3, K_acosh ac_stim, GN_KEYWORDS_VAMS_2_3, K_ac_stim alias, GN_KEYWORDS_1800_2005, K_alias aliasparam, GN_KEYWORDS_VAMS_2_3, K_aliasparam always, GN_KEYWORDS_1364_1995, K_always always_comb, GN_KEYWORDS_1800_2005, K_always_comb always_ff, GN_KEYWORDS_1800_2005, K_always_ff always_latch, GN_KEYWORDS_1800_2005, K_always_latch analog, GN_KEYWORDS_VAMS_2_3, K_analog analysis, GN_KEYWORDS_VAMS_2_3, K_analysis and, GN_KEYWORDS_1364_1995, K_and asin, GN_KEYWORDS_VAMS_2_3, K_asin asinh, GN_KEYWORDS_VAMS_2_3, K_asinh # This is defined by both SystemVerilog 1800-2005 and Verilog-AMS 2.3 assert, GN_KEYWORDS_1800_2005|GN_KEYWORDS_VAMS_2_3, K_assert assign, GN_KEYWORDS_1364_1995, K_assign assume, GN_KEYWORDS_1800_2005, K_assume atan, GN_KEYWORDS_VAMS_2_3, K_atan atan2, GN_KEYWORDS_VAMS_2_3, K_atan2 atanh, GN_KEYWORDS_VAMS_2_3, K_atanh automatic, GN_KEYWORDS_1364_2001, K_automatic before, GN_KEYWORDS_1800_2005, K_before begin, GN_KEYWORDS_1364_1995, K_begin bind, GN_KEYWORDS_1800_2005, K_bind bins, GN_KEYWORDS_1800_2005, K_bins binsof, GN_KEYWORDS_1800_2005, K_binsof bit, GN_KEYWORDS_1800_2005, K_bit branch, GN_KEYWORDS_VAMS_2_3, K_branch break, GN_KEYWORDS_1800_2005, K_break bool, GN_KEYWORDS_ICARUS, K_bool buf, GN_KEYWORDS_1364_1995, K_buf bufif0, GN_KEYWORDS_1364_1995, K_bufif0 bufif1, GN_KEYWORDS_1364_1995, K_bufif1 byte, GN_KEYWORDS_1800_2005, K_byte case, GN_KEYWORDS_1364_1995, K_case casex, GN_KEYWORDS_1364_1995, K_casex casez, GN_KEYWORDS_1364_1995, K_casez ceil, GN_KEYWORDS_VAMS_2_3, K_ceil cell, GN_KEYWORDS_1364_2001_CONFIG, K_cell chandle, GN_KEYWORDS_1800_2005, K_chandle checker, GN_KEYWORDS_1800_2009, K_checker class, GN_KEYWORDS_1800_2005, K_class clocking, GN_KEYWORDS_1800_2005, K_clocking cmos, GN_KEYWORDS_1364_1995, K_cmos config, GN_KEYWORDS_1364_2001_CONFIG, K_config connect, GN_KEYWORDS_VAMS_2_3, K_connect connectmodule, GN_KEYWORDS_VAMS_2_3, K_connectmodule connectrules, GN_KEYWORDS_VAMS_2_3, K_connectrules const, GN_KEYWORDS_1800_2005, K_const constraint, GN_KEYWORDS_1800_2005, K_constraint context, GN_KEYWORDS_1800_2005, K_context continue, GN_KEYWORDS_1800_2005, K_continue continuous, GN_KEYWORDS_VAMS_2_3, K_continuous cos, GN_KEYWORDS_VAMS_2_3, K_cos cosh, GN_KEYWORDS_VAMS_2_3, K_cosh cover, GN_KEYWORDS_1800_2005, K_cover covergroup, GN_KEYWORDS_1800_2005, K_covergroup coverpoint, GN_KEYWORDS_1800_2005, K_coverpoint cross, GN_KEYWORDS_1800_2005, K_cross ddt, GN_KEYWORDS_VAMS_2_3, K_ddt ddt_nature, GN_KEYWORDS_VAMS_2_3, K_ddt_nature ddx, GN_KEYWORDS_VAMS_2_3, K_ddx deassign, GN_KEYWORDS_1364_1995, K_deassign default, GN_KEYWORDS_1364_1995, K_default defparam, GN_KEYWORDS_1364_1995, K_defparam design, GN_KEYWORDS_1364_2001_CONFIG, K_design disable, GN_KEYWORDS_1364_1995, K_disable discipline, GN_KEYWORDS_VAMS_2_3, K_discipline discrete, GN_KEYWORDS_VAMS_2_3, K_discrete dist, GN_KEYWORDS_1800_2005, K_dist do, GN_KEYWORDS_1800_2005, K_do domain, GN_KEYWORDS_VAMS_2_3, K_domain driver_update, GN_KEYWORDS_VAMS_2_3, K_driver_update edge, GN_KEYWORDS_1364_1995, K_edge else, GN_KEYWORDS_1364_1995, K_else end, GN_KEYWORDS_1364_1995, K_end endcase, GN_KEYWORDS_1364_1995, K_endcase endchecker, GN_KEYWORDS_1800_2009, K_endchecker endconfig, GN_KEYWORDS_1364_2001_CONFIG, K_endconfig endclass, GN_KEYWORDS_1800_2005, K_endclass endclocking, GN_KEYWORDS_1800_2005, K_endclocking endconnectrules, GN_KEYWORDS_VAMS_2_3, K_endconnectrules enddiscipline, GN_KEYWORDS_VAMS_2_3, K_enddiscipline endfunction, GN_KEYWORDS_1364_1995, K_endfunction endgenerate, GN_KEYWORDS_1364_2001, K_endgenerate endgroup, GN_KEYWORDS_1800_2005, K_endgroup endinterface, GN_KEYWORDS_1800_2005, K_endinterface endmodule, GN_KEYWORDS_1364_1995, K_endmodule endnature, GN_KEYWORDS_VAMS_2_3, K_endnature endpackage, GN_KEYWORDS_1800_2005, K_endpackage endparamset, GN_KEYWORDS_VAMS_2_3, K_endparamset endprimitive, GN_KEYWORDS_1364_1995, K_endprimitive endprogram, GN_KEYWORDS_1800_2005, K_endprogram endproperty, GN_KEYWORDS_1800_2005, K_endproperty endspecify, GN_KEYWORDS_1364_1995, K_endspecify endsequence, GN_KEYWORDS_1800_2005, K_endsequence endtable, GN_KEYWORDS_1364_1995, K_endtable endtask, GN_KEYWORDS_1364_1995, K_endtask enum, GN_KEYWORDS_1800_2005, K_enum event, GN_KEYWORDS_1364_1995, K_event eventually, GN_KEYWORDS_1800_2009, K_eventually exclude, GN_KEYWORDS_VAMS_2_3, K_exclude exp, GN_KEYWORDS_VAMS_2_3, K_exp expect, GN_KEYWORDS_1800_2005, K_expect export, GN_KEYWORDS_1800_2005, K_export extends, GN_KEYWORDS_1800_2005, K_extends extern, GN_KEYWORDS_1800_2005, K_extern final, GN_KEYWORDS_1800_2005, K_final final_step, GN_KEYWORDS_VAMS_2_3, K_final_step first_match, GN_KEYWORDS_1800_2005, K_first_match flicker_noise, GN_KEYWORDS_VAMS_2_3, K_flicker_noise floor, GN_KEYWORDS_VAMS_2_3, K_floor flow, GN_KEYWORDS_VAMS_2_3, K_flow for, GN_KEYWORDS_1364_1995, K_for foreach, GN_KEYWORDS_1800_2005, K_foreach force, GN_KEYWORDS_1364_1995, K_force forever, GN_KEYWORDS_1364_1995, K_forever fork, GN_KEYWORDS_1364_1995, K_fork forkjoin, GN_KEYWORDS_1800_2005, K_forkjoin from, GN_KEYWORDS_VAMS_2_3, K_from function, GN_KEYWORDS_1364_1995, K_function generate, GN_KEYWORDS_1364_2001, K_generate genvar, GN_KEYWORDS_1364_2001, K_genvar global, GN_KEYWORDS_1800_2009, K_global ground, GN_KEYWORDS_VAMS_2_3, K_ground highz0, GN_KEYWORDS_1364_1995, K_highz0 highz1, GN_KEYWORDS_1364_1995, K_highz1 hypot, GN_KEYWORDS_VAMS_2_3, K_hypot idt, GN_KEYWORDS_VAMS_2_3, K_idt idtmod, GN_KEYWORDS_VAMS_2_3, K_idtmod idt_nature, GN_KEYWORDS_VAMS_2_3, K_idt_nature if, GN_KEYWORDS_1364_1995, K_if iff, GN_KEYWORDS_1800_2005, K_iff ifnone, GN_KEYWORDS_1364_1995, K_ifnone ignore_bins, GN_KEYWORDS_1800_2005, K_ignore_bins illegal_bins, GN_KEYWORDS_1800_2005, K_illegal_bins implies, GN_KEYWORDS_1800_2009, K_implies implements, GN_KEYWORDS_1800_2012, K_implements import, GN_KEYWORDS_1800_2005, K_import incdir, GN_KEYWORDS_1364_2001_CONFIG, K_incdir include, GN_KEYWORDS_1364_2001_CONFIG, K_include inf, GN_KEYWORDS_VAMS_2_3, K_inf initial, GN_KEYWORDS_1364_1995, K_initial initial_step, GN_KEYWORDS_VAMS_2_3, K_initial_step inout, GN_KEYWORDS_1364_1995, K_inout input, GN_KEYWORDS_1364_1995, K_input inside, GN_KEYWORDS_1800_2005, K_inside instance, GN_KEYWORDS_1364_2001_CONFIG, K_instance int, GN_KEYWORDS_1800_2005, K_int integer, GN_KEYWORDS_1364_1995, K_integer interconnect, GN_KEYWORDS_1800_2012, K_interconnect interface, GN_KEYWORDS_1800_2005, K_interface intersect, GN_KEYWORDS_1800_2005, K_intersect join, GN_KEYWORDS_1364_1995, K_join join_any, GN_KEYWORDS_1800_2005, K_join_any join_none, GN_KEYWORDS_1800_2005, K_join_none laplace_nd, GN_KEYWORDS_VAMS_2_3, K_laplace_nd laplace_np, GN_KEYWORDS_VAMS_2_3, K_laplace_np laplace_zd, GN_KEYWORDS_VAMS_2_3, K_laplace_zd laplace_zp, GN_KEYWORDS_VAMS_2_3, K_laplace_zp large, GN_KEYWORDS_1364_1995, K_large last_crossing, GN_KEYWORDS_VAMS_2_3, K_last_crossing let, GN_KEYWORDS_1800_2009, K_let liblist, GN_KEYWORDS_1364_2001_CONFIG, K_liblist library, GN_KEYWORDS_1364_2001_CONFIG, K_library limexp, GN_KEYWORDS_VAMS_2_3, K_limexp ln, GN_KEYWORDS_VAMS_2_3, K_ln local, GN_KEYWORDS_1800_2005, K_local localparam, GN_KEYWORDS_1364_2001, K_localparam log, GN_KEYWORDS_VAMS_2_3, K_log # This is defined by SystemVerilog 1800-2005 and as an Icarus extension. logic, GN_KEYWORDS_1800_2005|GN_KEYWORDS_ICARUS, K_logic longint, GN_KEYWORDS_1800_2005, K_longint macromodule, GN_KEYWORDS_1364_1995, K_macromodule matches, GN_KEYWORDS_1800_2005, K_matches max, GN_KEYWORDS_VAMS_2_3, K_max medium, GN_KEYWORDS_1364_1995, K_medium merged, GN_KEYWORDS_VAMS_2_3, K_merged min, GN_KEYWORDS_VAMS_2_3, K_min modport, GN_KEYWORDS_1800_2005, K_modport module, GN_KEYWORDS_1364_1995, K_module nand, GN_KEYWORDS_1364_1995, K_nand nature, GN_KEYWORDS_VAMS_2_3, K_nature negedge, GN_KEYWORDS_1364_1995, K_negedge net_resolution, GN_KEYWORDS_VAMS_2_3, K_net_resolution nettype, GN_KEYWORDS_1800_2012, K_nettype new, GN_KEYWORDS_1800_2005, K_new nexttime, GN_KEYWORDS_1800_2009, K_nexttime nmos, GN_KEYWORDS_1364_1995, K_nmos noise_table, GN_KEYWORDS_VAMS_2_3, K_noise_table nor, GN_KEYWORDS_1364_1995, K_nor noshowcancelled, GN_KEYWORDS_1364_2001, K_noshowcancelled not, GN_KEYWORDS_1364_1995, K_not notif0, GN_KEYWORDS_1364_1995, K_notif0 notif1, GN_KEYWORDS_1364_1995, K_notif1 null, GN_KEYWORDS_1800_2005, K_null or, GN_KEYWORDS_1364_1995, K_or output, GN_KEYWORDS_1364_1995, K_output package, GN_KEYWORDS_1800_2005, K_package packed, GN_KEYWORDS_1800_2005, K_packed parameter, GN_KEYWORDS_1364_1995, K_parameter paramset, GN_KEYWORDS_VAMS_2_3, K_paramset pmos, GN_KEYWORDS_1364_1995, K_pmos posedge, GN_KEYWORDS_1364_1995, K_posedge potential, GN_KEYWORDS_VAMS_2_3, K_potential pow, GN_KEYWORDS_VAMS_2_3, K_pow primitive, GN_KEYWORDS_1364_1995, K_primitive priority, GN_KEYWORDS_1800_2005, K_priority program, GN_KEYWORDS_1800_2005, K_program property, GN_KEYWORDS_1800_2005, K_property protected, GN_KEYWORDS_1800_2005, K_protected pull0, GN_KEYWORDS_1364_1995, K_pull0 pull1, GN_KEYWORDS_1364_1995, K_pull1 pulldown, GN_KEYWORDS_1364_1995, K_pulldown pullup, GN_KEYWORDS_1364_1995, K_pullup pulsestyle_onevent, GN_KEYWORDS_1364_2001, K_pulsestyle_onevent pulsestyle_ondetect, GN_KEYWORDS_1364_2001, K_pulsestyle_ondetect pure, GN_KEYWORDS_1800_2005, K_pure rand, GN_KEYWORDS_1800_2005, K_rand randc, GN_KEYWORDS_1800_2005, K_randc randcase, GN_KEYWORDS_1800_2005, K_randcase randsequence, GN_KEYWORDS_1800_2005, K_randsequence rcmos, GN_KEYWORDS_1364_1995, K_rcmos real, GN_KEYWORDS_1364_1995, K_real realtime, GN_KEYWORDS_1364_1995, K_realtime ref, GN_KEYWORDS_1800_2005, K_ref reg, GN_KEYWORDS_1364_1995, K_reg reject_on, GN_KEYWORDS_1800_2009, K_reject_on release, GN_KEYWORDS_1364_1995, K_release repeat, GN_KEYWORDS_1364_1995, K_repeat resolveto, GN_KEYWORDS_VAMS_2_3, K_resolveto restrict, GN_KEYWORDS_1800_2009, K_restrict return, GN_KEYWORDS_1800_2005, K_return rnmos, GN_KEYWORDS_1364_1995, K_rnmos rpmos, GN_KEYWORDS_1364_1995, K_rpmos rtran, GN_KEYWORDS_1364_1995, K_rtran rtranif0, GN_KEYWORDS_1364_1995, K_rtranif0 rtranif1, GN_KEYWORDS_1364_1995, K_rtranif1 s_always, GN_KEYWORDS_1800_2009, K_s_always s_eventually, GN_KEYWORDS_1800_2009, K_s_eventually s_nexttime, GN_KEYWORDS_1800_2009, K_s_nexttime s_until, GN_KEYWORDS_1800_2009, K_s_until s_until_with, GN_KEYWORDS_1800_2009, K_s_until_with scalared, GN_KEYWORDS_1364_1995, K_scalared sequence, GN_KEYWORDS_1800_2005, K_sequence shortint, GN_KEYWORDS_1800_2005, K_shortint shortreal, GN_KEYWORDS_1800_2005, K_shortreal showcancelled, GN_KEYWORDS_1364_2001, K_showcancelled signed, GN_KEYWORDS_1364_2001, K_signed sin, GN_KEYWORDS_VAMS_2_3, K_sin sinh, GN_KEYWORDS_VAMS_2_3, K_sinh slew, GN_KEYWORDS_VAMS_2_3, K_slew small, GN_KEYWORDS_1364_1995, K_small soft, GN_KEYWORDS_1800_2012, K_soft solve, GN_KEYWORDS_1800_2005, K_solve specify, GN_KEYWORDS_1364_1995, K_specify specparam, GN_KEYWORDS_1364_1995, K_specparam split, GN_KEYWORDS_VAMS_2_3, K_split sqrt, GN_KEYWORDS_VAMS_2_3, K_sqrt static, GN_KEYWORDS_1800_2005, K_static # This is defined by both SystemVerilog 1800-2005 and Verilog-AMS 2.3 string, GN_KEYWORDS_1800_2005|GN_KEYWORDS_VAMS_2_3, K_string strong, GN_KEYWORDS_1800_2009, K_strong strong0, GN_KEYWORDS_1364_1995, K_strong0 strong1, GN_KEYWORDS_1364_1995, K_strong1 struct, GN_KEYWORDS_1800_2005, K_struct super, GN_KEYWORDS_1800_2005, K_super supply0, GN_KEYWORDS_1364_1995, K_supply0 supply1, GN_KEYWORDS_1364_1995, K_supply1 sync_accept_on, GN_KEYWORDS_1800_2009, K_sync_accept_on sync_reject_on, GN_KEYWORDS_1800_2009, K_sync_reject_on table, GN_KEYWORDS_1364_1995, K_table tagged, GN_KEYWORDS_1800_2005, K_tagged tan, GN_KEYWORDS_VAMS_2_3, K_tan tanh, GN_KEYWORDS_VAMS_2_3, K_tanh task, GN_KEYWORDS_1364_1995, K_task this, GN_KEYWORDS_1800_2005, K_this throughout, GN_KEYWORDS_1800_2005, K_throughout time, GN_KEYWORDS_1364_1995, K_time timeprecision, GN_KEYWORDS_1800_2005, K_timeprecision timer, GN_KEYWORDS_VAMS_2_3, K_timer timeunit, GN_KEYWORDS_1800_2005, K_timeunit tran, GN_KEYWORDS_1364_1995, K_tran tranif0, GN_KEYWORDS_1364_1995, K_tranif0 tranif1, GN_KEYWORDS_1364_1995, K_tranif1 transition, GN_KEYWORDS_VAMS_2_3, K_transition tri, GN_KEYWORDS_1364_1995, K_tri tri0, GN_KEYWORDS_1364_1995, K_tri0 tri1, GN_KEYWORDS_1364_1995, K_tri1 triand, GN_KEYWORDS_1364_1995, K_triand trior, GN_KEYWORDS_1364_1995, K_trior trireg, GN_KEYWORDS_1364_1995, K_trireg type, GN_KEYWORDS_1800_2005, K_type typedef, GN_KEYWORDS_1800_2005, K_typedef union, GN_KEYWORDS_1800_2005, K_union unique, GN_KEYWORDS_1800_2005, K_unique unique0, GN_KEYWORDS_1800_2009, K_unique units, GN_KEYWORDS_VAMS_2_3, K_units # Reserved for future use! unsigned, GN_KEYWORDS_1364_2001, K_unsigned until, GN_KEYWORDS_1800_2009, K_until until_with, GN_KEYWORDS_1800_2009, K_until_with untyped, GN_KEYWORDS_1800_2009, K_untyped use, GN_KEYWORDS_1364_2001_CONFIG, K_use uwire, GN_KEYWORDS_1364_2005, K_uwire var, GN_KEYWORDS_1800_2005, K_var vectored, GN_KEYWORDS_1364_1995, K_vectored virtual, GN_KEYWORDS_1800_2005, K_virtual void, GN_KEYWORDS_1800_2005, K_void wait, GN_KEYWORDS_1364_1995, K_wait wait_order, GN_KEYWORDS_1800_2005, K_wait_order wand, GN_KEYWORDS_1364_1995, K_wand weak, GN_KEYWORDS_1800_2009, K_weak weak0, GN_KEYWORDS_1364_1995, K_weak0 weak1, GN_KEYWORDS_1364_1995, K_weak1 while, GN_KEYWORDS_1364_1995, K_while white_noise, GN_KEYWORDS_VAMS_2_3, K_white_noise wildcard, GN_KEYWORDS_1800_2005, K_wildcard wire, GN_KEYWORDS_1364_1995, K_wire with, GN_KEYWORDS_1800_2005, K_with within, GN_KEYWORDS_1800_2005, K_within # This is the name originally proposed for uwire and is deprecated! wone, GN_KEYWORDS_1364_2005, K_wone wor, GN_KEYWORDS_1364_1995, K_wor # This is defined by Verilog-AMS 2.3 and as an Icarus extension. wreal, GN_KEYWORDS_VAMS_2_3|GN_KEYWORDS_ICARUS, K_wreal xnor, GN_KEYWORDS_1364_1995, K_xnor xor, GN_KEYWORDS_1364_1995, K_xor zi_nd, GN_KEYWORDS_VAMS_2_3, K_zi_nd zi_np, GN_KEYWORDS_VAMS_2_3, K_zi_np zi_zd, GN_KEYWORDS_VAMS_2_3, K_zi_zd zi_zp, GN_KEYWORDS_VAMS_2_3, K_zi_zp %% int lexor_keyword_mask = 0; int lexor_keyword_code(const char*str, unsigned nstr) { const struct lexor_keyword*rc = Lkwd::check_identifier(str, nstr); if (rc == 0) return IDENTIFIER; else if ((rc->mask & lexor_keyword_mask) == 0) return IDENTIFIER; else return rc->tokenType; } iverilog-10_1/lexor_keyword.h000066400000000000000000000017571265551621300164220ustar00rootroot00000000000000#ifndef IVL_lexor_keyword_H #define IVL_lexor_keyword_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern int lexor_keyword_code (const char*str, unsigned len); #endif /* IVL_lexor_keyword_H */ iverilog-10_1/libmisc/000077500000000000000000000000001265551621300147645ustar00rootroot00000000000000iverilog-10_1/libmisc/LineInfo.cc000066400000000000000000000027151265551621300170030ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include using namespace std; LineInfo::LineInfo() : lineno_(0) { } LineInfo::LineInfo(const LineInfo&that) : file_(that.file_), lineno_(that.lineno_) { } LineInfo::~LineInfo() { } string LineInfo::get_fileline() const { ostringstream buf; buf << (file_.str()? file_.str() : "") << ":" << lineno_; string res = buf.str(); return res; } void LineInfo::set_line(const LineInfo&that) { file_ = that.file_; lineno_ = that.lineno_; } void LineInfo::set_file(perm_string f) { file_ = f; } void LineInfo::set_lineno(unsigned n) { lineno_ = n; } iverilog-10_1/libmisc/LineInfo.h000066400000000000000000000035441265551621300166460ustar00rootroot00000000000000#ifndef IVL_LineInfo_H #define IVL_LineInfo_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include using namespace std; /* * This class holds line information for an internal object. * * Note that the file names are C-style strings that are allocated by * the lexor (which parses the line directives) and are never * deallocated. We can therefore safely store the pointer and never * delete the string, even if LineInfo objects are destroyed. */ class LineInfo { public: LineInfo(); LineInfo(const LineInfo&that); virtual ~LineInfo(); // Get a fully formatted file/lineno string get_fileline() const; // Set the file/line from another LineInfo object. void set_line(const LineInfo&that); // Access parts of LineInfo data void set_file(perm_string f); void set_lineno(unsigned n); perm_string get_file() const { return file_; } unsigned get_lineno() const { return lineno_; } private: perm_string file_; unsigned lineno_; }; #endif /* IVL_LineInfo_H */ iverilog-10_1/libmisc/StringHeap.cc000066400000000000000000000112521265551621300173400ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include # include # include # include #ifdef CHECK_WITH_VALGRIND # include "ivl_alloc.h" static char **string_pool = NULL; static unsigned string_pool_count = 0; #endif StringHeap::StringHeap() { cell_base_ = 0; cell_ptr_ = HEAPCELL; cell_count_ = 0; } StringHeap::~StringHeap() { // This is a planned memory leak. The string heap is intended // to hold permanently-allocated strings. } const char* StringHeap::add(const char*text) { unsigned len = strlen(text); assert((len+1) <= HEAPCELL); unsigned rem = HEAPCELL - cell_ptr_; if (rem < (len+1)) { cell_base_ = (char*)malloc(HEAPCELL); #ifdef CHECK_WITH_VALGRIND string_pool_count += 1; string_pool = (char **) realloc(string_pool, string_pool_count*sizeof(char **)); string_pool[string_pool_count-1] = cell_base_; #endif cell_ptr_ = 0; cell_count_ += 1; assert(cell_base_ != 0); } char*res = cell_base_ + cell_ptr_; memcpy(res, text, len); cell_ptr_ += len; cell_base_[cell_ptr_++] = 0; assert(cell_ptr_ <= HEAPCELL); return res; } perm_string StringHeap::make(const char*text) { return perm_string(add(text)); } StringHeapLex::StringHeapLex() { hit_count_ = 0; add_count_ = 0; for (unsigned idx = 0 ; idx < HASH_SIZE ; idx += 1) hash_table_[idx] = 0; } StringHeapLex::~StringHeapLex() { } void StringHeapLex::cleanup() { #ifdef CHECK_WITH_VALGRIND for (unsigned idx = 0 ; idx < string_pool_count ; idx += 1) { free(string_pool[idx]); } free(string_pool); string_pool = NULL; string_pool_count = 0; for (unsigned idx = 0 ; idx < HASH_SIZE ; idx += 1) { hash_table_[idx] = 0; } #endif } unsigned StringHeapLex::add_hit_count() const { return hit_count_; } unsigned StringHeapLex::add_count() const { return add_count_; } static unsigned hash_string(const char*text) { unsigned h = 0; while (*text) { h = (h << 4) ^ (h >> 28) ^ *text; text += 1; } return h; } const char* StringHeapLex::add(const char*text) { unsigned hash_value = hash_string(text) % HASH_SIZE; /* If we easily find the string in the hash table, then return that and be done. */ if (hash_table_[hash_value] && (strcmp(hash_table_[hash_value], text) == 0)) { hit_count_ += 1; return hash_table_[hash_value]; } /* The existing hash entry is not a match. Replace it with the newly allocated value, and return the new pointer as the result to the add. */ const char*res = StringHeap::add(text); hash_table_[hash_value] = res; add_count_ += 1; return res; } perm_string StringHeapLex::make(const char*text) { return perm_string(add(text)); } perm_string StringHeapLex::make(const string&text) { return perm_string(add(text.c_str())); } bool operator == (perm_string a, const char*b) { if (a.str() == b) return true; if (! (a.str() && b)) return false; if (strcmp(a.str(), b) == 0) return true; return false; } bool operator == (perm_string a, perm_string b) { return a == b.str(); } bool operator != (perm_string a, const char*b) { return ! (a == b); } bool operator != (perm_string a, perm_string b) { return ! (a == b); } bool operator < (perm_string a, perm_string b) { if (b.str() && !a.str()) return true; if (b.str() == a.str()) return false; if (strcmp(a.str(), b.str()) < 0) return true; return false; } ostream& operator << (ostream&out, perm_string that) { if (that.nil()) out << ""; else out << that.str(); return out; } const perm_string empty_perm_string = perm_string::literal(""); iverilog-10_1/libmisc/StringHeap.h000066400000000000000000000073641265551621300172130ustar00rootroot00000000000000#ifndef IVL_StringHeap_H #define IVL_StringHeap_H /* * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include using namespace std; class perm_string { public: perm_string() : text_(0) { } perm_string(const perm_string&that) : text_(that.text_) { } ~perm_string() { } inline bool nil() const { return text_ == 0; } perm_string& operator = (const perm_string&that) { text_ = that.text_; return *this; } const char*str() const { return text_; } operator const char* () const { return str(); } // This is an escape for making perm_string objects out of // literals. For example, perm_string::literal("Label"); Please // do *not* cheat and pass arbitrary const char* items here. static perm_string literal(const char*t) { return perm_string(t); } private: friend class StringHeap; friend class StringHeapLex; perm_string(const char*t) : text_(t) { }; private: const char*text_; }; extern const perm_string empty_perm_string; extern bool operator == (perm_string a, perm_string b); extern bool operator == (perm_string a, const char* b); extern bool operator != (perm_string a, perm_string b); extern bool operator != (perm_string a, const char* b); extern bool operator > (perm_string a, perm_string b); extern bool operator < (perm_string a, perm_string b); extern bool operator >= (perm_string a, perm_string b); extern bool operator <= (perm_string a, perm_string b); extern ostream& operator << (ostream&out, perm_string that); /* * The string heap is a way to permanently allocate strings * efficiently. They only take up the space of the string characters * and the terminating nul, there is no malloc overhead. */ class StringHeap { public: StringHeap(); ~StringHeap(); const char*add(const char*); perm_string make(const char*); private: enum { HEAPCELL = 0x10000 }; char*cell_base_; unsigned cell_ptr_; unsigned cell_count_; private: // not implemented StringHeap(const StringHeap&); StringHeap& operator= (const StringHeap&); }; /* * A lexical string heap is a string heap that makes an effort to * return the same pointer for identical strings. This saves further * space by not allocating duplicate strings, so in a system with lots * of identifiers, this can theoretically save more space. */ class StringHeapLex : private StringHeap { public: StringHeapLex(); ~StringHeapLex(); const char*add(const char*); perm_string make(const char*); perm_string make(const string&); unsigned add_count() const; unsigned add_hit_count() const; void cleanup(); private: enum { HASH_SIZE = 4096 }; const char*hash_table_[HASH_SIZE]; unsigned add_count_; unsigned hit_count_; private: // not implemented StringHeapLex(const StringHeapLex&); StringHeapLex& operator= (const StringHeapLex&); }; #endif /* IVL_StringHeap_H */ iverilog-10_1/libveriuser/000077500000000000000000000000001265551621300156755ustar00rootroot00000000000000iverilog-10_1/libveriuser/Makefile.in000066400000000000000000000063461265551621300177530ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ RANLIB = @RANLIB@ AR = @AR@ LD = @LD@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif LDRELOCFLAGS = @LDRELOCFLAGS@ LDTARGETFLAGS = @LDTARGETFLAGS@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ A = a_close.o a_compare_handles.o a_configure.o a_fetch_argc.o \ a_fetch_argv.o a_fetch_dir.o a_fetch_fullname.o a_fetch_location.o \ a_fetch_param.o a_fetch_range.o a_fetch_tfarg.o a_fetch_time.o \ a_fetch_type.o a_fetch_type_str.o a_fetch_value.o a_handle_by_name.o \ a_handle_hiconn.o a_handle_object.o a_handle_parent.o \ a_handle_simulated_net.o a_handle_tfarg.o a_initialize.o a_next.o \ a_next_bit.o a_next_port.o a_next_topmod.o a_object_of_type.o \ a_product_version.o a_set_value.o a_vcl.o a_version.o O = asynch.o delay.o exprinfo.o finish.o getcstringp.o getinstance.o \ getlongp.o getp.o getsimtime.o io_print.o math.o mc_scan_plusargs.o \ nodeinfo.o nump.o putlongp.o putp.o spname.o typep.o workarea.o \ veriusertfs.o priv.o $A all: dep libveriuser.a $(ALL32) check: all clean: rm -rf *.o dep libveriuser.a libveriuser.o distclean: clean rm -f Makefile config.log rm -f config.h stamp-config-h cppcheck: $(O:.o=.c) cppcheck --enable=all -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in cd ..; ./config.status --file=libveriuser/$@ dep: mkdir dep stamp-config-h: $(srcdir)/config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=libveriuser/config.h config.h: stamp-config-h libveriuser.o: $O $(LD) $(LDTARGETFLAGS) -r -o $@ $O libveriuser.a: libveriuser.o rm -f $@ $(AR) cvq $@ libveriuser.o $(RANLIB) $@ %.o: %.c config.h $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep install:: all installdirs $(libdir)/libveriuser$(suffix).a $(INSTALL32) $(libdir)/libveriuser$(suffix).a: ./libveriuser.a $(INSTALL_DATA) ./libveriuser.a "$(DESTDIR)$(libdir)/libveriuser$(suffix).a" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)" uninstall:: rm -f "$(DESTDIR)$(libdir)/libveriuser$(suffix).a" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/libveriuser/a_close.c000066400000000000000000000017401265551621300174500ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "priv.h" void acc_close(void) { if (pli_trace) { fprintf(pli_trace, "acc_close()\n"); } } iverilog-10_1/libveriuser/a_compare_handles.c000066400000000000000000000017451265551621300214740ustar00rootroot00000000000000/* * Copyright (c) 2003 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include PLI_INT32 acc_compare_handles(handle handle1, handle handle2) { return handle1 == handle2; } iverilog-10_1/libveriuser/a_configure.c000066400000000000000000000044051265551621300203250ustar00rootroot00000000000000/* * Copyright (c) 2003-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" #include int acc_configure(PLI_INT32 config_param, const char*value) { int rc; switch (config_param) { case accDevelopmentVersion: vpi_printf("Request PLI Development Version %s\n", value); rc = 1; if (pli_trace) { fprintf(pli_trace, "acc_configure(accDevelopmentVersion, %s)\n", value); } break; case accEnableArgs: if (pli_trace) { fprintf(pli_trace, "acc_configure(accEnableArgs, %s)\n", value); } rc = 1; if (strcmp(value,"acc_set_scope") == 0) { vpi_printf("XXXX acc_configure argument: Sorry: " "(accEnableArgs, %s\n", value); rc = 0; } else if (strcmp(value,"no_acc_set_scope") == 0) { vpi_printf("XXXX acc_configure argument: Sorry: " "(accEnableArgs, %s\n", value); rc = 0; } else { vpi_printf("XXXX acc_configure argument error. " "(accEnableArgs, %s(invalid)\n", value); rc = 0; } break; default: if (pli_trace) { fprintf(pli_trace, "acc_configure(config=%d, %s)\n", (int)config_param, value); } #if 0 vpi_printf("XXXX acc_configure(%d, %s)\n", (int)config_param, value); #else /* Parameter is not necessarily a string. */ vpi_printf("XXXX acc_configure(%d, ...)\n", (int)config_param); #endif rc = 0; break; } return rc; } iverilog-10_1/libveriuser/a_fetch_argc.c000066400000000000000000000022601265551621300204260ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include /* * acc_fetch_argc implemented using VPI interface */ int acc_fetch_argc(void) { s_vpi_vlog_info vpi_vlog_info; /* get command line info */ if (! vpi_get_vlog_info(&vpi_vlog_info)) return 0; /* return argc */ return vpi_vlog_info.argc; } iverilog-10_1/libveriuser/a_fetch_argv.c000066400000000000000000000022741265551621300204560ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include /* * acc_fetch_argv implemented using VPI interface */ char **acc_fetch_argv(void) { s_vpi_vlog_info vpi_vlog_info; /* get command line info */ if (! vpi_get_vlog_info(&vpi_vlog_info)) return (char **)0; /* return argc */ return vpi_vlog_info.argv; } iverilog-10_1/libveriuser/a_fetch_dir.c000066400000000000000000000025561265551621300203000ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@picturel.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" PLI_INT32 acc_fetch_direction(handle obj) { (void)obj; /* Parameter is not used. */ if (pli_trace) { fprintf(pli_trace, "acc_fetch_direction: enter.\n"); fflush(pli_trace); } fprintf(stderr, "acc_fetch_direction: XXXX not implemented. XXXX\n"); if (pli_trace) { fprintf(pli_trace, "acc_fetch_direction: return.\n"); fflush(pli_trace); } return accInout; } iverilog-10_1/libveriuser/a_fetch_fullname.c000066400000000000000000000024141265551621300213160ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" /* * acc_fetch_fullname implemented using VPI interface */ char *acc_fetch_fullname(handle object) { return __acc_newstring(vpi_get_str(vpiFullName, object)); } char* acc_fetch_name(handle object) { return __acc_newstring(vpi_get_str(vpiName, object)); } char* acc_fetch_defname(handle object) { return __acc_newstring(vpi_get_str(vpiDefName, object)); } iverilog-10_1/libveriuser/a_fetch_location.c000066400000000000000000000020671265551621300213270ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include int acc_fetch_location(p_location loc, handle obj) { (void)obj; /* Parameter is not used. */ loc->line_no = 0; loc->filename = ""; return 1; } iverilog-10_1/libveriuser/a_fetch_param.c000066400000000000000000000027131265551621300206150ustar00rootroot00000000000000/* * Copyright (c) 2003-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "priv.h" double acc_fetch_paramval(handle object) { s_vpi_value val; val.format = vpiObjTypeVal; vpi_get_value(object, &val); switch (val.format) { case vpiStringVal: if (pli_trace) { fprintf(pli_trace, "acc_fetch_paramval(%s) --> \"%s\"\n", vpi_get_str(vpiName, object), val.value.str); } return (double) (intptr_t)val.value.str; default: vpi_printf("XXXX: parameter %s has type %d\n", vpi_get_str(vpiName, object), (int)val.format); assert(0); return 0.0; } } iverilog-10_1/libveriuser/a_fetch_range.c000066400000000000000000000021441265551621300206070ustar00rootroot00000000000000/* * Copyright (c) 2003 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include /* * acc_fetch_range implemented using VPI interface */ PLI_INT32 acc_fetch_range(handle object, int *msb, int *lsb) { *msb = vpi_get(vpiLeftRange, object); *lsb = vpi_get(vpiRightRange, object); return 0; } iverilog-10_1/libveriuser/a_fetch_tfarg.c000066400000000000000000000063121265551621300206170ustar00rootroot00000000000000/* * Copyright (c) 2002-2009 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" /* * acc_fetch_tfarg and friends implemented using VPI interface */ double acc_fetch_itfarg(PLI_INT32 n, handle obj) { vpiHandle iter, hand = 0; s_vpi_value value; int idx = n; double rtn; iter = vpi_iterate(vpiArgument, obj); /* scan to nth argument */ while (idx > 0 && (hand = vpi_scan(iter))) idx--; if (hand) { value.format=vpiRealVal; vpi_get_value(hand, &value); rtn = value.value.real; vpi_free_object(iter); } else { rtn = 0.0; } if (pli_trace) { fprintf(pli_trace, "%s: acc_fetch_itfarg(%d, %p) --> %f\n", vpi_get_str(vpiName, obj), (int)n, obj, rtn); } return rtn; } double acc_fetch_tfarg(PLI_INT32 n) { return acc_fetch_itfarg_int(n, vpi_handle(vpiSysTfCall,0)); } PLI_INT32 acc_fetch_itfarg_int(PLI_INT32 n, handle obj) { vpiHandle iter, hand = 0; s_vpi_value value; int idx = n; int rtn; iter = vpi_iterate(vpiArgument, obj); /* scan to nth argument */ while (idx > 0 && (hand = vpi_scan(iter))) idx--; if (hand) { value.format=vpiIntVal; vpi_get_value(hand, &value); rtn = value.value.integer; vpi_free_object(iter); } else { rtn = 0; } if (pli_trace) { fprintf(pli_trace, "%s: acc_fetch_itfarg_int(%d, %p) --> %d\n", vpi_get_str(vpiName, obj), (int)n, obj, rtn); } return rtn; } PLI_INT32 acc_fetch_tfarg_int(PLI_INT32 n) { return acc_fetch_itfarg_int(n, vpi_handle(vpiSysTfCall,0)); } char *acc_fetch_itfarg_str(PLI_INT32 n, handle obj) { vpiHandle iter, hand = 0; s_vpi_value value; int idx = n; char *rtn; iter = vpi_iterate(vpiArgument, obj); /* scan to nth argument */ while (idx > 0 && (hand = vpi_scan(iter))) idx -= 1; if (hand) { value.format=vpiStringVal; vpi_get_value(hand, &value); rtn = __acc_newstring(value.value.str); vpi_free_object(iter); } else { rtn = (char *) 0; } if (pli_trace) { fprintf(pli_trace, "%s: acc_fetch_itfarg_str(%d, %p) --> \"%s\"\n", vpi_get_str(vpiName, obj), (int)n, obj, rtn? rtn : ""); } return rtn; } char *acc_fetch_tfarg_str(PLI_INT32 n) { return acc_fetch_itfarg_str(n, vpi_handle(vpiSysTfCall,0)); } iverilog-10_1/libveriuser/a_fetch_time.c000066400000000000000000000020751265551621300204540ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" void acc_fetch_timescale_info(handle obj, p_timescale_info info) { info->precision = vpi_get(vpiTimePrecision, 0); info->unit = vpi_get(vpiTimeUnit, obj); } iverilog-10_1/libveriuser/a_fetch_type.c000066400000000000000000000054461265551621300205040ustar00rootroot00000000000000/* * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include PLI_INT32 acc_fetch_size(handle obj) { return vpi_get(vpiSize, obj); } PLI_INT32 acc_fetch_type(handle obj) { switch (vpi_get(vpiType, obj)) { case vpiConstant: /*XXXX SWIFT PLI tasks seem to assume that string constants show up an accParameter, instead of accConstant. */ if (vpi_get(vpiConstType, obj) == vpiStringConst) return accParameter; else return accConstant; case vpiNamedEvent: return accNamedEvent; case vpiNet: return accNet; case vpiParameter: return accParameter; case vpiReg: return accReg; case vpiIntegerVar: return accIntegerVar; case vpiModule: return accModule; } vpi_printf("acc_fetch_type: vpiType %d is what accType?\n", (int)vpi_get(vpiType, obj)); return accUnknown; } PLI_INT32 acc_fetch_fulltype(handle obj) { int type = vpi_get(vpiType, obj); switch (type) { case vpiNet: { type = vpi_get(vpiNetType, obj); switch(type) { case vpiWire: return accWire; default: vpi_printf("acc_fetch_fulltype: vpiNetType %d unknown?\n", type); return accUnknown; } } case vpiConstant: /* see acc_fetch_type */ if (vpi_get(vpiConstType, obj) == vpiStringConst) return accStringParam; else return accConstant; case vpiIntegerVar: return accIntegerVar; case vpiModule: if (!vpi_handle(vpiScope, obj)) return accTopModule; else return accModuleInstance; /* FIXME accCellInstance */ case vpiNamedEvent: return accNamedEvent; case vpiParameter: switch(vpi_get(vpiConstType, obj)) { case vpiRealConst: return accRealParam; case vpiStringConst: return accStringParam; default: return accIntegerParam; } case vpiReg: return accReg; default: vpi_printf("acc_fetch_fulltype: vpiType %d unknown?\n", type); return accUnknown; } } iverilog-10_1/libveriuser/a_fetch_type_str.c000066400000000000000000000027231265551621300213670ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include const char* acc_fetch_type_str(PLI_INT32 type) { switch (type) { case accNet: return "accNet"; case accReg: return "accReg"; case accParameter: return "accParameter"; case accConstant: return "accConstant"; } vpi_printf("acc_fetch_type_str: type %d is what accType?\n", (int)type); return "acc_fetch_type_str(unknown)"; } /* * FIXME: What does this do? How should it be declared in acc_user.h? */ PLI_INT32 acc_fetch_paramtype(handle obj) { (void)obj; /* Parameter is not used. */ return 0; } iverilog-10_1/libveriuser/a_fetch_value.c000066400000000000000000000062471265551621300206370ustar00rootroot00000000000000/* * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include "priv.h" # include # include static char* fetch_struct_value(handle obj, s_acc_value*value) { struct t_vpi_value val; switch (value->format) { case accScalarVal: val.format = vpiScalarVal; vpi_get_value(obj, &val); switch (val.value.scalar) { case vpi0: value->value.scalar = acc0; break; case vpi1: value->value.scalar = acc1; break; case vpiX: value->value.scalar = accX; break; case vpiZ: value->value.scalar = accZ; break; default: assert(0); } if (pli_trace) { fprintf(pli_trace, "acc_fetch_value(<%s>, " "accScalarVal) --> %d\n", vpi_get_str(vpiFullName,obj), value->value.scalar); } break; case accIntVal: val.format = vpiIntVal; vpi_get_value(obj, &val); value->value.integer = val.value.integer; if (pli_trace) { fprintf(pli_trace, "acc_fetch_value(<%s>, " "accIntVal) --> %d\n", vpi_get_str(vpiFullName,obj), value->value.integer); } break; case accRealVal: val.format = vpiRealVal; vpi_get_value(obj, &val); value->value.real = val.value.real; if (pli_trace) { fprintf(pli_trace, "acc_fetch_value(<%s>, " "accRealVal) --> %g\n", vpi_get_str(vpiFullName,obj), value->value.real); } break; default: vpi_printf("XXXX acc_fetch_value(..., \"%%%%\", <%d>);\n", value->format); value->value.str = ""; break; } return 0; } static char* fetch_strength_value(handle obj) { struct t_vpi_value val; char str[4]; val.format = vpiStrengthVal; vpi_get_value(obj, &val); /* Should this iterate over the bits? It now matches the old code. */ vpip_format_strength(str, &val, 0); if (pli_trace) { fprintf(pli_trace, "acc_fetch_value(<%s>, \"%%v\") --> %s\n", vpi_get_str(vpiFullName,obj), str); } return __acc_newstring(str); } char* acc_fetch_value(handle obj, const char*fmt, s_acc_value*value) { if (strcmp(fmt, "%%") == 0) return fetch_struct_value(obj, value); if (strcmp(fmt, "%v") == 0) return fetch_strength_value(obj); vpi_printf("XXXX acc_fetch_value(..., \"%s\", ...)\n", fmt); return ""; } iverilog-10_1/libveriuser/a_handle_by_name.c000066400000000000000000000030011265551621300212600ustar00rootroot00000000000000/* * Copyright (c) 2003-2012 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include "priv.h" # include # include /* * acc_handle_by_name implemented using VPI interface */ handle acc_handle_by_name(const char*obj_name, handle scope) { vpiHandle res; /* if no scope provided, use tasks scope */ if (!scope) { vpiHandle sys_h = vpi_handle(vpiSysTfCall, 0 /* NULL */); scope = vpi_handle(vpiScope, sys_h); } res = vpi_handle_by_name(obj_name, scope); if (pli_trace) { fprintf(pli_trace, "acc_handle_by_name(\"%s\", scope=%s) " " --> %p\n", obj_name, vpi_get_str(vpiFullName, scope), res); } return res; } iverilog-10_1/libveriuser/a_handle_hiconn.c000066400000000000000000000025341265551621300211360ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@picturel.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" handle acc_handle_hiconn(handle obj) { (void)obj; /* Parameter is not used. */ if (pli_trace) { fprintf(pli_trace, "acc_handle_hiconn: enter.\n"); fflush(pli_trace); } fprintf(stderr, "acc_handle_hiconn: XXXX not implemented. XXXX\n"); if (pli_trace) { fprintf(pli_trace, "acc_handle_hiconn: return.\n"); fflush(pli_trace); } return 0; } iverilog-10_1/libveriuser/a_handle_object.c000066400000000000000000000031101265551621300211150ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" static vpiHandle search_scope = 0; handle acc_handle_object(const char*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle scope = search_scope? search_scope : vpi_handle(vpiScope, sys); vpiHandle res = vpi_handle_by_name(name, scope); if (pli_trace) { fprintf(pli_trace, "acc_handle_object(%s ) --> .\n", name, acc_fetch_fullname(scope)); } return res; } char* acc_set_scope(handle ref, ...) { char*name; search_scope = ref; name = acc_fetch_fullname(search_scope); if (pli_trace) { fprintf(pli_trace, "acc_set_scope()\n", name); } return acc_fetch_fullname(ref); } iverilog-10_1/libveriuser/a_handle_parent.c000066400000000000000000000022771265551621300211550ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" handle acc_handle_parent(handle obj) { vpiHandle scope = vpi_handle(vpiScope, obj); while (scope && (vpi_get(vpiType, scope) != vpiModule)) scope = vpi_handle(vpiScope, scope); return scope; } handle acc_handle_scope(handle obj) { return vpi_handle(vpiScope, obj); } iverilog-10_1/libveriuser/a_handle_simulated_net.c000066400000000000000000000022071265551621300225120ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@picturel.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" handle acc_handle_simulated_net(handle obj) { if (pli_trace) { fprintf(pli_trace, "acc_handle_simulated_set: returns argument\n"); fflush(pli_trace); } return obj; } iverilog-10_1/libveriuser/a_handle_tfarg.c000066400000000000000000000027141265551621300207630ustar00rootroot00000000000000/* * Copyright (c) 2002-2012 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include /* * acc_handle_tfarg implemented using VPI interface */ handle acc_handle_tfarg(int n) { vpiHandle rtn_h = 0; if (n > 0) { vpiHandle sys_h, sys_i; sys_h = vpi_handle(vpiSysTfCall, 0 /* NULL */); sys_i = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (n > 0) { rtn_h = vpi_scan(sys_i); if (rtn_h == 0) break; n--; } if (rtn_h) vpi_free_object(sys_i); } else { rtn_h = (vpiHandle) 0; } return rtn_h; } handle acc_handle_tfinst(void) { return vpi_handle(vpiSysTfCall, 0); } iverilog-10_1/libveriuser/a_initialize.c000066400000000000000000000017031265551621300205030ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include int acc_error_flag; int acc_initialize() { acc_error_flag = 0; return 1; } iverilog-10_1/libveriuser/a_next.c000066400000000000000000000050041265551621300173160ustar00rootroot00000000000000/* * Copyright (c) 2003-2009 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "priv.h" /* * acc_next and friends implemented using VPI */ handle acc_next(PLI_INT32 *type, handle scope, handle prev) { vpiHandle iter, hand = 0; /* trace */ if (pli_trace) { PLI_INT32 *ip; fprintf(pli_trace, "acc_next(%p <", type); for (ip = type; *ip; ip++) { fprintf(pli_trace, "%s%d", ip != type ? "," : "", (int)*ip); } fprintf(pli_trace, ">, %p", scope); if (scope) fprintf(pli_trace, " \"%s\"", vpi_get_str(vpiName, scope)); fprintf(pli_trace, ", %p", prev); if (prev) fprintf(pli_trace, " \"%s\"", vpi_get_str(vpiName, prev)); else fprintf(pli_trace, ")"); fflush(pli_trace); } /* * The acc_next_* functions need to be reentrant, so we need to * rescan all the items up to the previous one, then return * the next one. */ iter = vpi_iterate(vpiScope, scope); /* ICARUS extension */ if (prev) { while ((hand = vpi_scan(iter))) { if (hand == prev) break; } } /* scan for next */ if (!prev || hand) { while ((hand = vpi_scan(iter))) { if (acc_object_in_typelist(hand, type)) break; } } /* don't leak iterators */ if (hand) vpi_free_object(iter); /* trace */ if (pli_trace) { fprintf(pli_trace, " --> %p", hand); if (hand) fprintf(pli_trace, " \"%s\"\n", vpi_get_str(vpiName, hand)); else fprintf(pli_trace, "\n"); } return hand; } handle acc_next_scope(handle scope, handle prev) { PLI_INT32 type[2] = {accScope, 0}; return acc_next(type, scope, prev); } iverilog-10_1/libveriuser/a_next_bit.c000066400000000000000000000026021265551621300201550ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@picturel.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" handle acc_next_bit(handle ref, handle bit) { (void)ref; /* Parameter is not used. */ (void)bit; /* Parameter is not used. */ if (pli_trace) { fprintf(pli_trace, "acc_next_bit: enter.\n"); fflush(pli_trace); } fprintf(stderr, "acc_next_bit: XXXX not implemented. XXXX\n"); if (pli_trace) { fprintf(pli_trace, "acc_next_bit: return.\n"); fflush(pli_trace); } return 0; } iverilog-10_1/libveriuser/a_next_port.c000066400000000000000000000026041265551621300203650ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" handle acc_next_port(handle ref, handle bit) { (void)ref; /* Parameter is not used. */ (void)bit; /* Parameter is not used. */ if (pli_trace) { fprintf(pli_trace, "acc_next_port: enter.\n"); fflush(pli_trace); } fprintf(stderr, "acc_next_port: XXXX not implemented. XXXX\n"); if (pli_trace) { fprintf(pli_trace, "acc_next_port: return.\n"); fflush(pli_trace); } return 0; } iverilog-10_1/libveriuser/a_next_topmod.c000066400000000000000000000025151265551621300207040ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #undef NULL #define NULL 0 /* * acc_next_topmod implemented using VPI interface */ handle acc_next_topmod(handle prev_topmod) { static vpiHandle last = NULL; static vpiHandle mod_i = NULL; if (!prev_topmod) { /* start over */ mod_i = vpi_iterate(vpiModule, NULL); } else { /* subsequent time through */ assert(prev_topmod == last); } last = vpi_scan(mod_i); return last; } iverilog-10_1/libveriuser/a_object_of_type.c000066400000000000000000000053641265551621300213440ustar00rootroot00000000000000/* * Copyright (c) 2002-2013 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "priv.h" /* * acc_object_of_type implemented using VPI interface */ int acc_object_of_type(handle object, PLI_INT32 type) { int vtype; int rtn = 0; /* false */ if (pli_trace) { fprintf(pli_trace, "acc_object_of_type(%p \"%s\", %d)", object, vpi_get_str(vpiName, object), (int)type); fflush(pli_trace); } /* get VPI type of object */ vtype = vpi_get(vpiType, object); switch (type) { case accModule: rtn = vtype == vpiModule; break; case accScope: if (vtype == vpiModule || vtype == vpiNamedBegin || vtype == vpiNamedFork || vtype == vpiTask || vtype == vpiFunction || vtype == vpiGenScope) rtn = 1; break; case accNet: rtn = vtype == vpiNet; break; case accReg: rtn = vtype == vpiReg; break; case accRealParam: if (vtype == vpiNamedEvent && vpi_get(vpiConstType, object) == vpiRealConst) rtn = 1; break; case accParameter: rtn = vtype == vpiParameter; break; case accNamedEvent: rtn = vtype == vpiNamedEvent; break; case accIntegerVar: rtn = vtype == vpiIntegerVar; break; case accRealVar: rtn = vtype == vpiRealVar; break; case accTimeVar: rtn = vtype == vpiTimeVar; break; case accScalar: if (vtype == vpiReg || vtype == vpiNet) rtn = vpi_get(vpiSize, object) == 1; break; case accVector: if (vtype == vpiReg || vtype == vpiNet) rtn = vpi_get(vpiSize, object) > 1; break; default: vpi_printf("acc_object_of_type: Unknown type %d\n", (int)type); rtn = 0; } if (pli_trace) fprintf(pli_trace, " --> %d\n", rtn); return rtn; } int acc_object_in_typelist(handle object, PLI_INT32*typelist) { while (typelist[0] != 0) { int rtn = acc_object_of_type(object, typelist[0]); if (rtn) return rtn; typelist += 1; } return 0; } iverilog-10_1/libveriuser/a_product_version.c000066400000000000000000000020271265551621300215670ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include char *acc_product_version(void) { s_vpi_vlog_info info; if (! vpi_get_vlog_info(&info)) return (char *)0; return info.version; } iverilog-10_1/libveriuser/a_set_value.c000066400000000000000000000065041265551621300203350ustar00rootroot00000000000000/* * Copyright (c) 2002-2009 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include /* * acc_set_value implemented using VPI interface */ int acc_set_value(handle object, p_setval_value value, p_setval_delay delay) { s_vpi_time when, *whenp; s_vpi_value val; int flags; assert(delay); assert(value); assert(object); /* map setval_delay.model to flags */ switch (delay->model) { case accNoDelay: flags = vpiNoDelay; break; case accInertialDelay: flags = vpiInertialDelay; break; case accTransportDelay: flags = vpiTransportDelay; break; case accPureTransportDelay: flags = vpiPureTransportDelay; break; case accForceFlag: flags = vpiForceFlag; break; case accReleaseFlag: flags = vpiReleaseFlag; break; default: flags = -1; assert(0); break; } /* map acc_time to vpi_time */ if (delay->model != accNoDelay) { switch (delay->time.type) { case accSimTime: when.type = vpiSimTime; break; case accRealTime: when.type = vpiScaledRealTime; break; default: assert(0); break; } when.high = delay->time.high; when.low = delay->time.low; when.real = delay->time.real; whenp = &when; } else whenp = 0; /* map setval_value to vpi_value and flags */ switch (value->format) { case accBinStrVal: val.format = vpiBinStrVal; val.value.str = value->value.str; break; case accOctStrVal: val.format = vpiOctStrVal; val.value.str = value->value.str; break; case accDecStrVal: val.format = vpiDecStrVal; val.value.str = value->value.str; break; case accHexStrVal: val.format = vpiHexStrVal; val.value.str = value->value.str; break; case accScalarVal: val.format = vpiScalarVal; val.value.scalar = value->value.scalar; break; case accIntVal: val.format = vpiIntVal; val.value.integer = value->value.integer; break; case accRealVal: val.format = vpiRealVal; val.value.real = value->value.real; break; case accStringVal: val.format = vpiStringVal; val.value.str = value->value.str; break; case accVectorVal: val.format = vpiVectorVal; val.value.vector = (p_vpi_vecval)value->value.vector; break; default: vpi_printf("XXXX acc_set_value(value->format=%d)\n", value->format); assert(0); break; } /* put value */ vpi_put_value(object, &val, whenp, flags); return 1; } iverilog-10_1/libveriuser/a_vcl.c000066400000000000000000000133761265551621300171370ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "priv.h" #include #include "ivl_alloc.h" /* * This is the structure of a record that I use locally to hold the * information about a VCL. This record includes a pointer to the vpi * callback that is actually watching the value, and that callback has * a pointer to this record in its user_data so that I can get to it * when the value changes. * * Keep all these records in a vcl_list so that I can get access to * them for the vcl_delete. */ struct vcl_record { /* Object who's value I'm watching. */ vpiHandle obj; /* User's callback routine. */ PLI_INT32(*consumer)(p_vc_record); void*user_data; PLI_INT32 vcl_flag; vpiHandle callback; struct vcl_record*next; }; static struct vcl_record*vcl_list = 0; static int vpi_strength_to_vcl(int vs) { switch (vs) { case vpiSupplyDrive: return vclSupply; case vpiStrongDrive: return vclStrong; case vpiPullDrive: return vclPull; case vpiLargeCharge: return vclLarge; case vpiWeakDrive: return vclWeak; case vpiMediumCharge: return vclMedium; case vpiSmallCharge: return vclSmall; case vpiHiZ: return vclHighZ; default: return -1; } } /* * This is a VPI callback that notices the value change. This function * further dispatches the information about the callback to the * consumer function. */ static PLI_INT32 vcl_value_callback(struct t_cb_data*cb) { s_vpi_time sim_time; s_vpi_value obj_value; struct t_vc_record vcr; struct vcl_record*cur = (struct vcl_record*)cb->user_data; sim_time.type = vpiSimTime; vpi_get_time(cur->obj, &sim_time); switch (cur->vcl_flag) { case VCL_VERILOG_LOGIC: vpi_printf("XXXX vcl_value_callback(%s=%d);\n", vpi_get_str(vpiName, cur->obj), -1); vcr.vc_reason = logic_value_change; break; case VCL_VERILOG_STRENGTH: vcr.vc_reason = strength_value_change; obj_value.format = vpiStrengthVal; vpi_get_value(cur->obj, &obj_value); assert(obj_value.format == vpiStrengthVal); switch (obj_value.value.strength[0].logic) { case vpi0: vcr.out_value.strengths_s.logic_value = acc0; vcr.out_value.strengths_s.strength1 = vpi_strength_to_vcl(obj_value.value.strength[0].s0); vcr.out_value.strengths_s.strength2 = vpi_strength_to_vcl(obj_value.value.strength[0].s0); break; case vpi1: vcr.out_value.strengths_s.logic_value = acc1; vcr.out_value.strengths_s.strength1 = vpi_strength_to_vcl(obj_value.value.strength[0].s1); vcr.out_value.strengths_s.strength2 = vpi_strength_to_vcl(obj_value.value.strength[0].s1); break; case vpiX: vcr.out_value.strengths_s.logic_value = accX; vcr.out_value.strengths_s.strength1 = vpi_strength_to_vcl(obj_value.value.strength[0].s1); vcr.out_value.strengths_s.strength2 = vpi_strength_to_vcl(obj_value.value.strength[0].s0); break; case vpiZ: vcr.out_value.strengths_s.logic_value = accZ; vcr.out_value.strengths_s.strength1 = vclHighZ; vcr.out_value.strengths_s.strength2 = vclHighZ; break; default: assert(0); } if (pli_trace) { fprintf(pli_trace, "Call vcl_value_callback(%s=%d )\n", vpi_get_str(vpiFullName, cur->obj), vcr.out_value.strengths_s.logic_value, vcr.out_value.strengths_s.strength1, vcr.out_value.strengths_s.strength2); } break; default: assert(0); } vcr.vc_hightime = sim_time.high; vcr.vc_lowtime = sim_time.low; vcr.user_data = cur->user_data; (cur->consumer) (&vcr); return 0; } void acc_vcl_add(handle obj, PLI_INT32(*consumer)(p_vc_record), void*data, PLI_INT32 vcl_flag) { struct vcl_record*cur; struct t_cb_data cb; switch (vpi_get(vpiType, obj)) { case vpiNet: case vpiReg: cur = malloc(sizeof (struct vcl_record)); cur->obj = obj; cur->consumer = consumer; cur->user_data = data; cur->vcl_flag = vcl_flag; cur->next = vcl_list; vcl_list = cur; cb.reason = cbValueChange; cb.cb_rtn = vcl_value_callback; cb.obj = obj; cb.time = 0; cb.value = 0; cb.user_data = (void*)cur; cur->callback = vpi_register_cb(&cb); if (pli_trace) { fprintf(pli_trace, "acc_vcl_add(<%s>, ..., %p, %d)\n", vpi_get_str(vpiFullName, obj), data, (int)vcl_flag); } break; default: vpi_printf("XXXX acc_vcl_add(, ..., %d);\n", (int)vpi_get(vpiType, obj), (int)vcl_flag); break; } } void acc_vcl_delete(handle obj, PLI_INT32(*consumer)(p_vc_record), void*data, PLI_INT32 vcl_flag) { (void)obj; /* Parameter is not used. */ (void)consumer; /* Parameter is not used. */ (void)data; /* Parameter is not used. */ (void)vcl_flag; /* Parameter is not used. */ vpi_printf("XXXX acc_vcl_delete(...)\n"); } iverilog-10_1/libveriuser/a_version.c000066400000000000000000000020171265551621300200260ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include char *acc_version(void) { s_vpi_vlog_info info; if (! vpi_get_vlog_info(&info)) return (char *)0; return info.version; } iverilog-10_1/libveriuser/asynch.c000066400000000000000000000021511265551621300173250ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include /* Enables async misctf callbacks */ int async_misctf_enable = 0; /* * Implement misctf async enable */ int tf_asynchon(void) { async_misctf_enable = 1; return 0; } int tf_asynchoff(void) { async_misctf_enable = 0; return 0; } iverilog-10_1/libveriuser/config.h.in000066400000000000000000000024351265551621300177240ustar00rootroot00000000000000#ifndef IVL_config_H #define IVL_config_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # define SIZEOF_UNSIGNED_LONG_LONG 0 # define SIZEOF_UNSIGNED_LONG 0 # define SIZEOF_UNSIGNED 0 #if SIZEOF_UNSIGNED >= 8 typedef unsigned ivl_u64_t; #else # if SIZEOF_UNSIGNED_LONG >= 8 typedef unsigned long ivl_u64_t; # else # if SIZEOF_UNSIGNED_LONG_LONG > SIZEOF_UNSIGNED_LONG typedef unsigned long long ivl_u64_t; # else typedef unsigned long ivl_u64_t; # endif # endif #endif #endif /* IVL_config_H */ iverilog-10_1/libveriuser/delay.c000066400000000000000000000042241265551621300171410ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "priv.h" #include static PLI_INT32 delay_callback(struct t_cb_data*cb) { (void)cb; /* Parameter is not used. */ vpi_printf("XXXX delay_callback called.\n"); return 0; } int tf_isetdelay(PLI_INT32 delay, void*ss) { vpiHandle sys = (vpiHandle)ss; int unit = vpi_get(vpiTimeUnit, sys); int prec = vpi_get(vpiTimePrecision, 0); struct t_cb_data cb; struct t_vpi_time ct; if (pli_trace) { fprintf(pli_trace, "%s: tf_isetdelay(%d, ...)" " ;\n", vpi_get_str(vpiName, sys), (int)delay, unit, prec); } /* Convert the delay from the UNITS of the specified task/function to the precision of the simulation. */ assert(unit >= prec); while (unit > prec) { PLI_INT32 tmp = delay * 10; assert(tmp > delay); delay = tmp; unit -= 1; } /* Create a VPI callback to schedule the delay. */ ct.type = vpiSimTime; ct.high = 0; ct.low = delay; cb.reason = cbAfterDelay; cb.cb_rtn = delay_callback; cb.obj = 0; cb.time = &ct; cb.value = 0; cb.user_data = 0; vpi_register_cb(&cb); return 0; } int tf_setdelay(PLI_INT32 delay) { return tf_isetdelay(delay, tf_getinstance()); } iverilog-10_1/libveriuser/exprinfo.c000066400000000000000000000026231265551621300176760ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" struct t_tfexprinfo* tf_exprinfo(PLI_INT32 a, struct t_tfexprinfo*ip) { (void)a; /* Parameter is not used. */ (void)ip; /* Parameter is not used. */ if (pli_trace) { fprintf(pli_trace, "tf_exprinfo: enter.\n"); fflush(pli_trace); } fprintf(stderr, "tf_exprinfo: XXXX not implemented. XXXX\n"); if (pli_trace) { fprintf(pli_trace, "tf_exprinfo: return.\n"); fflush(pli_trace); } return 0; } iverilog-10_1/libveriuser/finish.c000066400000000000000000000021311265551621300173160ustar00rootroot00000000000000/* * Copyright (c) 2002 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include /* * Implement tf_dofinish and tf_dostop using vpi functions. */ int tf_dofinish(void) { vpi_control(vpiFinish, 0); return 0; } int tf_dostop(void) { vpi_control(vpiStop, 0); return 0; } iverilog-10_1/libveriuser/getcstringp.c000066400000000000000000000020361265551621300203730ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include /* * tf_getinstance implemented using equivalent acc_ routing */ char *tf_getcstringp(int n) { char*res = acc_fetch_tfarg_str(n); return res; } iverilog-10_1/libveriuser/getinstance.c000066400000000000000000000020371265551621300203470ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include /* * tf_getinstance implemented using VPI interface */ PLI_BYTE8* tf_getinstance(void) { return (PLI_BYTE8 *)vpi_handle(vpiSysTfCall, 0 /* NULL */); } iverilog-10_1/libveriuser/getlongp.c000066400000000000000000000036751265551621300176730ustar00rootroot00000000000000/* * Copyright (c) 2002-2012 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include /* * tf_getlongp implemented using VPI interface */ int tf_getlongp(int *highvalue, int n) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value value; int len, rtn; assert(highvalue); assert(n > 0); /* get task/func handle */ sys_h = vpi_handle(vpiSysTfCall, 0); sys_i = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) assert(0); n--; } /* get the value */ value.format = vpiHexStrVal; vpi_get_value(arg_h, &value); /* convert string to int(s) */ len = strlen(value.value.str); if (len > 8) { char *str; /* low word */ str = value.value.str + (len - 8); rtn = (int) strtoul(str, 0, 16); /* high word */ *str = '\0'; *highvalue = (int) strtoul(value.value.str, 0, 16); } else { *highvalue = 0; rtn = (int) strtoul(value.value.str, 0, 16); } vpi_free_object(sys_i); return rtn; } iverilog-10_1/libveriuser/getp.c000066400000000000000000000106341265551621300170040ustar00rootroot00000000000000/* * Copyright (c) 2002-2009 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" /* * tf_getp and friends, implemented using VPI interface */ PLI_INT32 tf_igetp(PLI_INT32 n, void *obj) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value value; int rtn = 0; assert(n > 0); /* get task/func handle */ sys_h = (vpiHandle)obj; sys_i = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) { goto out; } n--; } /* If it is a constant string, return a pointer to it else int value */ if (vpi_get(vpiType, arg_h) == vpiConstant && vpi_get(vpiConstType, arg_h) == vpiStringConst) { value.format = vpiStringVal; vpi_get_value(arg_h, &value); /* The following may generate a compilation warning, but this * functionality is required by some versions of the standard. */ rtn = (int) value.value.str; /* Oh my */ } else { value.format = vpiIntVal; vpi_get_value(arg_h, &value); rtn = value.value.integer; } vpi_free_object(sys_i); out: if (pli_trace) { fprintf(pli_trace, "tf_igetp(n=%d, obj=%p) --> %d\n", (int)n, obj, rtn); } return rtn; } PLI_INT32 tf_getp(PLI_INT32 n) { int rtn = tf_igetp(n, vpi_handle(vpiSysTfCall, 0)); return rtn; } double tf_igetrealp(PLI_INT32 n, void *obj) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value value; double rtn = 0.0; assert(n > 0); /* get task/func handle */ sys_h = (vpiHandle)obj; sys_i = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) { goto out; } n--; } if (vpi_get(vpiType, arg_h) == vpiConstant && vpi_get(vpiConstType, arg_h) == vpiStringConst) { rtn = 0.0; } else { value.format = vpiRealVal; vpi_get_value(arg_h, &value); rtn = value.value.real; } vpi_free_object(sys_i); out: if (pli_trace) { fprintf(pli_trace, "tf_igetrealp(n=%d, obj=%p) --> %f\n", (int)n, obj, rtn); } return rtn; } double tf_getrealp(PLI_INT32 n) { double rtn = tf_igetrealp(n, vpi_handle(vpiSysTfCall, 0)); return rtn; } char *tf_istrgetp(PLI_INT32 n, PLI_INT32 fmt, void *obj) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value value; char *rtn = 0; assert(n > 0); /* get task/func handle */ sys_h = (vpiHandle)obj; sys_i = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) { goto out; } n--; } if (vpi_get(vpiType, arg_h) == vpiConstant && vpi_get(vpiConstType, arg_h) == vpiStringConst) { value.format = vpiStringVal; vpi_get_value(arg_h, &value); rtn = value.value.str; } else { value.format = -1; switch (tolower(fmt)) { case 'b': value.format = vpiBinStrVal; break; case 'o': value.format = vpiOctStrVal; break; case 'd': value.format = vpiDecStrVal; break; case 'h': value.format = vpiHexStrVal; break; } if (value.format > 0) { vpi_get_value(arg_h, &value); rtn = value.value.str; } } vpi_free_object(sys_i); out: if (pli_trace) { fprintf(pli_trace, "tf_istrgetp(n=%d, fmt=%c, obj=%p) --> \"%s\"\n", (int)n, (int)fmt, obj, rtn); } return rtn; } char *tf_strgetp(PLI_INT32 n, PLI_INT32 fmt) { char *rtn = tf_istrgetp(n, fmt, vpi_handle(vpiSysTfCall, 0)); return rtn; } iverilog-10_1/libveriuser/getsimtime.c000066400000000000000000000142151265551621300202130ustar00rootroot00000000000000/* * Copyright (c) 2002-2014 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "config.h" #include "priv.h" #include /* * some TF time routines implemented using VPI interface */ // On some platforms (e.g. MinGW), pow() may not always generate an // exact integer result when supplied with integer operands. Converting // the result to an integer before we use it seems to be enough to work // round this issue. static ivl_u64_t pow10u(PLI_INT32 val) { return (ivl_u64_t)pow(10, val); } static ivl_u64_t scale(int high, int low, void*obj) { ivl_u64_t scaled; vpiHandle use_obj = obj; if (use_obj == 0) { /* If object is not passed in, then use current scope. */ vpiHandle hand = vpi_handle(vpiScope, vpi_handle(vpiSysTfCall,0)); use_obj = hand; } else { /* If object IS passed in, make sure it is a scope. If it is not, then get the scope of the object. We need a scope handle to go on. */ switch (vpi_get(vpiType,use_obj)) { case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: break; default: use_obj = vpi_handle(vpiScope, use_obj); break; } } scaled = high; scaled = (scaled << 32) | low; scaled /= pow10u(vpi_get(vpiTimeUnit, use_obj) - vpi_get(vpiTimePrecision,0)); return scaled; } PLI_INT32 tf_gettime(void) { s_vpi_time timerec; timerec.type = vpiSimTime; vpi_get_time (0, &timerec); return scale(timerec.high, timerec.low, 0) & 0xffffffff; } char *tf_strgettime(void) { static char buf[32]; s_vpi_time timerec; timerec.type = vpiSimTime; vpi_get_time (0, &timerec); if (timerec.high) snprintf(buf, sizeof(buf)-1, "%u%08u", (unsigned int)timerec.high, (unsigned int)timerec.low); else snprintf(buf, sizeof(buf)-1, "%u", (unsigned int)timerec.low); return buf; } PLI_INT32 tf_igetlongtime(PLI_INT32 *high, void*obj) { s_vpi_time timerec; ivl_u64_t scaled; timerec.type = vpiSimTime; vpi_get_time ((vpiHandle)obj, &timerec); scaled = scale(timerec.high, timerec.low, obj); *high = (scaled >> 32) & 0xffffffff; return scaled & 0xffffffff; } PLI_INT32 tf_getlongtime(PLI_INT32 *high) { return tf_igetlongtime(high, 0); } /* * This function is not defined in the IEEE standard, but is provided for * compatibility with other simulators. On platforms that support this, * make it a weak symbol just in case the user has defined their own * function for this. */ #if !defined(__CYGWIN__) && !defined(__MINGW32__) PLI_INT32 tf_getlongsimtime(PLI_INT32 *high) __attribute__ ((weak)); #endif PLI_INT32 tf_getlongsimtime(PLI_INT32 *high) { s_vpi_time timerec; timerec.type = vpiSimTime; vpi_get_time (0, &timerec); *high = timerec.high; return timerec.low; } void tf_scale_longdelay(void*obj, PLI_INT32 low, PLI_INT32 high, PLI_INT32 *alow, PLI_INT32 *ahigh) { ivl_u64_t scaled = scale(high, low, obj); *ahigh = (scaled >> 32) & 0xffffffff; *alow = scaled & 0xffffffff; } void tf_unscale_longdelay(void*obj, PLI_INT32 low, PLI_INT32 high, PLI_INT32 *alow, PLI_INT32 *ahigh) { ivl_u64_t unscaled; vpiHandle hand = vpi_handle(vpiScope, vpi_handle(vpiSysTfCall,0)); (void)obj; /* Parameter is not used. */ unscaled = high; unscaled = (unscaled << 32) | low; unscaled *= pow(10, vpi_get(vpiTimeUnit, hand) - vpi_get(vpiTimePrecision, 0)); *ahigh = (unscaled >> 32) & 0xffffffff; *alow = unscaled & 0xffffffff; } void tf_scale_realdelay(void*obj, double real, double *areal) { vpiHandle hand = vpi_handle(vpiScope, vpi_handle(vpiSysTfCall,0)); (void)obj; /* Parameter is not used. */ *areal = real / pow(10, vpi_get(vpiTimeUnit, hand) - vpi_get(vpiTimePrecision, 0)); } void tf_unscale_realdelay(void*obj, double real, double *areal) { vpiHandle hand = vpi_handle(vpiScope, vpi_handle(vpiSysTfCall,0)); (void)obj; /* Parameter is not used. */ *areal = real * pow(10, vpi_get(vpiTimeUnit, hand) - vpi_get(vpiTimePrecision, 0)); } PLI_INT32 tf_gettimeprecision(void) { PLI_INT32 rc; vpiHandle hand; vpiHandle sys = vpi_handle(vpiSysTfCall,0); assert(sys); hand = vpi_handle(vpiScope, sys); rc = vpi_get(vpiTimePrecision, hand); if (pli_trace) fprintf(pli_trace, "tf_gettimeprecision(<%s>) --> %d\n", vpi_get_str(vpiName, sys), (int)rc); return rc; } PLI_INT32 tf_igettimeprecision(void*obj) { PLI_INT32 rc; if (obj == 0) { /* If the obj pointer is null, then get the simulation time precision. */ rc = vpi_get(vpiTimePrecision, 0); } else { vpiHandle scope = vpi_handle(vpiScope, (vpiHandle)obj); assert(scope); rc = vpi_get(vpiTimePrecision, scope); } if (pli_trace) fprintf(pli_trace, "tf_igettimeprecision(<%s>) --> %d\n", obj? vpi_get_str(vpiName, obj) : ".", (int)rc); return rc; } PLI_INT32 tf_gettimeunit() { vpiHandle hand = vpi_handle(vpiScope, vpi_handle(vpiSysTfCall,0)); return vpi_get(vpiTimeUnit, hand); } PLI_INT32 tf_igettimeunit(void*obj) { return vpi_get(!obj ? vpiTimePrecision : vpiTimeUnit, (vpiHandle)obj); } iverilog-10_1/libveriuser/io_print.c000066400000000000000000000033041265551621300176640ustar00rootroot00000000000000/* * Copyright (c) 2002-2014 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include /* * io_printf implemented using VPI interface */ void io_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vpi_vprintf(fmt, ap); va_end(ap); } void tf_warning(const char *fmt, ...) { va_list ap; vpi_printf("warning! "); va_start(ap, fmt); vpi_vprintf(fmt, ap); va_end(ap); } void tf_error(const char *fmt, ...) { va_list ap; vpi_printf("error! "); va_start(ap, fmt); vpi_vprintf(fmt, ap); va_end(ap); } PLI_INT32 tf_message(PLI_INT32 level, char*facility, char*messno, char*fmt, ...) { va_list ap; (void)level; /* Parameter is not used. */ vpi_printf("%s[%s] ", facility, messno); va_start(ap, fmt); vpi_vprintf(fmt, ap); va_end(ap); vpi_printf("\n"); return 0; } iverilog-10_1/libveriuser/math.c000066400000000000000000000031701265551621300167730ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "config.h" # include "vpi_user.h" # include "veriuser.h" void tf_multiply_long(PLI_INT32*aof_low1, PLI_INT32*aof_high1, PLI_INT32 aof_low2, PLI_INT32 aof_high2) { ivl_u64_t a, b; a = *aof_high1; a = (a << 32) | *aof_low1; b = aof_high2; b = (b << 32) | aof_low2; a *= b; *aof_high1 = (a >> 32) & 0xffffffff; *aof_low1 = a & 0xffffffff; } void tf_real_to_long(double real, PLI_INT32*low, PLI_INT32*high) { ivl_u64_t rtn = (ivl_u64_t)real; *high = (rtn >> 32) & 0xffffffff; *low = rtn & 0xffffffff; } void tf_long_to_real(PLI_INT32 low, PLI_INT32 high, double *real) { ivl_u64_t a; a = high; a = (a << 32) | low; *real = (double)a; } iverilog-10_1/libveriuser/mc_scan_plusargs.c000066400000000000000000000032741265551621300213720ustar00rootroot00000000000000/* * Copyright (c) 2002-2013 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include /* * mc_scan_plusargs implemented using VPI interface */ char *mc_scan_plusargs(char *plusarg) { int argc, diff; char **argv; s_vpi_vlog_info vpi_vlog_info; /* get command line */ if (! vpi_get_vlog_info(&vpi_vlog_info)) return (char *)0; /* for each argument */ argv = vpi_vlog_info.argv; for (argc = 0; argc < vpi_vlog_info.argc; argc++, argv++) { char *a, *p; a = *argv; p = plusarg; /* only plusargs */ if (*a != '+') continue; a += 1; /* impossible matches */ if (strlen(a) < strlen(p)) continue; diff = 0; while (*p) { if (*a != *p) { diff = 1; break; } a++; p++; } if (!diff) return a; } /* didn't find it yet */ return (char *)0; } iverilog-10_1/libveriuser/nodeinfo.c000066400000000000000000000026741265551621300176530ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "priv.h" /* XXX Not declared or used anywhere? */ struct t_tfnodeinfo* tf_nodeinfo(PLI_INT32 a, struct t_tfnodeinfo*ip) { (void)a; /* Parameter is not used. */ (void)ip; /* Parameter is not used. */ if (pli_trace) { fprintf(pli_trace, "tf_nodeinfo: enter.\n"); fflush(pli_trace); } fprintf(stderr, "tf_nodeinfo: XXXX not implemented. XXXX\n"); if (pli_trace) { fprintf(pli_trace, "tf_nodeinfo: return.\n"); fflush(pli_trace); } return 0; } iverilog-10_1/libveriuser/nump.c000066400000000000000000000024771265551621300170320ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "veriuser.h" /* * tf_nump implemented using VPI interface */ int tf_inump(void *obj) { vpiHandle sys_h, sys_i; int cnt; sys_h = (vpiHandle)obj; sys_i = vpi_iterate(vpiArgument, sys_h); /* count number of args */ for (cnt = 0; sys_i && vpi_scan(sys_i); cnt++); return cnt; } int tf_nump(void) { vpiHandle sys_h = vpi_handle(vpiSysTfCall, 0 /* NULL */); return tf_inump(sys_h); } iverilog-10_1/libveriuser/priv.c000066400000000000000000000026721265551621300170300ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include # include FILE* pli_trace = 0; static char string_buffer[8192]; static unsigned string_fill = 0; static void buffer_reset(void) { string_fill = 0; } char* __acc_newstring(const char*txt) { char*res; unsigned len; if (txt == 0) return 0; len = strlen(txt); assert(len < sizeof string_buffer); if ((string_fill + len + 1) >= sizeof string_buffer) buffer_reset(); res = string_buffer + string_fill; strcpy(string_buffer + string_fill, txt); string_fill += len + 1; return res; } iverilog-10_1/libveriuser/priv.h000066400000000000000000000023141265551621300170260ustar00rootroot00000000000000#ifndef IVL_priv_H #define IVL_priv_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include /* * This function implements the acc_ string buffer, by adding the * input string to the buffer, and returning a pointer to the first * character of the new string. */ extern char* __acc_newstring(const char*txt); /* * Trace file for logging ACC and TF calls. */ FILE* pli_trace; #endif /* IVL_priv_H */ iverilog-10_1/libveriuser/putlongp.c000066400000000000000000000033701265551621300177140ustar00rootroot00000000000000/* * Copyright (c) 2002 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include /* * tf_putlongp implemented using VPI interface */ void tf_putlongp(int n, int lowvalue, int highvalue) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value val; int type; char str[20]; assert(n >= 0); /* get task/func handle */ sys_h = vpi_handle(vpiSysTfCall, 0); sys_i = vpi_iterate(vpiArgument, sys_h); type = vpi_get(vpiType, sys_h); /* verify function */ assert(!(n == 0 && type != vpiSysFuncCall)); /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) assert(0); n--; } if (!arg_h) arg_h = sys_h; /* fill in vpi_value */ sprintf(str, "%x%08x", highvalue, lowvalue); val.format = vpiHexStrVal; val.value.str = str; vpi_put_value(arg_h, &val, 0, vpiNoDelay); vpi_free_object(sys_i); } iverilog-10_1/libveriuser/putp.c000066400000000000000000000070521265551621300170350ustar00rootroot00000000000000/* * Copyright (c) 2002-2009 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "priv.h" /* * tf_putp and friends implemented using VPI interface */ PLI_INT32 tf_iputp(PLI_INT32 n, PLI_INT32 value, void *obj) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value val; int rtn = 0, type; assert(n >= 0); /* get task/func handle */ sys_h = (vpiHandle)obj; type = vpi_get(vpiType, sys_h); /* Special case, we are putting the return value. */ if ((type == vpiSysFuncCall) && (n == 0)) { val.format = vpiIntVal; val.value.integer = value; vpi_put_value(sys_h, &val, 0, vpiNoDelay); if (pli_trace) { fprintf(pli_trace, "tf_iputp(, value=%d, func=%s) " "--> %d\n", (int)value, vpi_get_str(vpiName, obj), 0); } return 0; } if ((n == 0) && (type != vpiSysFuncCall)) { if (pli_trace) { fprintf(pli_trace, "tf_iputp(, value=%d, func=%s) " "--> %d\n", (int)value, vpi_get_str(vpiName, obj), 1); } return 1; } sys_i = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) { rtn = 1; goto out; } n--; } /* fill in vpi_value */ val.format = vpiIntVal; val.value.integer = value; vpi_put_value(arg_h, &val, 0, vpiNoDelay); if (arg_h) vpi_free_object(sys_i); out: if (pli_trace) { fprintf(pli_trace, "tf_iputp(n=%d, value=%d, obj=%p) --> %d\n", (int)n, (int)value, obj, rtn); } return rtn; } PLI_INT32 tf_putp(PLI_INT32 n, PLI_INT32 value) { int rtn = tf_iputp(n, value, vpi_handle(vpiSysTfCall, 0)); return rtn; } PLI_INT32 tf_iputrealp(PLI_INT32 n, double value, void *obj) { vpiHandle sys_h, sys_i, arg_h = 0; s_vpi_value val; int rtn = 0, type; assert(n >= 0); /* get task/func handle */ sys_h = (vpiHandle)obj; sys_i = vpi_iterate(vpiArgument, sys_h); type = vpi_get(vpiType, sys_h); /* verify function */ if (n == 0 && type != vpiSysFuncCall) { rtn = 1; goto free; } /* find nth arg */ while (n > 0) { if (!(arg_h = vpi_scan(sys_i))) { rtn = 1; goto out; } n--; } if (!arg_h) arg_h = sys_h; /* fill in vpi_value */ val.format = vpiRealVal; val.value.real = value; vpi_put_value(arg_h, &val, 0, vpiNoDelay); free: vpi_free_object(sys_i); out: if (pli_trace) { fprintf(pli_trace, "tf_iputrealp(n=%d, value=%f, obj=%p) --> %d\n", (int)n, value, obj, rtn); } return rtn; } PLI_INT32 tf_putrealp(PLI_INT32 n, double value) { int rtn = tf_iputrealp(n, value, vpi_handle(vpiSysTfCall, 0)); return rtn; } iverilog-10_1/libveriuser/spname.c000066400000000000000000000027071265551621300173320ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "priv.h" char* tf_spname(void) { char*rtn; vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle scope = vpi_handle(vpiScope, sys); rtn = __acc_newstring(vpi_get_str(vpiFullName, scope)); if (pli_trace) { fprintf(pli_trace, "%s: tf_spname() --> %s\n", vpi_get_str(vpiName,sys), rtn); } return rtn; } char *tf_imipname(void *obj) { return vpi_get_str(vpiFullName, vpi_handle(vpiScope, (vpiHandle)obj)); } char *tf_mipname(void) { return tf_imipname(vpi_handle(vpiSysTfCall,0)); } iverilog-10_1/libveriuser/typep.c000066400000000000000000000035631265551621300172110ustar00rootroot00000000000000/* * Copyright (c) 2002 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include PLI_INT32 tf_typep(PLI_INT32 narg) { vpiHandle sys_h, argv, arg_h = 0; int rtn; assert(narg > 0); /* get task/func handle */ sys_h = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, sys_h); /* find nth arg */ while (narg > 0) { /* Watch that the argument is not out of range. */ if (!(arg_h = vpi_scan(argv))) return TF_NULLPARAM; narg -= 1; } switch (vpi_get(vpiType, arg_h)) { case vpiConstant: switch (vpi_get(vpiConstType, arg_h)) { case vpiStringConst: rtn = TF_STRING; break; case vpiRealConst: rtn = TF_READONLYREAL; break; default: rtn = TF_READONLY; break; } break; case vpiIntegerVar: case vpiReg: case vpiMemoryWord: rtn = TF_READWRITE; break; case vpiRealVar: rtn = TF_READWRITEREAL; break; default: rtn = TF_READONLY; break; } vpi_free_object(argv); return rtn; } iverilog-10_1/libveriuser/veriusertfs.c000066400000000000000000000253371265551621300204340ustar00rootroot00000000000000/* * Copyright (c) 2002-2014 Michael Ruff (mruff at chiaro.com) * Michael Runyan (mrunyan at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Contains the routines required to implement veriusertfs routines * via VPI. This is extremely ugly, so don't look after eating dinner. */ # include # include # include # include # include "config.h" # include "priv.h" # include "vpi_user.h" # include "veriuser.h" # include "ivl_alloc.h" /* * local structure used to hold the persistent veriusertfs data * and anything else we decide to put in here, like workarea data. */ typedef struct t_pli_data { p_tfcell tf; /* pointer to veriusertfs cell */ int paramvc; /* parameter number for misctf */ } s_pli_data, *p_pli_data; static PLI_INT32 compiletf(char *); static PLI_INT32 calltf(char *); static PLI_INT32 callback(p_cb_data); /* * Keep a pointer to the user data so that it can be freed when the * simulation is finished. */ static p_pli_data* udata_store = 0; static unsigned udata_count = 0; static PLI_INT32 sys_end_of_simulation(p_cb_data cb_data) { unsigned idx; (void)cb_data; /* Parameter is not used. */ for (idx = 0; idx < udata_count; idx += 1) { free(udata_store[idx]); } free(udata_store); udata_store = 0; udata_count = 0; return 0; } /* * Register veriusertfs routines/wrappers. Iterate over the tfcell * array, registering each function. */ void veriusertfs_register_table(p_tfcell vtable) { static int need_EOS_cb = 1; const char*path; p_tfcell tf; s_vpi_systf_data tf_data; p_pli_data data; if (!pli_trace && (path = getenv("PLI_TRACE"))) { static char trace_buf[1024]; if (strcmp(path,"-") == 0) pli_trace = stdout; else { pli_trace = fopen(path, "w"); if (!pli_trace) { perror(path); exit(1); } } setvbuf(pli_trace, trace_buf, _IOLBF, sizeof(trace_buf)); } for (tf = vtable; tf; tf++) { /* last element */ if (tf->type == 0) break; /* force forwref true */ if (!tf->forwref) { vpi_printf("veriusertfs: %s, forcing forwref = true\n", tf->tfname); } /* squirrel away veriusertfs in persistent user_data */ data = calloc(1, sizeof(s_pli_data)); udata_count += 1; udata_store = (p_pli_data*)realloc(udata_store, udata_count*sizeof(p_pli_data*)); udata_store[udata_count-1] = data; if (need_EOS_cb) { s_cb_data cb_data; cb_data.reason = cbEndOfSimulation; cb_data.time = 0; cb_data.cb_rtn = sys_end_of_simulation; cb_data.user_data = "system"; vpi_register_cb(&cb_data); need_EOS_cb = 0; } data->tf = tf; /* Build a VPI system task/function structure, and point it to the pli_data that represents this function. Supply wrapper functions for the system task actions. */ memset(&tf_data, 0, sizeof(s_vpi_systf_data)); switch (tf->type) { case usertask: tf_data.type = vpiSysTask; break; case userfunction: tf_data.sysfunctype = vpiIntFunc; tf_data.type = vpiSysFunc; break; case userrealfunction: tf_data.sysfunctype = vpiRealFunc; tf_data.type = vpiSysFunc; break; default: vpi_printf("veriusertfs: %s, unsupported type %d\n", tf->tfname, tf->type); continue; } tf_data.tfname = tf->tfname; tf_data.compiletf = compiletf; tf_data.calltf = calltf; tf_data.sizetf = (PLI_INT32 (*)(PLI_BYTE8 *))tf->sizetf; tf_data.user_data = (char *)data; if (pli_trace) { fprintf(pli_trace, "Registering system %s:\n", tf->type == usertask ? "task" : "function"); fprintf(pli_trace, " tfname : %s\n", tf->tfname); if (tf->data) fprintf(pli_trace, " data : %d\n", tf->data); if (tf->checktf) fprintf(pli_trace, " checktf: %p\n", tf->checktf); if (tf->sizetf) fprintf(pli_trace, " sizetf : %p\n", tf->sizetf); if (tf->calltf) fprintf(pli_trace, " calltf : %p\n", tf->calltf); if (tf->misctf) fprintf(pli_trace, " misctf : %p\n", tf->misctf); } /* register */ vpi_register_systf(&tf_data); } return; } /* * This function calls the veriusertfs checktf and sets up all the * callbacks misctf requires. */ static PLI_INT32 compiletf(char *data) { p_pli_data pli; p_tfcell tf; s_cb_data cb_data; vpiHandle call_h, arg_i, arg_h; int rtn = 0; /* cast back from opaque */ pli = (p_pli_data)data; tf = pli->tf; /* get call handle */ call_h = vpi_handle(vpiSysTfCall, NULL); /* Attach the pli_data structure to the vpi handle of the system task. This is how I manage the map from vpiHandle to PLI1 pli data. We do it here (instead of during register) because this is the first that I have both the vpiHandle and the pli_data. */ vpi_put_userdata(call_h, pli); /* default cb_data */ memset(&cb_data, 0, sizeof(s_cb_data)); cb_data.cb_rtn = callback; cb_data.user_data = data; /* register EOS misctf callback */ cb_data.reason = cbEndOfSimulation; cb_data.obj = call_h; vpi_register_cb(&cb_data); /* If there is a misctf function, then create a value change callback for all the arguments. In the tf_* API, misctf functions get value change callbacks, controlled by the tf_asyncon and tf_asyncoff functions. */ if (tf->misctf && ((arg_i = vpi_iterate(vpiArgument, call_h)) != NULL)) { int paramvc = 1; cb_data.reason = cbValueChange; while ((arg_h = vpi_scan(arg_i)) != NULL) { /* replicate user_data for each instance */ p_pli_data dp = calloc(1, sizeof(s_pli_data)); memcpy(dp, cb_data.user_data, sizeof(s_pli_data)); dp->paramvc = paramvc++; cb_data.user_data = (char *)dp; cb_data.obj = arg_h; vpi_register_cb(&cb_data); } } /* * Since we are in compiletf, checktf and misctf need to * be executed. Check runs first to match other simulators. */ if (tf->checktf) { if (pli_trace) { fprintf(pli_trace, "Call %s->checktf(reason_checktf)\n", tf->tfname); } rtn = tf->checktf(tf->data, reason_checktf); } if (tf->misctf) { if (pli_trace) { fprintf(pli_trace, "Call %s->misctf" "(user_data=%d, reason=%d, paramvc=%d)\n", tf->tfname, tf->data, reason_endofcompile, 0); } tf->misctf(tf->data, reason_endofcompile, 0); } return rtn; } /* * This function is the wrapper for the veriusertfs calltf routine. */ static PLI_INT32 calltf(char *data) { int rc = 0; p_pli_data pli; p_tfcell tf; /* cast back from opaque */ pli = (p_pli_data)data; tf = pli->tf; /* execute calltf */ if (tf->calltf) { if (pli_trace) { fprintf(pli_trace, "Call %s->calltf(%d, %d)\n", tf->tfname, tf->data, reason_calltf); } rc = tf->calltf(tf->data, reason_calltf); } return rc; } /* * This function is the wrapper for all the misctf callbacks */ extern int async_misctf_enable; static PLI_INT32 callback(p_cb_data data) { p_pli_data pli; p_tfcell tf; int reason; int paramvc = 0; PLI_INT32 rc; /* not enabled */ if (data->reason == cbValueChange && !async_misctf_enable) return 0; /* cast back from opaque */ pli = (p_pli_data)data->user_data; tf = pli->tf; switch (data->reason) { case cbValueChange: reason = reason_paramvc; paramvc = pli->paramvc; break; case cbEndOfSimulation: reason = reason_finish; break; case cbReadWriteSynch: reason = reason_synch; break; case cbReadOnlySynch: reason = reason_rosynch; break; case cbAfterDelay: reason = reason_reactivate; break; default: reason = -1; assert(0); } if (pli_trace) { fprintf(pli_trace, "Call %s->misctf" "(user_data=%d, reason=%d, paramvc=%d)\n", tf->tfname, tf->data, reason, paramvc); } /* execute misctf */ rc = (tf->misctf) ? tf->misctf(tf->data, reason, paramvc) : 0; return rc; } PLI_INT32 tf_isynchronize(void*obj) { vpiHandle sys = (vpiHandle)obj; p_pli_data pli = vpi_get_userdata(sys); s_cb_data cb; s_vpi_time ti; ti.type = vpiSuppressTime; cb.reason = cbReadWriteSynch; cb.cb_rtn = callback; cb.obj = sys; cb.time = &ti; cb.user_data = (char *)pli; vpi_register_cb(&cb); if (pli_trace) fprintf(pli_trace, "tf_isynchronize(%p) --> %d\n", obj, 0); return 0; } PLI_INT32 tf_synchronize(void) { return tf_isynchronize(tf_getinstance()); } PLI_INT32 tf_irosynchronize(void*obj) { vpiHandle sys = (vpiHandle)obj; p_pli_data pli = vpi_get_userdata(sys); s_cb_data cb; s_vpi_time ti = {vpiSuppressTime, 0, 0, 0.0}; cb.reason = cbReadOnlySynch; cb.cb_rtn = callback; cb.obj = sys; cb.time = &ti; cb.user_data = (char *)pli; vpi_register_cb(&cb); if (pli_trace) fprintf(pli_trace, "tf_irosynchronize(%p) --> %d\n", obj, 0); return 0; } PLI_INT32 tf_rosynchronize(void) { return tf_irosynchronize(tf_getinstance()); } PLI_INT32 tf_isetrealdelay(double dly, void*obj) { vpiHandle sys = (vpiHandle)obj; p_pli_data pli = vpi_get_userdata(sys); s_cb_data cb; s_vpi_time ti = {vpiSimTime, 0, 0, 0.0}; /* Scale delay to SimTime */ ivl_u64_t delay = ((dly / pow(10, tf_gettimeunit() - tf_gettimeprecision())) + 0.5); ti.high = delay >> 32 & 0xffffffff; ti.low = delay & 0xffffffff; cb.reason = cbAfterDelay; cb.cb_rtn = callback; cb.obj = sys; cb.time = &ti; cb.user_data = (char *)pli; vpi_register_cb(&cb); if (pli_trace) fprintf(pli_trace, "tf_isetrealdelay(%f, %p) --> %d\n", dly, obj, 0); return 0; } PLI_INT32 tf_setrealdelay(double dly) { return tf_isetrealdelay(dly, tf_getinstance()); } iverilog-10_1/libveriuser/workarea.c000066400000000000000000000035511265551621300176600ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "ivl_alloc.h" /* * Keep a list of sys handle to work area bindings. */ struct workarea_cell { vpiHandle sys; void* area; struct workarea_cell*next; }; static struct workarea_cell*area_list = 0; PLI_INT32 tf_setworkarea(void*workarea) { vpiHandle sys; struct workarea_cell*cur; sys = vpi_handle(vpiSysTfCall, 0); cur = area_list; while (cur) { if (cur->sys == sys) { cur->area = workarea; return 0; } cur = cur->next; } cur = calloc(1, sizeof (struct workarea_cell)); cur->next = area_list; cur->sys = sys; cur->area = workarea; area_list = cur; return 0; } PLI_BYTE8* tf_getworkarea(void) { struct workarea_cell*cur; vpiHandle sys; sys = vpi_handle(vpiSysTfCall, 0); cur = area_list; while (cur) { if (cur->sys == sys) { return cur->area; } cur = cur->next; } return 0; } iverilog-10_1/link_const.cc000066400000000000000000000200331265551621300160120ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "netlist.h" # include "netmisc.h" # include "ivl_assert.h" /* * Scan the link for drivers. If there are only constant drivers, then * the nexus has a known constant value. */ bool Nexus::drivers_constant() const { if (driven_ == VAR) return false; if (driven_ != NO_GUESS) return true; unsigned constant_drivers = 0; for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { /* A target of a procedural assign or force statement can't be treated as constant. */ const NetNet*sig = dynamic_cast(cur->get_obj()); if (sig && (sig->peek_lref() > 0)) { driven_ = VAR; return false; } /* If we are connected to a tran, there may be a driver on the other side of the tran. We could try checking for this, but for now, be pessimistic. */ if (dynamic_cast(cur->get_obj())) { driven_ = VAR; return false; } Link::DIR cur_dir = cur->get_dir(); if (cur_dir == Link::INPUT) continue; /* If this is an input or inout port of a root module, then this is probably not a constant value. I certainly don't know what the value is, anyhow. This can happen in cases like this: module main(sig); input sig; endmodule If main is a root module (it has no parent) then sig is not constant because it connects to an unspecified outside world. */ if (cur_dir == Link::PASSIVE) { if (sig == 0 || sig->scope()->parent() != 0) continue; if (sig->port_type() == NetNet::NOT_A_PORT) continue; if (sig->port_type() == NetNet::POUTPUT) continue; driven_ = VAR; return false; } /* If there is an implicit pullup/pulldown on a net, count it as a constant driver. */ if (sig) switch (sig->type()) { case NetNet::SUPPLY0: case NetNet::TRI0: constant_drivers += 1; driven_ = V0; continue; case NetNet::SUPPLY1: case NetNet::TRI1: constant_drivers += 1; driven_ = V1; continue; default: break; } if (! dynamic_cast(cur->get_obj())) { driven_ = VAR; return false; } constant_drivers += 1; } /* If there is more than one constant driver for this nexus, we would need to resolve the constant value, taking into account the drive strengths. This is a lot of work for something that will rarely occur, so for now leave the resolution to be done at run time. */ if (constant_drivers > 1) { driven_ = VAR; return false; } return true; } verinum::V Nexus::driven_value() const { switch (driven_) { case V0: return verinum::V0; case V1: return verinum::V1; case Vx: return verinum::Vx; case Vz: return verinum::Vz; case VAR: assert(0); break; case NO_GUESS: break; } const Link*cur = list_; verinum::V val = verinum::Vz; for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) { const NetConst*obj; const NetNet*sig; if ((obj = dynamic_cast(cur->get_obj()))) { // Multiple drivers are not currently supported. ivl_assert(*obj, val == verinum::Vz); val = obj->value(cur->get_pin()); } else if ((sig = dynamic_cast(cur->get_obj()))) { // If we find an implicit pullup or pulldown on a // net, this is a good guess for the driven value, // but keep looking for other drivers. if ((sig->type() == NetNet::SUPPLY0) || (sig->type() == NetNet::TRI0)) { // Multiple drivers are not currently supported. ivl_assert(*obj, val == verinum::Vz); val = verinum::V0; } if ((sig->type() == NetNet::SUPPLY1) || (sig->type() == NetNet::TRI1)) { // Multiple drivers are not currently supported. ivl_assert(*obj, val == verinum::Vz); val = verinum::V1; } } } /* Cache the result. */ switch (val) { case verinum::V0: driven_ = V0; break; case verinum::V1: driven_ = V1; break; case verinum::Vx: driven_ = Vx; break; case verinum::Vz: driven_ = Vz; break; } return val; } verinum Nexus::driven_vector() const { const Link*cur = list_; verinum val; unsigned width = 0; for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) { const NetConst*obj; const NetNet*sig; if ((obj = dynamic_cast(cur->get_obj()))) { // Multiple drivers are not currently supported. ivl_assert(*obj, val.len() == 0); ivl_assert(*obj, cur->get_pin() == 0); val = obj->value(); width = val.len(); } else if ((sig = dynamic_cast(cur->get_obj()))) { width = sig->vector_width(); // If we find an implicit pullup or pulldown on a // net, this is a good guess for the driven value, // but keep looking for other drivers. if ((sig->type() == NetNet::SUPPLY0) || (sig->type() == NetNet::TRI0)) { // Multiple drivers are not currently supported. ivl_assert(*obj, val.len() == 0); val = verinum(verinum::V0, width); } if ((sig->type() == NetNet::SUPPLY1) || (sig->type() == NetNet::TRI1)) { // Multiple drivers are not currently supported. ivl_assert(*obj, val.len() == 0); val = verinum(verinum::V1, width); } } } // If we have a width but not a value, this must be an undriven net. if (val.len() != width) val = verinum(verinum::Vz, width); return val; } /* * Calculate a vector that represent all the bits of the vector, with * each driven bit set to true, otherwise false. */ vector Nexus::driven_mask(void) const { vector mask (vector_width()); for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { Link::DIR link_dir = cur->get_dir(); if (link_dir==Link::PASSIVE) continue; if (link_dir==Link::INPUT) continue; const NetPins*obj = cur->get_obj(); // If the link is to a variable (REG or INTEGER) then // the variable is driving all the bits. We have our // complete answer, mark all the bits as driven and // finish. Otherwise, we are not going to get new // information from this node, move on. if (const NetNet*sig = dynamic_cast (obj)) { NetNet::Type sig_type = sig->type(); if (sig_type==NetNet::INTEGER || sig_type==NetNet::REG) { for (size_t idx = 0 ; idx < mask.size() ; idx += 1) mask[idx] = true; return mask; } continue; } const NetPartSelect*obj_ps = dynamic_cast(obj); if(obj_ps) { if (obj_ps->dir()==NetPartSelect::VP) { if(cur->get_pin() != 0) continue; for (size_t idx = 0 ; idx < mask.size() ; idx += 1) mask[idx] = true; return mask; } else { if (cur->get_pin() != 1) continue; } for (unsigned idx = 0 ; idx < obj_ps->width() ; idx += 1) { size_t bit = idx + obj_ps->base(); ivl_assert(*obj, bit < mask.size()); mask[bit] = true; } continue; } for (size_t idx = 0 ; idx < mask.size() ; idx += 1) mask[idx] = true; return mask; } return mask; } iverilog-10_1/load_module.cc000066400000000000000000000123471265551621300161440ustar00rootroot00000000000000/* * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "util.h" # include "parse_api.h" # include "compiler.h" # include # include # include # include # include # include # include # include # include # include "ivl_alloc.h" /* * The module library items are maps of key names to file name within * the directory. */ struct module_library { char*dir; bool key_case_sensitive; mapname_map; struct module_library*next; }; static struct module_library*library_list = 0; static struct module_library*library_last = 0; const char dir_character = '/'; extern char depfile_mode; extern FILE *depend_file; /* * Use the type name as a key, and search the module library for a * file name that has that key. */ bool load_module(const char*type) { char path[4096]; char*ltype = strdup(type); for (char*tmp = ltype ; *tmp ; tmp += 1) *tmp = tolower(*tmp); for (struct module_library*lcur = library_list ; lcur != 0 ; lcur = lcur->next) { const char*key = lcur->key_case_sensitive? type : ltype; map::const_iterator cur; cur = lcur->name_map.find(key); if (cur == lcur->name_map.end()) continue; sprintf(path, "%s%c%s", lcur->dir, dir_character, (*cur).second); if(depend_file) { if (depfile_mode == 'p') { fprintf(depend_file, "M %s\n", path); } else if (depfile_mode != 'i') { fprintf(depend_file, "%s\n", path); } fflush(depend_file); } if (ivlpp_string) { char*cmdline = (char*)malloc(strlen(ivlpp_string) + strlen(path) + 4); strcpy(cmdline, ivlpp_string); strcat(cmdline, " \""); strcat(cmdline, path); strcat(cmdline, "\""); if (verbose_flag) cerr << "Executing: " << cmdline << endl<< flush; FILE*file = popen(cmdline, "r"); if (verbose_flag) cerr << "...parsing output from preprocessor..." << endl << flush; pform_parse(path, file); pclose(file); free(cmdline); } else { if (verbose_flag) cerr << "Loading library file " << path << "." << endl; FILE*file = fopen(path, "r"); assert(file); pform_parse(path, file); fclose(file); } if (verbose_flag) cerr << "... Load module complete." << endl << flush; return true; } return false; } /* * This function takes the name of a library directory that the caller * passed, and builds a name index for it. */ int build_library_index(const char*path, bool key_case_sensitive) { DIR*dir = opendir(path); if (dir == 0) return -1; if (verbose_flag) { cerr << "Indexing library: " << path << endl; } struct module_library*mlp = new struct module_library; mlp->dir = strdup(path); mlp->key_case_sensitive = key_case_sensitive; /* Scan the director for files. check each file name to see if it has one of the configured suffixes. If it does, then use the root of the name as the key and index the file name. */ while (struct dirent*de = readdir(dir)) { unsigned namsiz = strlen(de->d_name); char*key = 0; for (list::iterator suf = library_suff.begin() ; suf != library_suff.end() ; ++ suf ) { const char*sufptr = *suf; unsigned sufsiz = strlen(sufptr); if (sufsiz >= namsiz) continue; /* If the directory is case insensitive, then so is the suffix. */ if (key_case_sensitive) { if (strcmp(de->d_name + (namsiz-sufsiz), sufptr) != 0) continue; } else { if (strcasecmp(de->d_name + (namsiz-sufsiz), sufptr) != 0) continue; } key = new char[namsiz-sufsiz+1]; strncpy(key, de->d_name, namsiz-sufsiz); key[namsiz-sufsiz] = 0; break; } if (key == 0) continue; /* If the key is not to be case sensitive, then change it to lowercase. */ if (! key_case_sensitive) for (char*tmp = key ; *tmp ; tmp += 1) *tmp = tolower(*tmp); mlp->name_map[key] = strdup(de->d_name); delete[]key; } closedir(dir); if (library_last) { assert(library_list); library_last->next = mlp; mlp->next = 0; library_last = mlp; } else { library_list = mlp; library_last = mlp; mlp->next = 0; } return 0; } iverilog-10_1/lpm.txt000066400000000000000000000022221265551621300146710ustar00rootroot00000000000000 WHAT IS LPM LPM (Library of Parameterized Modules) is EIS-IS standard 103-A. It is a standard library of abstract devices that are designed to be close enough to the target hardware to be easily translated, yet abstract enough to support a variety of target technologies without excessive constraints. Icarus Verilog uses LPM internally to represent idealized hardware, especially when doing target neutral synthesis. In general, the user does not even see the LPM that Icarus Verilog generates, because the LPM devices are translated into technology specific devices by the final code generator or target specific optimizers. INTERNAL USES OF LPM Internally, Icarus Verilog uses LPM devices to represent the design in abstract, especially when synthesizing such functions as addition, flip-flops, etc. The ``synth'' functor generates LPM modules when interpreting procedural constructs. The functor generates the LPM objects needed to replace a behavioral description, and uses attributes to tag the devices with LPM properties. Code generators need to understand the supported LPM devices so that they can translate the devices into technology specific devices. iverilog-10_1/macosx.txt000066400000000000000000000061711265551621300154020ustar00rootroot00000000000000 This file describes the procedure to build and install Icarus Verilog on Mac OS X. I assume that you have experience with Unix and Terminal.app and a basic knowledge of how to download, compile and install software from source form. Yasuhisa Kato wrote another set of instructions that has also been known to work: . You may try those instructions instead of these, although they are essentially quite similar. 1) Obtain and install a libdl compatibility library. If you don't already have /usr/local/lib/libdl.{a,dylib} and /usr/local/include/dlfcn.h, you can obtain the source for a compatibility layer from at least one of two places: http://download.sourceforge.net/fink/dlcompat-20010831.tar.gz http://www.omnigroup.com/~bungi/dlcompat-20010831.tar.gz Unpack this tar file and read the README and Makefile. Install the library according to the instructions. Installation in /usr/local is strongly recommended since otherwise autoconf very likely won't be able to find it. 2) Make sure you have a copy of the 'gperf' tool. This does not come with the Mac OS X 10.1 developer tools, so you probably don't. You can check with: % which gperf If not found, grab a gperf source package and install it. See "GPERF FOR MACOSX" below. Snapshots of Icarus Verilog source now come with the lexor_keyword.cc file pre-made, so if you have trouble with gperf, then just make sure the distributed lexor_keyword.cc is newer than lexor_keyword.gperf, and use that. 3) If working with source from git, you must run autoconf in the top directory. This is simplified by the 'autoconf.sh' script at the top of the source tree: sh ./autoconf.sh This will also run the gperf command, so make sure you've completed step #2 first. 4) Configure, build and install the Icarus Verilog sources as normal. The only change you need to make here is to use a configure command like: % CC="cc -no-cpp-precomp" ./configure This assumes you are using 'sh', 'zsh', or 'bash'. If you are using 'csh' or 'tcsh', then you'll want something like: % setenv CC "cc -no-cpp-precomp" % ./configure You can, of course, add other configure options. 6) NOTE: 'make check' will not work until after 'make install' has been run since dynamically loaded code is searched for in the install location rather than the build location. The dlopen emulation library doesn't support a search path option. If you are worried about overwriting a working installation with a new, potentially broken one, you can always configure using --prefix="/some/path", and install there to make sure everything is working and then re-configure with the real path you want to install at, make clean, and make install. 5) Done! GPERF FOR MACOSX Get version 2.7.2 of gperf from here: Get a MacosX patch from here: http://www.eternal.nest.or.jp/~shiro/binaries/gperf-2.7.2-macosx-patch.gz Apply the patch to the gperf-2.7.2 source that you previously downloaded, then follow the remaining gperf installation instructions. iverilog-10_1/main.cc000066400000000000000000001026021265551621300145760ustar00rootroot00000000000000const char COPYRIGHT[] = "Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "version_base.h" # include "version_tag.h" const char NOTICE[] = " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; # include # include # include # include # include # include # include # include # include #if defined(HAVE_TIMES) # include #endif #if defined(HAVE_GETOPT_H) # include #endif # include "pform.h" # include "parse_api.h" # include "PGenerate.h" # include "netlist.h" # include "target.h" # include "compiler.h" # include "discipline.h" # include "t-dll.h" #if defined(__MINGW32__) && !defined(HAVE_GETOPT_H) extern "C" int getopt(int argc, char*argv[], const char*fmt); extern "C" int optind; extern "C" const char*optarg; #endif #if defined(__CYGWIN32__) && !defined(HAVE_GETOPT_H) extern "C" int getopt(int argc, char*argv[], const char*fmt); extern "C" int optind; extern "C" const char*optarg; #endif #if defined(TRAP_SIGINT_FOR_DEBUG) /* * This is a debugging aid. Do not compile it in general, but leave it * here for those days when I need the ability to cleanly exit on a * signal interrupt. */ # include static void signals_handler(int sig) { fprintf(stderr, "Exit on signal %d\n", sig); exit(1); } #endif # include "ivl_alloc.h" /* Count errors detected in flag processing. */ unsigned flag_errors = 0; const char*basedir = strdup("."); /* * These are the language support control flags. These support which * language features (the generation) to support. The generation_flag * is a major mode, and the gn_* flags control specific sub-features. */ generation_t generation_flag = GN_DEFAULT; bool gn_icarus_misc_flag = true; bool gn_cadence_types_flag = true; bool gn_specify_blocks_flag = true; bool gn_assertions_flag = true; bool gn_io_range_error_flag = true; bool gn_strict_ca_eval_flag = false; bool gn_strict_expr_width_flag = false; bool gn_verilog_ams_flag = false; /* * For some generations we allow a system function to be called * as a task and only print a warning message. The default for * this is that it is a run time error. */ ivl_sfunc_as_task_t def_sfunc_as_task = IVL_SFUNC_AS_TASK_ERROR; map flags; char*vpi_module_list = 0; void add_vpi_module(const char*name) { if (vpi_module_list == 0) { vpi_module_list = strdup(name); } else { char*tmp = (char*)realloc(vpi_module_list, strlen(vpi_module_list) + strlen(name) + 2); strcat(tmp, ","); strcat(tmp, name); vpi_module_list = tmp; } flags["VPI_MODULE_LIST"] = vpi_module_list; } map missing_modules; map library_file_map; list library_suff; list roots; char*ivlpp_string = 0; char depfile_mode = 'a'; char* depfile_name = NULL; FILE *depend_file = NULL; /* * These are the warning enable flags. */ bool warn_implicit = false; bool warn_implicit_dimensions = false; bool warn_timescale = false; bool warn_portbinding = false; bool warn_inf_loop = false; bool warn_ob_select = false; bool warn_sens_entire_vec = false; bool warn_sens_entire_arr = false; bool warn_anachronisms = false; /* * Debug message class flags. */ bool debug_scopes = false; bool debug_eval_tree = false; bool debug_elaborate = false; bool debug_emit = false; bool debug_synth2 = false; bool debug_optimizer = false; /* * Optimization control flags. */ unsigned opt_const_func = 0; /* * Miscellaneous flags. */ bool disable_virtual_pins = false; unsigned long array_size_limit = 16777216; // Minimum required by IEEE-1364? unsigned recursive_mod_limit = 10; bool disable_concatz_generation = false; /* * Verbose messages enabled. */ bool verbose_flag = false; unsigned integer_width = 32; /* * Width limit for unsized expressions. */ unsigned width_cap = 65536; int def_ts_units = 0; int def_ts_prec = 0; /* * Keep a heap of identifier strings that I encounter. This is a more * efficient way to allocate those strings. */ StringHeapLex lex_strings; StringHeapLex filename_strings; StringHeapLex bits_strings; /* * In library searches, Windows file names are never case sensitive. */ #if defined(__MINGW32__) const bool CASE_SENSITIVE = false; #else const bool CASE_SENSITIVE = true; #endif /* * Are we doing synthesis? */ bool synthesis = false; extern void cprop(Design*des); extern void synth(Design*des); extern void synth2(Design*des); extern void syn_rules(Design*des); extern void nodangle(Design*des); typedef void (*net_func)(Design*); static struct net_func_map { const char*name; void (*func)(Design*); } func_table[] = { { "cprop", &cprop }, { "nodangle",&nodangle }, { "synth", &synth }, { "synth2", &synth2 }, { "syn-rules", &syn_rules }, { 0, 0 } }; queue net_func_queue; net_func name_to_net_func(const string&name) { for (unsigned idx = 0 ; func_table[idx].name ; idx += 1) if (name == func_table[idx].name) return func_table[idx].func; return 0; } const char *net_func_to_name(const net_func func) { for (unsigned idx = 0 ; func_table[idx].name ; idx += 1) if (func == func_table[idx].func) return func_table[idx].name; return "This cannot happen"; } static void process_generation_flag(const char*gen) { if (strcmp(gen,"1") == 0) { // FIXME: Deprecated for 1995 generation_flag = GN_VER1995; } else if (strcmp(gen,"2") == 0) { // FIXME: Deprecated for 2001 generation_flag = GN_VER2001; } else if (strcmp(gen,"2x") == 0) { // FIXME: Deprecated for 2001 generation_flag = GN_VER2001; gn_icarus_misc_flag = true; } else if (strcmp(gen,"1995") == 0) { generation_flag = GN_VER1995; } else if (strcmp(gen,"2001") == 0) { generation_flag = GN_VER2001; } else if (strcmp(gen,"2001-noconfig") == 0) { generation_flag = GN_VER2001_NOCONFIG; } else if (strcmp(gen,"2005") == 0) { generation_flag = GN_VER2005; } else if (strcmp(gen,"2005-sv") == 0) { generation_flag = GN_VER2005_SV; } else if (strcmp(gen,"2009") == 0) { generation_flag = GN_VER2009; } else if (strcmp(gen,"2012") == 0) { generation_flag = GN_VER2012; } else if (strcmp(gen,"icarus-misc") == 0) { gn_icarus_misc_flag = true; } else if (strcmp(gen,"no-icarus-misc") == 0) { gn_icarus_misc_flag = false; } else if (strcmp(gen,"xtypes") == 0) { gn_cadence_types_flag = true; } else if (strcmp(gen,"no-xtypes") == 0) { gn_cadence_types_flag = false; } else if (strcmp(gen,"specify") == 0) { gn_specify_blocks_flag = true; } else if (strcmp(gen,"no-specify") == 0) { gn_specify_blocks_flag = false; } else if (strcmp(gen,"assertions") == 0) { gn_assertions_flag = true; } else if (strcmp(gen,"no-assertions") == 0) { gn_assertions_flag = false; } else if (strcmp(gen,"verilog-ams") == 0) { gn_verilog_ams_flag = true; } else if (strcmp(gen,"no-verilog-ams") == 0) { gn_verilog_ams_flag = false; } else if (strcmp(gen,"io-range-error") == 0) { gn_io_range_error_flag = true; } else if (strcmp(gen,"no-io-range-error") == 0) { gn_io_range_error_flag = false; } else if (strcmp(gen,"strict-ca-eval") == 0) { gn_strict_ca_eval_flag = true; } else if (strcmp(gen,"no-strict-ca-eval") == 0) { gn_strict_ca_eval_flag = false; } else if (strcmp(gen,"strict-expr-width") == 0) { gn_strict_expr_width_flag = true; } else if (strcmp(gen,"no-strict-expr-width") == 0) { gn_strict_expr_width_flag = false; } else { } } static void parm_to_flagmap(const string&flag) { string key; const char*value; unsigned off = flag.find('='); if (off > flag.size()) { key = flag; value = strdup(""); } else { key = flag.substr(0, off); value = strdup(flag.substr(off+1).c_str()); } flags[key] = value; } static void find_module_mention(map&check_map, Module*m); static void find_module_mention(map&check_map, PGenerate*s); /* * Convert a string to a time unit or precision. * * Returns true on failure. */ static bool get_ts_const(const char*&cp, int&res, bool is_units) { /* Check for the 1 digit. */ if (*cp != '1') { if (is_units) { cerr << "Error: Invalid +timescale units constant " "(1st digit)." << endl; } else { cerr << "Error: Invalid +timescale precision constant " "(1st digit)." << endl; } return true; } cp += 1; /* Check the number of zeros after the 1. */ res = strspn(cp, "0"); if (res > 2) { if (is_units) { cerr << "Error: Invalid +timescale units constant " "(number of zeros)." << endl; } else { cerr << "Error: Invalid +timescale precision constant " "(number of zeros)." << endl; } return true; } cp += res; /* Now process the scaling string. */ if (strncmp("s", cp, 1) == 0) { res -= 0; cp += 1; return false; } else if (strncmp("ms", cp, 2) == 0) { res -= 3; cp += 2; return false; } else if (strncmp("us", cp, 2) == 0) { res -= 6; cp += 2; return false; } else if (strncmp("ns", cp, 2) == 0) { res -= 9; cp += 2; return false; } else if (strncmp("ps", cp, 2) == 0) { res -= 12; cp += 2; return false; } else if (strncmp("fs", cp, 2) == 0) { res -= 15; cp += 2; return false; } if (is_units) { cerr << "Error: Invalid +timescale units scale." << endl; } else { cerr << "Error: Invalid +timescale precision scale." << endl; } return true; } /* * Process a string with the following form (no space allowed): * * num = < '1' | '10' | '100' > * scale = < 's' | 'ms' | 'us' | 'ns' | 'ps' | 'fs' > * * " '/' * * and set the default time units and precision if successful. * * Return true if we have an error processing the timescale string. */ static bool set_default_timescale(const char*ts_string) { /* Because this came from a command file we can not have embedded * space in this string. */ const char*cp = ts_string; int units = 0; int prec = 0; /* Get the time units. */ if (get_ts_const(cp, units, true)) return true; /* Skip the '/'. */ if (*cp != '/') { cerr << "Error: +timescale separator '/' is missing." << endl; return true; } cp += 1; /* Get the time precision. */ if (get_ts_const(cp, prec, false)) return true; /* The time unit must be greater than or equal to the precision. */ if (units < prec) { cerr << "Error: +timescale unit must not be less than the " "precision." << endl; return true; } /* We have valid units and precision so set the global defaults. */ def_ts_units = units; def_ts_prec = prec; return false; } /* * Read the contents of a config file. This file is a temporary * configuration file made by the compiler driver to carry the bulky * flags generated from the user. This reduces the size of the command * line needed to invoke ivl. * * Each line of the iconfig file has the format: * * : * * The is all the text after the ':' and up to but not * including the end of the line. Thus, white spaces and ':' * characters may appear here. * * The valid keys are: * * -y: * -yl: * -Y: * * -T: * Select which expression to use. * * -t: (obsolete) * Usually, "-t:dll" * * basedir: * Location to look for installed sub-components * * debug: * Activate a class of debug messages. * * depfile: * Give the path to an output dependency file. * * flag:= * Generic compiler flag strings. * * functor: * Append a named functor to the processing path. * * generation:<1|2|2x|xtypes|no-xtypes|specify|no-specify> * This is the generation flag * * ivlpp: * This specifies the ivlpp command line used to process * library modules as I read them in. * * iwidth: * This specifies the width of integer variables. (that is, * variables declared using the "integer" keyword.) * * library_file: * This marks that a source file with the given path is a * library. Any modules in that file are marked as library * modules. * * module: * Load a VPI module. * * out: * Path to the output file. * * sys_func: * Path to a system functions descriptor table * * root: * Specify a root module. There may be multiple of this. * * warnings: * Warning flag letters. */ bool had_timescale = false; static void read_iconfig_file(const char*ipath) { char buf[8*1024]; FILE*ifile = fopen(ipath, "r"); if (ifile == 0) { cerr << "ERROR: Unable to read config file: " << ipath << endl; return; } while (fgets(buf, sizeof buf, ifile) != 0) { if (buf[0] == '#') continue; char*cp = strchr(buf, ':'); if (cp == 0) continue; *cp++ = 0; char*ep = cp + strlen(cp); while (ep > cp) { ep -= 1; switch (*ep) { case '\r': case '\n': case ' ': case '\t': *ep = 0; break; default: ep = cp; } } if (strcmp(buf, "basedir") == 0) { free((void *)basedir); basedir = strdup(cp); } else if (strcmp(buf, "debug") == 0) { if (strcmp(cp, "scopes") == 0) { debug_scopes = true; cerr << "debug: Enable scopes debug" << endl; } else if (strcmp(cp,"eval_tree") == 0) { debug_eval_tree = true; cerr << "debug: Enable eval_tree debug" << endl; } else if (strcmp(cp,"elaborate") == 0) { debug_elaborate = true; cerr << "debug: Enable elaborate debug" << endl; } else if (strcmp(cp,"emit") == 0) { debug_emit = true; cerr << "debug: Enable emit debug" << endl; } else if (strcmp(cp,"synth2") == 0) { debug_synth2 = true; cerr << "debug: Enable synth2 debug" << endl; } else if (strcmp(cp,"optimizer") == 0) { debug_optimizer = true; cerr << "debug: Enable optimizer debug" << endl; } else { } } else if (strcmp(buf, "depmode") == 0) { depfile_mode = *cp; } else if (strcmp(buf, "depfile") == 0) { depfile_name = strdup(cp); } else if (strcmp(buf, "flag") == 0) { string parm = cp; parm_to_flagmap(parm); } else if (strcmp(buf,"functor") == 0) { if (strncmp(cp, "synth", 5) == 0) { synthesis = true; // We are doing synthesis. } net_func tmp = name_to_net_func(cp); if (tmp == 0) { cerr << "No such design transform function ``" << cp << "''." << endl; flag_errors += 1; break; } net_func_queue.push(tmp); } else if (strcmp(buf, "generation") == 0) { process_generation_flag(cp); } else if (strcmp(buf, "ivlpp") == 0) { ivlpp_string = strdup(cp); } else if (strcmp(buf, "iwidth") == 0) { integer_width = strtoul(cp,0,10); } else if (strcmp(buf, "widthcap") == 0) { width_cap = strtoul(cp,0,10); } else if (strcmp(buf, "library_file") == 0) { perm_string path = filename_strings.make(cp); library_file_map[path] = true; } else if (strcmp(buf,"module") == 0) { add_vpi_module(cp); } else if (strcmp(buf, "out") == 0) { free((void *)flags["-o"]); flags["-o"] = strdup(cp); } else if (strcmp(buf, "sys_func") == 0) { load_sys_func_table(cp); } else if (strcmp(buf, "root") == 0) { roots.push_back(lex_strings.make(cp)); } else if (strcmp(buf,"warnings") == 0) { /* Scan the warnings enable string for warning flags. */ for ( ; *cp ; cp += 1) switch (*cp) { case 'i': warn_implicit = true; break; case 'd': warn_implicit_dimensions = true; break; case 'l': warn_inf_loop = true; break; case 's': warn_ob_select = true; break; case 'p': warn_portbinding = true; break; case 't': warn_timescale = true; break; case 'v': warn_sens_entire_vec = true; break; case 'a': warn_sens_entire_arr = true; break; case 'n': warn_anachronisms = true; break; default: break; } } else if (strcmp(buf, "-y") == 0) { build_library_index(cp, CASE_SENSITIVE); } else if (strcmp(buf, "-yl") == 0) { build_library_index(cp, false); } else if (strcmp(buf, "-Y") == 0) { library_suff.push_back(strdup(cp)); } else if (strcmp(buf,"-t") == 0) { // NO LONGER USED } else if (strcmp(buf,"-T") == 0) { if (strcmp(cp,"min") == 0) { min_typ_max_flag = MIN; min_typ_max_warn = 0; } else if (strcmp(cp,"typ") == 0) { min_typ_max_flag = TYP; min_typ_max_warn = 0; } else if (strcmp(cp,"max") == 0) { min_typ_max_flag = MAX; min_typ_max_warn = 0; } else { cerr << "Invalid argument (" << optarg << ") to -T flag." << endl; flag_errors += 1; } } else if (strcmp(buf,"defparam") == 0) { parm_to_defparam_list(cp); } else if (strcmp(buf,"timescale") == 0) { if (had_timescale) { cerr << "Command File: Warning: default timescale " "is being set multiple times." << endl; cerr << " : using the last valid " "+timescale found." << endl; } if (set_default_timescale(cp)) { cerr << " : with +timescale+" << cp << "+" << endl; flag_errors += 1; } else had_timescale = true; } } fclose(ifile); } extern Design* elaborate(list root); #if defined(HAVE_TIMES) static double cycles_diff(struct tms *a, struct tms *b) { clock_t aa = a->tms_utime + a->tms_stime + a->tms_cutime + a->tms_cstime; clock_t bb = b->tms_utime + b->tms_stime + b->tms_cutime + b->tms_cstime; return (aa-bb)/(double)sysconf(_SC_CLK_TCK); } #else // ! defined(HAVE_TIMES) // Provide dummies struct tms { int x; }; inline static void times(struct tms *) { } inline static double cycles_diff(struct tms *, struct tms *) { return 0; } #endif // ! defined(HAVE_TIMES) static void EOC_cleanup(void) { cleanup_sys_func_table(); for (list::iterator suf = library_suff.begin() ; suf != library_suff.end() ; ++ suf ) { free((void *)*suf); } library_suff.clear(); free((void *) basedir); free(ivlpp_string); free(depfile_name); for (map::iterator flg = flags.begin() ; flg != flags.end() ; ++ flg ) { free((void *)flg->second); } flags.clear(); lex_strings.cleanup(); bits_strings.cleanup(); filename_strings.cleanup(); } int main(int argc, char*argv[]) { bool help_flag = false; bool times_flag = false; bool version_flag = false; const char* net_path = 0; const char* pf_path = 0; int opt; struct tms cycles[5]; #if defined(TRAP_SIGINT_FOR_DEBUG) signal(SIGINT, &signals_handler); #endif if( ::getenv("IVL_WAIT_FOR_DEBUGGER") != 0 ) { fprintf( stderr, "Waiting for debugger...\n"); bool debugger_release = false; while( !debugger_release ) { #if defined(__MINGW32__) Sleep(1000); #else sleep(1); #endif } } library_suff.push_back(strdup(".v")); // Start the module list with the base system module. add_vpi_module("system"); add_vpi_module("vhdl_sys"); flags["-o"] = strdup("a.out"); min_typ_max_flag = TYP; min_typ_max_warn = 10; while ((opt = getopt(argc, argv, "C:f:hN:P:p:Vv")) != EOF) switch (opt) { case 'C': read_iconfig_file(optarg); break; case 'f': parm_to_flagmap(optarg); break; case 'h': help_flag = true; break; case 'N': net_path = optarg; break; case 'P': pf_path = optarg; break; case 'p': parm_to_flagmap(optarg); break; case 'v': verbose_flag = true; # if defined(HAVE_TIMES) times_flag = true; # endif flags["VVP_EXTRA_ARGS"] = strdup(" -v"); break; case 'V': version_flag = true; break; default: flag_errors += 1; break; } if (flag_errors) return flag_errors; if (version_flag) { cout << "\nIcarus Verilog Parser/Elaborator version " << VERSION << " (" << VERSION_TAG << ")" << endl << endl; cout << COPYRIGHT << endl << endl; cout << NOTICE << endl; cout << " FLAGS DLL " << flags["DLL"] << endl; dll_target_obj.test_version(flags["DLL"]); return 0; } if (help_flag) { cout << "Icarus Verilog Parser/Elaborator version " << VERSION << " (" << VERSION_TAG << ")" << endl << "usage: ivl \n" "options:\n" "\t-C Config file from driver.\n" "\t-h Print usage information, and exit.\n" "\t-N Dump the elaborated netlist to .\n" "\t-P Write the parsed input to .\n" "\t-p Set a parameter value.\n" "\t-v Print progress indications" #if defined(HAVE_TIMES) " and execution times" #endif ".\n" "\t-V Print version and copyright information, and exit.\n" ; return 0; } if (optind == argc) { cerr << "No input files." << endl; return 1; } if( depfile_name ) { depend_file = fopen(depfile_name, "a"); if(! depend_file) { perror(depfile_name); } } lexor_keyword_mask = 0; switch (generation_flag) { case GN_VER2012: lexor_keyword_mask |= GN_KEYWORDS_1800_2012; case GN_VER2009: lexor_keyword_mask |= GN_KEYWORDS_1800_2009; case GN_VER2005_SV: lexor_keyword_mask |= GN_KEYWORDS_1800_2005; case GN_VER2005: lexor_keyword_mask |= GN_KEYWORDS_1364_2005; case GN_VER2001: lexor_keyword_mask |= GN_KEYWORDS_1364_2001_CONFIG; case GN_VER2001_NOCONFIG: lexor_keyword_mask |= GN_KEYWORDS_1364_2001; case GN_VER1995: lexor_keyword_mask |= GN_KEYWORDS_1364_1995; } if (gn_cadence_types_flag) lexor_keyword_mask |= GN_KEYWORDS_ICARUS; if (gn_verilog_ams_flag) lexor_keyword_mask |= GN_KEYWORDS_VAMS_2_3; if (verbose_flag) { if (times_flag) times(cycles+0); cout << "Using language generation: "; switch (generation_flag) { case GN_VER1995: cout << "IEEE1364-1995"; break; case GN_VER2001_NOCONFIG: cout << "IEEE1364-2001-noconfig"; break; case GN_VER2001: cout << "IEEE1364-2001"; break; case GN_VER2005: cout << "IEEE1364-2005"; break; case GN_VER2005_SV: cout << "IEEE1800-2005"; break; case GN_VER2009: cout << "IEEE1800-2009"; break; case GN_VER2012: cout << "IEEE1800-2012"; break; } if (gn_verilog_ams_flag) cout << ",verilog-ams"; if (gn_specify_blocks_flag) cout << ",specify"; else cout << ",no-specify"; if (gn_cadence_types_flag) cout << ",xtypes"; else cout << ",no-xtypes"; if (gn_icarus_misc_flag) cout << ",icarus-misc"; else cout << ",no-icarus-misc"; cout << endl << "PARSING INPUT" << endl; } const char *flag_tmp = flags["DISABLE_VIRTUAL_PINS"]; if (flag_tmp) disable_virtual_pins = strcmp(flag_tmp,"true")==0; flag_tmp = flags["ARRAY_SIZE_LIMIT"]; if (flag_tmp) array_size_limit = strtoul(flag_tmp,NULL,0); flag_tmp = flags["RECURSIVE_MOD_LIMIT"]; if (flag_tmp) recursive_mod_limit = strtoul(flag_tmp,NULL,0); flag_tmp = flags["DISABLE_CONCATZ_GENERATION"]; if (flag_tmp) disable_concatz_generation = strcmp(flag_tmp,"true")==0; /* Parse the input. Make the pform. */ pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); int rc = pform_parse(argv[optind]); if (pf_path) { ofstream out (pf_path); out << "PFORM DUMP NATURES:" << endl; for (map::iterator cur = natures.begin() ; cur != natures.end() ; ++ cur ) { pform_dump(out, (*cur).second); } out << "PFORM DUMP DISCIPLINES:" << endl; for (map::iterator cur = disciplines.begin() ; cur != disciplines.end() ; ++ cur ) { pform_dump(out, (*cur).second); } out << "PFORM DUMP $ROOT TASKS/FUNCTIONS:" << endl; for (map::iterator cur = pform_tasks.begin() ; cur != pform_tasks.end() ; ++ cur) { pform_dump(out, cur->second); } out << "PFORM DUMP $ROOT CLASSES:" << endl; for (map::iterator cur = pform_classes.begin() ; cur != pform_classes.end() ; ++ cur) { pform_dump(out, cur->second); } out << "PFORM DUMP PACKAGES:" << endl; for (map::iterator pac = pform_packages.begin() ; pac != pform_packages.end() ; ++ pac) { pform_dump(out, pac->second); } out << "PFORM DUMP MODULES:" << endl; for (map::iterator mod = pform_modules.begin() ; mod != pform_modules.end() ; ++ mod ) { pform_dump(out, (*mod).second); } out << "PFORM DUMP PRIMITIVES:" << endl; for (map::iterator idx = pform_primitives.begin() ; idx != pform_primitives.end() ; ++ idx ) { (*idx).second->dump(out); } } if (rc) { return rc; } /* If the user did not give specific module(s) to start with, then look for modules that are not instantiated anywhere. */ if (roots.empty()) { map mentioned_p; map::iterator mod; if (verbose_flag) cout << "LOCATING TOP-LEVEL MODULES" << endl << " "; for (mod = pform_modules.begin() ; mod != pform_modules.end() ; ++ mod ) { find_module_mention(mentioned_p, mod->second); } for (mod = pform_modules.begin() ; mod != pform_modules.end() ; ++ mod ) { /* Don't choose library modules. */ if ((*mod).second->library_flag) continue; /* Don't choose modules instantiated in other modules. */ if (mentioned_p[(*mod).second->mod_name()]) continue; /* What's left might as well be chosen as a root. */ if (verbose_flag) cout << " " << (*mod).second->mod_name(); roots.push_back((*mod).second->mod_name()); } if (verbose_flag) cout << endl; } /* If there is *still* no guess for the root module, then give up completely, and complain. */ if (roots.empty()) { cerr << "No top level modules, and no -s option." << endl; return 1; } if (verbose_flag) { if (times_flag) { times(cycles+1); cerr<<" ... done, " <= GN_VER2005_SV) { def_sfunc_as_task = IVL_SFUNC_AS_TASK_WARNING; } /* On with the process of elaborating the module. */ Design*des = elaborate(roots); if ((des == 0) || (des->errors > 0)) { if (des != 0) { cerr << des->errors << " error(s) during elaboration." << endl; if (net_path) { ofstream out (net_path); des->dump(out); } } else { cerr << "Elaboration failed" << endl; } goto errors_summary; } des->set_flags(flags); switch(min_typ_max_flag) { case MIN: des->set_delay_sel(Design::MIN); break; case TYP: des->set_delay_sel(Design::TYP); break; case MAX: des->set_delay_sel(Design::MAX); break; default: assert(0); } /* Done with all the pform data. Delete the modules. */ for (map::iterator idx = pform_modules.begin() ; idx != pform_modules.end() ; ++ idx ) { delete (*idx).second; (*idx).second = 0; } for(map::iterator it = pform_typedefs.begin() ; it != pform_typedefs.end() ; ++it) { delete (*it).second; (*it).second = 0; } if (verbose_flag) { if (times_flag) { times(cycles+2); cerr<<" ... done, " <join_islands(); if (net_path) { if (verbose_flag) cerr<<" dumping netlist to " <dump(out); } if (des->errors) { cerr << des->errors << " error(s) in post-elaboration processing." << endl; return des->errors; } if (verbose_flag) { if (times_flag) { times(cycles+3); cerr<<" ... done, " <emit(&dll_target_obj)) { if (emit_rc > 0) { cerr << "error: Code generation had " << emit_rc << " error(s)." << endl; return 1; } if (emit_rc < 0) { cerr << "error: Code generator failure: " << emit_rc << endl; return -1; } assert(emit_rc); } if (verbose_flag) { if (times_flag) { times(cycles+4); cerr<<" ... done, " <::const_iterator idx; for (idx = missing_modules.begin() ; idx != missing_modules.end() ; ++ idx ) cerr << " " << (*idx).first << " referenced " << (*idx).second << " times."<< endl; cerr << "***" << endl; } return des? des->errors : 1; } static void find_module_mention(map&check_map, Module*mod) { list gates = mod->get_gates(); list::const_iterator gate; for (gate = gates.begin(); gate != gates.end(); ++ gate ) { PGModule*tmp = dynamic_cast(*gate); if (tmp) { // Note that this module has been instantiated check_map[tmp->get_type()] = true; } } list::const_iterator cur; for (cur = mod->generate_schemes.begin() ; cur != mod->generate_schemes.end() ; ++ cur ) { find_module_mention(check_map, *cur); } } static void find_module_mention(map&check_map, PGenerate*schm) { list::const_iterator gate; for (gate = schm->gates.begin(); gate != schm->gates.end(); ++ gate ) { PGModule*tmp = dynamic_cast(*gate); if (tmp) { // Note that this module has been instantiated check_map[tmp->get_type()] = true; } } list::const_iterator cur; for (cur = schm->generate_schemes.begin() ; cur != schm->generate_schemes.end() ; ++ cur ) { find_module_mention(check_map, *cur); } } iverilog-10_1/mingw.txt000066400000000000000000000003551265551621300152270ustar00rootroot00000000000000Please see the Icarus Verilog Wiki for instruction on building and installing Icarus Verilog as a native Windows application using the MinGW tools: http://iverilog.wikia.com/wiki/Installation_Guide#Compiling_on_MS_Windows_.28MinGW.29 iverilog-10_1/mkinstalldirs000077500000000000000000000066471265551621300161650ustar00rootroot00000000000000#! /bin/sh # mkinstalldirs --- make directory hierarchy scriptversion=2006-05-11.19 # Original author: Noah Friedman # Created: 1993-05-16 # Public domain. # # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' IFS=" "" $nl" errstatus=0 dirmode= usage="\ Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... Create each directory DIR (with mode MODE, if specified), including all leading file name components. Report bugs to ." # process command line arguments while test $# -gt 0 ; do case $1 in -h | --help | --h*) # -h for help echo "$usage" exit $? ;; -m) # -m PERM arg shift test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } dirmode=$1 shift ;; --version) echo "$0 $scriptversion" exit $? ;; --) # stop option processing shift break ;; -*) # unknown option echo "$usage" 1>&2 exit 1 ;; *) # first non-opt arg break ;; esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and # mkdir -p a/c at the same time, both will detect that a is missing, # one will create a, then the other will try to create a and die with # a "File exists" error. This is a problem when calling mkinstalldirs # from a parallel make. We use --version in the probe to restrict # ourselves to GNU mkdir, which is thread-safe. case $dirmode in '') if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" else # On NextStep and OpenStep, the `mkdir' command does not # recognize any option. It will interpret all options as # directories to create, and then abort because `.' already # exists. test -d ./-p && rmdir ./-p test -d ./--version && rmdir ./--version fi ;; *) if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" else # Clean up after NextStep and OpenStep mkdir. for d in ./-m ./-p ./--version "./$dirmode"; do test -d $d && rmdir $d done fi ;; esac for file do case $file in /*) pathcomp=/ ;; *) pathcomp= ;; esac oIFS=$IFS IFS=/ set fnord $file shift IFS=$oIFS for d do test "x$d" = x && continue pathcomp=$pathcomp$d case $pathcomp in -*) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr else if test ! -z "$dirmode"; then echo "chmod $dirmode $pathcomp" lasterr= chmod "$dirmode" "$pathcomp" || lasterr=$? if test ! -z "$lasterr"; then errstatus=$lasterr fi fi fi fi pathcomp=$pathcomp/ done done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: iverilog-10_1/named.h000066400000000000000000000021631265551621300146010ustar00rootroot00000000000000#ifndef IVL_named_H #define IVL_named_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" /* * There are lots of places where names are attached to objects. This * simple template expresses the lot. */ template struct named { perm_string name; T parm; }; #endif /* IVL_named_H */ iverilog-10_1/net_analog.cc000066400000000000000000000024241265551621300157620ustar00rootroot00000000000000/* * Copyright (c) 2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "ivl_assert.h" NetContribution::NetContribution(NetEAccess*l, NetExpr*r) : lval_(l), rval_(r) { } NetContribution::~NetContribution() { } const NetEAccess* NetContribution::lval() const { return lval_; } const NetExpr* NetContribution::rval() const { return rval_; } iverilog-10_1/net_assign.cc000066400000000000000000000202371265551621300160070ustar00rootroot00000000000000/* * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "netlist.h" # include "netclass.h" # include "netdarray.h" # include "netenum.h" # include "ivl_assert.h" /* * NetAssign */ unsigned count_lval_width(const NetAssign_*idx) { unsigned wid = 0; while (idx) { wid += idx->lwidth(); idx = idx->more; } return wid; } NetAssign_::NetAssign_(NetAssign_*n) : nest_(n), sig_(0), word_(0), base_(0), sel_type_(IVL_SEL_OTHER) { more = 0; } NetAssign_::NetAssign_(NetNet*s) : nest_(0), sig_(s), word_(0), base_(0), sel_type_(IVL_SEL_OTHER) { lwid_ = sig_->vector_width(); sig_->incr_lref(); more = 0; turn_sig_to_wire_on_release_ = false; } NetAssign_::~NetAssign_() { if (sig_) { sig_->decr_lref(); if (turn_sig_to_wire_on_release_ && sig_->peek_lref() == 0) sig_->type(NetNet::WIRE); } assert( more == 0 ); delete word_; } string NetAssign_::get_fileline() const { if (sig_) return sig_->get_fileline(); else return nest_->get_fileline(); } NetScope*NetAssign_::scope() const { if (sig_) return sig_->scope(); else return nest_->scope(); } void NetAssign_::set_word(NetExpr*r) { assert(word_ == 0); word_ = r; } NetExpr* NetAssign_::word() { return word_; } const NetExpr* NetAssign_::word() const { return word_; } const NetExpr* NetAssign_::get_base() const { return base_; } ivl_select_type_t NetAssign_::select_type() const { return sel_type_; } unsigned NetAssign_::lwidth() const { // This gets me the type of the l-value expression, down to // the type of the member. If this returns nil, then resort to // the lwid_ value. ivl_type_t ntype = net_type(); if (ntype == 0) return lwid_; // If the type is a darray, and there is a word index, then we // actually want the width of the elements. if (const netdarray_t*darray = dynamic_cast (ntype)) { if (word_ == 0) return 1; else return darray->element_width(); } return ntype->packed_width(); } ivl_variable_type_t NetAssign_::expr_type() const { ivl_type_t ntype = net_type(); if (const netdarray_t*darray = dynamic_cast(ntype)) { if (word_ == 0) return IVL_VT_DARRAY; else return darray->element_base_type(); } if (sig_ && sig_->data_type()==IVL_VT_STRING && base_!=0) return IVL_VT_BOOL; if (ntype) return ntype->base_type(); ivl_assert(*this, sig_); return sig_->data_type(); } const ivl_type_s* NetAssign_::net_type() const { if (nest_) { const ivl_type_s*ntype = nest_->net_type(); if (member_.nil()) return ntype; if (const netclass_t*class_type = dynamic_cast(ntype)) { int pidx = class_type->property_idx_from_name(member_); ivl_assert(*this, pidx >= 0); ivl_type_t tmp = class_type->get_prop_type(pidx); return tmp; } if (const netdarray_t*darray = dynamic_cast (ntype)) { if (word_ == 0) return ntype; else return darray->element_type(); } return 0; } if (const netclass_t*class_type = sig_->class_type()) { if (member_.nil()) return sig_->net_type(); int pidx = class_type->property_idx_from_name(member_); ivl_assert(*sig_, pidx >= 0); ivl_type_t tmp = class_type->get_prop_type(pidx); return tmp; } if (const netdarray_t*darray = dynamic_cast (sig_->net_type())) { if (word_ == 0) return sig_->net_type(); else return darray->element_type(); } return 0; } const netenum_t*NetAssign_::enumeration() const { const netenum_t*tmp = 0; ivl_type_t ntype = net_type(); if (ntype == 0) { ivl_assert(*this, sig_); // If the base signal is not an enumeration, return nil. if ( (tmp = sig_->enumeration()) == 0 ) return 0; } else { tmp = dynamic_cast(ntype); if (tmp == 0) return 0; } // Part select of an enumeration is not an enumeration. if (base_ != 0) return 0; // Concatenation of enumerations is not an enumeration. if (more != 0) return 0; return tmp; } perm_string NetAssign_::name() const { if (sig_) { return sig_->name(); } else { return perm_string::literal(""); } } NetNet* NetAssign_::sig() const { assert(sig_? nest_==0 : nest_!=0); return sig_; } void NetAssign_::set_part(NetExpr*base, unsigned wid, ivl_select_type_t sel_type) { base_ = base; lwid_ = wid; sel_type_ = sel_type; } void NetAssign_::set_property(const perm_string&mname) { //ivl_assert(*sig_, sig_->class_type()); member_ = mname; } /* */ void NetAssign_::turn_sig_to_wire_on_release() { turn_sig_to_wire_on_release_ = true; } NetAssignBase::NetAssignBase(NetAssign_*lv, NetExpr*rv) : lval_(lv), rval_(rv), delay_(0) { } NetAssignBase::~NetAssignBase() { delete rval_; while (lval_) { NetAssign_*tmp = lval_; lval_ = tmp->more; tmp->more = 0; delete tmp; } } NetExpr* NetAssignBase::rval() { return rval_; } const NetExpr* NetAssignBase::rval() const { return rval_; } void NetAssignBase::set_rval(NetExpr*r) { delete rval_; rval_ = r; } NetAssign_* NetAssignBase::l_val(unsigned idx) { NetAssign_*cur = lval_; while (idx > 0) { if (cur == 0) return cur; cur = cur->more; idx -= 1; } assert(idx == 0); return cur; } const NetAssign_* NetAssignBase::l_val(unsigned idx) const { const NetAssign_*cur = lval_; while (idx > 0) { if (cur == 0) return cur; cur = cur->more; idx -= 1; } assert(idx == 0); return cur; } unsigned NetAssignBase::l_val_count() const { const NetAssign_*cur = lval_; unsigned cnt = 0; while (cur) { cnt += 1; cur = cur->more; } return cnt; } unsigned NetAssignBase::lwidth() const { unsigned sum = 0; for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) sum += cur->lwidth(); return sum; } void NetAssignBase::set_delay(NetExpr*expr) { delay_ = expr; } const NetExpr* NetAssignBase::get_delay() const { return delay_; } NetAssign::NetAssign(NetAssign_*lv, NetExpr*rv) : NetAssignBase(lv, rv), op_(0) { } NetAssign::NetAssign(NetAssign_*lv, char op, NetExpr*rv) : NetAssignBase(lv, rv), op_(op) { } NetAssign::~NetAssign() { } NetAssignNB::NetAssignNB(NetAssign_*lv, NetExpr*rv, NetEvWait*ev, NetExpr*cnt) : NetAssignBase(lv, rv) { event_ = ev; count_ = cnt; } NetAssignNB::~NetAssignNB() { } unsigned NetAssignNB::nevents() const { if (event_) return event_->nevents(); return 0; } const NetEvent*NetAssignNB::event(unsigned idx) const { if (event_) return event_->event(idx); return 0; } const NetExpr*NetAssignNB::get_count() const { return count_; } NetCAssign::NetCAssign(NetAssign_*lv, NetExpr*rv) : NetAssignBase(lv, rv) { } NetCAssign::~NetCAssign() { } NetDeassign::NetDeassign(NetAssign_*l) : NetAssignBase(l, 0) { } NetDeassign::~NetDeassign() { } NetForce::NetForce(NetAssign_*lv, NetExpr*rv) : NetAssignBase(lv, rv) { } NetForce::~NetForce() { } NetRelease::NetRelease(NetAssign_*l) : NetAssignBase(l, 0) { } NetRelease::~NetRelease() { } iverilog-10_1/net_design.cc000066400000000000000000000641401265551621300157750ustar00rootroot00000000000000/* * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include /* * This source file contains all the implementations of the Design * class declared in netlist.h. */ # include "netlist.h" # include "util.h" # include "compiler.h" # include "netmisc.h" # include "PExpr.h" # include "PTask.h" # include # include "ivl_assert.h" Design:: Design() : errors(0), nodes_(0), procs_(0), aprocs_(0) { branches_ = 0; procs_idx_ = 0; des_precision_ = 0; nodes_functor_cur_ = 0; nodes_functor_nxt_ = 0; des_delay_sel_ = Design::TYP; } Design::~Design() { } void Design::set_precision(int val) { if (val < des_precision_) des_precision_ = val; } int Design::get_precision() const { return des_precision_; } void Design::set_delay_sel(delay_sel_t sel) { des_delay_sel_ = sel; } const char* Design::get_delay_sel() const { switch (des_delay_sel_) { case Design::MIN: return "MINIMUM"; break; case Design::TYP: return "TYPICAL"; break; case Design::MAX: return "MAXIMUM"; break; default: assert(0); return "TYPICAL"; } } uint64_t Design::scale_to_precision(uint64_t val, const NetScope*scope) const { int units = scope->time_unit(); assert( units >= des_precision_ ); while (units > des_precision_) { units -= 1; val *= 10; } return val; } NetScope* Design::make_root_scope(perm_string root, bool program_block, bool is_interface) { NetScope *root_scope_; root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, false, program_block, is_interface); /* This relies on the fact that the basename return value is permallocated. */ root_scope_->set_module_name(root_scope_->basename()); root_scopes_.push_back(root_scope_); return root_scope_; } NetScope* Design::find_root_scope() { assert(root_scopes_.front()); return root_scopes_.front(); } list Design::find_root_scopes() const { return root_scopes_; } NetScope* Design::make_package_scope(perm_string name) { NetScope*scope; scope = new NetScope(0, hname_t(name), NetScope::PACKAGE, false, false); scope->set_module_name(scope->basename()); packages_[name] = scope; return scope; } void Design::add_class(netclass_t*cl, PClass*pclass) { Definitions::add_class(cl); class_to_pclass_[cl] = pclass; } netclass_t* Design::find_class(perm_string name) const { map::const_iterator cur = classes_.find(name); if (cur != classes_.end()) return cur->second; return 0; } NetScope* Design::find_package(perm_string name) const { map::const_iterator cur = packages_.find(name); if (cur == packages_.end()) return 0; return cur->second; } list Design::find_package_scopes() const { listres; for (map::const_iterator cur = packages_.begin() ; cur != packages_.end() ; ++cur) { res.push_back (cur->second); } return res; } list Design::find_roottask_scopes() const { listres; for (map::const_iterator cur = root_tasks_.begin() ; cur != root_tasks_.end() ; ++ cur) { res.push_back (cur->first); } return res; } /* * This method locates a scope in the design, given its rooted * hierarchical name. Each component of the key is used to scan one * more step down the tree until the name runs out or the search * fails. */ NetScope* Design::find_scope(const std::list&path) const { if (path.empty()) return 0; for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end(); ++ scope ) { NetScope*cur = *scope; if (path.front() != cur->fullname()) continue; std::list tmp = path; tmp.pop_front(); while (cur) { if (tmp.empty()) return cur; cur = cur->child( tmp.front() ); tmp.pop_front(); } } for (map::const_iterator root = root_tasks_.begin() ; root != root_tasks_.end() ; ++ root) { NetScope*cur = root->first; if (path.front() != cur->fullname()) continue; std::list tmp = path; tmp.pop_front(); while (cur) { if (tmp.empty()) return cur; cur = cur->child( tmp.front() ); tmp.pop_front(); } } return 0; } /* * This method locates a scope in the design, given its rooted * hierarchical name. Each component of the key is used to scan one * more step down the tree until the name runs out or the search * fails. */ NetScope* Design::find_scope(const hname_t&path) const { for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end(); ++ scope ) { NetScope*cur = *scope; if (path.peek_name() == cur->basename()) return cur; } return 0; } /* * This is a relative lookup of a scope by name. The starting point is * the scope parameter within which I start looking for the scope. If * I do not find the scope within the passed scope, start looking in * parent scopes until I find it, or I run out of parent scopes. */ NetScope* Design::find_scope(NetScope*scope, const std::list&path, NetScope::TYPE type) const { assert(scope); if (path.empty()) return scope; for ( ; scope ; scope = scope->parent()) { std::list tmp = path; NetScope*cur = scope; do { hname_t key = tmp.front(); /* If we are looking for a module or we are not * looking at the last path component check for * a name match (second line). */ if (cur->type() == NetScope::MODULE && (type == NetScope::MODULE || tmp.size() > 1) && cur->module_name()==key.peek_name()) { /* Up references may match module name */ } else { cur = cur->child( key ); if (cur == 0) break; } tmp.pop_front(); } while (! tmp.empty()); if (cur) return cur; } // Last chance. Look for the name starting at the root. return find_scope(path); } /* * This is a relative lookup of a scope by name. The starting point is * the scope parameter within which I start looking for the scope. If * I do not find the scope within the passed scope, start looking in * parent scopes until I find it, or I run out of parent scopes. */ NetScope* Design::find_scope(NetScope*scope, const hname_t&path, NetScope::TYPE type) const { assert(scope); for ( ; scope ; scope = scope->parent()) { NetScope*cur = scope; /* If we are looking for a module or we are not * looking at the last path component check for * a name match (second line). */ if (cur->type() == NetScope::MODULE && (type == NetScope::MODULE) && cur->module_name()==path.peek_name()) { /* Up references may match module name */ } else { cur = cur->child( path ); } if (cur) return cur; } // Last chance. Look for the name starting at the root. listpath_list; path_list.push_back(path); return find_scope(path_list); } /* * This method runs through the scope, noticing the defparam * statements that were collected during the elaborate_scope pass and * applying them to the target parameters. The implementation actually * works by using a specialized method from the NetScope class that * does all the work for me. */ void Design::run_defparams() { for (list::const_iterator scope = root_scopes_.begin(); scope != root_scopes_.end(); ++ scope ) (*scope)->run_defparams(this); } void NetScope::run_defparams(Design*des) { for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) cur->second->run_defparams(des); while (! defparams.empty()) { pair pp = defparams.front(); defparams.pop_front(); pform_name_t path = pp.first; PExpr*val = pp.second; perm_string perm_name = peek_tail_name(path); path.pop_back(); list eval_path = eval_scope_path(des, this, path); /* If there is no path on the name, then the targ_scope is the current scope. */ NetScope*targ_scope = des->find_scope(this, eval_path); if (targ_scope == 0) { // Push the defparam onto a list for retry // later. It is possible for the scope lookup to // fail if the scope being defparam'd into is // generated by an index array for generate. eval_path.push_back(hname_t(perm_name)); defparams_later.push_back(make_pair(eval_path,val)); continue; } bool flag = targ_scope->replace_parameter(perm_name, val, this); if (! flag) { cerr << val->get_fileline() << ": warning: parameter " << perm_name << " not found in " << scope_path(targ_scope) << "." << endl; } } // If some of the defparams didn't find a scope in the name, // then try again later. It may be that the target scope is // created later by generate scheme or instance array. if (! defparams_later.empty()) des->defparams_later.insert(this); } void NetScope::run_defparams_later(Design*des) { set target_scopes; list,PExpr*> > defparams_even_later; while (! defparams_later.empty()) { pair,PExpr*> cur = defparams_later.front(); defparams_later.pop_front(); listeval_path = cur.first; perm_string name = eval_path.back().peek_name(); eval_path.pop_back(); PExpr*val = cur.second; NetScope*targ_scope = des->find_scope(this, eval_path); if (targ_scope == 0) { // If a scope in the target path is not found, // then push this defparam for handling even // later. Maybe a later generate scheme or // instance array will create the scope. defparams_even_later.push_back(cur); continue; } bool flag = targ_scope->replace_parameter(name, val, this); if (! flag) { cerr << val->get_fileline() << ": warning: parameter " << name << " not found in " << scope_path(targ_scope) << "." << endl; } // We'll need to re-evaluate parameters in this scope target_scopes.insert(targ_scope); } // The scopes that this defparam set touched will be // re-evaluated later it a top_defparams work item. So do not // do the evaluation now. // If there are some scopes that still have missing scopes, // then save them back into the defparams_later list for a // later pass. defparams_later = defparams_even_later; if (! defparams_later.empty()) des->defparams_later.insert(this); } void Design::evaluate_parameters() { for (map::const_iterator cur = packages_.begin() ; cur != packages_.end() ; ++ cur) { cur->second->evaluate_parameters(this); } for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end() ; ++ scope ) { (*scope)->evaluate_parameters(this); } } void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) { long msb = 0; long lsb = 0; bool range_flag = false; /* Evaluate the msb expression, if it is present. */ PExpr*msb_expr = (*cur).second.msb_expr; if (msb_expr) { (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1, true); if (! eval_as_long(msb, (*cur).second.msb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate msb expression " << "for parameter " << (*cur).first << ": " << *(*cur).second.msb << endl; des->errors += 1; return; } range_flag = true; } /* Evaluate the lsb expression, if it is present. */ PExpr*lsb_expr = (*cur).second.lsb_expr; if (lsb_expr) { (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1, true); if (! eval_as_long(lsb, (*cur).second.lsb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate lsb expression " << "for parameter " << (*cur).first << ": " << *(*cur).second.lsb << endl; des->errors += 1; return; } range_flag = true; } /* Evaluate the parameter expression. */ PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; int lv_width = -2; if (range_flag) lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width, true, (*cur).second.is_annotatable, (*cur).second.type); if (! expr) return; switch (expr->expr_type()) { case IVL_VT_REAL: if (! dynamic_cast(expr)) { cerr << expr->get_fileline() << ": error: Unable to evaluate real parameter " << (*cur).first << " value: " << *expr << endl; des->errors += 1; return; } break; case IVL_VT_LOGIC: case IVL_VT_BOOL: if (! dynamic_cast(expr)) { cerr << expr->get_fileline() << ": error: Unable to evaluate parameter " << (*cur).first << " value: " << *expr << endl; des->errors += 1; return; } // If the parameter has type or range information, then // make sure the type is set right. Note that if the // parameter doesn't have an explicit type or range, // then it will get the signedness from the expression itself. if (cur->second.type != IVL_VT_NO_TYPE) { expr->cast_signed(cur->second.signed_flag); } else if (cur->second.signed_flag) { expr->cast_signed(true); } if (!range_flag && !expr->has_width()) { expr = pad_to_width(expr, integer_width, *expr); } break; default: cerr << expr->get_fileline() << ": internal error: " << "Unhandled expression type?" << endl; des->errors += 1; return; } cur->second.val = expr; // If there are no value ranges to test the value against, // then we are done. if ((*cur).second.range == 0) { return; } NetEConst*val = dynamic_cast((*cur).second.val); ivl_assert(*(*cur).second.val, (*cur).second.val); ivl_assert(*(*cur).second.val, val); verinum value = val->value(); bool from_flag = (*cur).second.range == 0? true : false; for (range_t*value_range = (*cur).second.range ; value_range ; value_range = value_range->next) { // If we already know that the value is // within a "from" range, then do not test // any more of the from ranges. if (from_flag && value_range->exclude_flag==false) continue; if (value_range->low_expr) { NetEConst*tmp = dynamic_cast(value_range->low_expr); ivl_assert(*value_range->low_expr, tmp); if (value_range->low_open_flag && value <= tmp->value()) continue; else if (value < tmp->value()) continue; } if (value_range->high_expr) { NetEConst*tmp = dynamic_cast(value_range->high_expr); ivl_assert(*value_range->high_expr, tmp); if (value_range->high_open_flag && value >= tmp->value()) continue; else if (value > tmp->value()) continue; } // Within the range. If this is a "from" // range, then set the from_flag and continue. if (value_range->exclude_flag == false) { from_flag = true; continue; } // OH NO! In an excluded range. signal an error. from_flag = false; break; } // If we found no from range that contains the // value, then report an error. if (! from_flag) { cerr << val->get_fileline() << ": error: " << "Parameter value " << value << " is out of range for parameter " << (*cur).first << "." << endl; des->errors += 1; } } void NetScope::evaluate_parameter_real_(Design*des, param_ref_t cur) { PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1, true, (*cur).second.is_annotatable, (*cur).second.type); if (! expr) return; NetECReal*res = 0; switch (expr->expr_type()) { case IVL_VT_REAL: if (NetECReal*tmp = dynamic_cast(expr)) { res = tmp; } else { cerr << expr->get_fileline() << ": error: " << "Unable to evaluate real parameter " << (*cur).first << " value: " << *expr << endl; des->errors += 1; return; } break; default: cerr << expr->get_fileline() << ": internal error: " << "Failed to cast expression?" << endl; des->errors += 1; return; break; } (*cur).second.val = res; double value = res->value().as_double(); bool from_flag = (*cur).second.range == 0? true : false; for (range_t*value_range = (*cur).second.range ; value_range ; value_range = value_range->next) { if (from_flag && value_range->exclude_flag==false) continue; if (value_range->low_expr) { double tmp; bool flag = eval_as_double(tmp, value_range->low_expr); ivl_assert(*value_range->low_expr, flag); if (value_range->low_open_flag && value <= tmp) continue; else if (value < tmp) continue; } if (value_range->high_expr) { double tmp; bool flag = eval_as_double(tmp, value_range->high_expr); ivl_assert(*value_range->high_expr, flag); if (value_range->high_open_flag && value >= tmp) continue; else if (value > tmp) continue; } if (value_range->exclude_flag == false) { from_flag = true; continue; } // All the above tests failed, so we must have tripped // an exclude rule. from_flag = false; break; } if (! from_flag) { cerr << res->get_fileline() << ": error: " << "Parameter value " << value << " is out of range for real parameter " << (*cur).first << "." << endl; des->errors += 1; } } void NetScope::evaluate_parameter_(Design*des, param_ref_t cur) { // If the parameter has already been evaluated, quietly return. if (cur->second.val_expr == 0) return; if (cur->second.solving) { cerr << cur->second.get_fileline() << ": error: " << "Recursive parameter reference found involving " << cur->first << "." << endl; des->errors += 1; } else { cur->second.solving = true; switch (cur->second.type) { case IVL_VT_NO_TYPE: case IVL_VT_BOOL: case IVL_VT_LOGIC: evaluate_parameter_logic_(des, cur); break; case IVL_VT_REAL: evaluate_parameter_real_(des, cur); break; default: cerr << cur->second.get_fileline() << ": internal error: " << "Unexpected expression type " << cur->second.type << "." << endl; cerr << cur->second.get_fileline() << ": : " << "Parameter name: " << cur->first << endl; cerr << cur->second.get_fileline() << ": : " << "Expression is: " << *cur->second.val_expr << endl; ivl_assert(cur->second, 0); break; } cur->second.solving = false; } // If we have failed to evaluate the expression, create a dummy // value. This prevents spurious error messages being output. if (cur->second.val == 0) { verinum val(verinum::Vx); cur->second.val = new NetEConst(val); } // Flag that the expression has been evaluated. cur->second.val_expr = 0; } void NetScope::evaluate_parameters(Design*des) { for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) cur->second->evaluate_parameters(des); if (debug_scopes) cerr << "debug: " << "Evaluating parameters in " << scope_path(this) << endl; for (param_ref_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur) { evaluate_parameter_(des, cur); } } void Design::residual_defparams() { for (list::const_iterator scope = root_scopes_.begin(); scope != root_scopes_.end(); ++ scope ) (*scope)->residual_defparams(this); } void NetScope::residual_defparams(Design*des) { // Clean out the list of defparams that never managed to match // a scope. Print a warning for each. while (! defparams_later.empty()) { pair,PExpr*> cur = defparams_later.front(); defparams_later.pop_front(); cerr << cur.second->get_fileline() << ": warning: " << "Scope of " << cur.first << " not found." << endl; } for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) cur->second->residual_defparams(des); } const char* Design::get_flag(const string&key) const { map::const_iterator tmp = flags_.find(key); if (tmp == flags_.end()) return ""; else return (*tmp).second; } /* * This method looks for a signal (reg, wire, whatever) starting at * the specified scope. If the name is hierarchical, it is split into * scope and name and the scope used to find the proper starting point * for the real search. * * It is the job of this function to properly implement Verilog scope * rules as signals are concerned. */ NetNet* Design::find_signal(NetScope*scope, pform_name_t path) { assert(scope); perm_string key = peek_tail_name(path); path.pop_back(); if (! path.empty()) { list eval_path = eval_scope_path(this, scope, path); scope = find_scope(scope, eval_path); } while (scope) { if (NetNet*net = scope->find_signal(key)) return net; if (scope->type() == NetScope::MODULE) break; scope = scope->parent(); } return 0; } NetFuncDef* Design::find_function(NetScope*scope, const pform_name_t&name) { assert(scope); std::list eval_path = eval_scope_path(this, scope, name); NetScope*func = find_scope(scope, eval_path, NetScope::FUNC); if (func && (func->type() == NetScope::FUNC)) { // If a function is used in a parameter definition or in // a signal declaration, it is possible to get here before // the function's signals have been elaborated. If this is // the case, elaborate them now. if (func->elab_stage() < 2) { func->need_const_func(true); const PFunction*pfunc = func->func_pform(); assert(pfunc); pfunc->elaborate_sig(this, func); } return func->func_def(); } return 0; } NetScope* Design::find_task(NetScope*scope, const pform_name_t&name) { std::list eval_path = eval_scope_path(this, scope, name); NetScope*task = find_scope(scope, eval_path, NetScope::TASK); if (task && (task->type() == NetScope::TASK)) return task; return 0; } void Design::add_root_task(NetScope*tscope, PTaskFunc*tf) { root_tasks_[tscope] = tf; } void Design::add_node(NetNode*net) { assert(net->design_ == 0); if (nodes_ == 0) { net->node_next_ = net; net->node_prev_ = net; } else { net->node_next_ = nodes_->node_next_; net->node_prev_ = nodes_; net->node_next_->node_prev_ = net; net->node_prev_->node_next_ = net; } nodes_ = net; net->design_ = this; } void Design::del_node(NetNode*net) { assert(net->design_ == this); assert(net != 0); /* Interact with the Design::functor method by manipulating the cur and nxt pointers that it is using. */ if (net == nodes_functor_nxt_) nodes_functor_nxt_ = nodes_functor_nxt_->node_next_; if (net == nodes_functor_nxt_) nodes_functor_nxt_ = 0; if (net == nodes_functor_cur_) nodes_functor_cur_ = 0; /* Now perform the actual delete. */ if (nodes_ == net) nodes_ = net->node_prev_; if (nodes_ == net) { nodes_ = 0; } else { net->node_next_->node_prev_ = net->node_prev_; net->node_prev_->node_next_ = net->node_next_; } net->design_ = 0; } void Design::add_branch(NetBranch*bra) { bra->next_ = branches_; branches_ = bra; } void Design::add_process(NetProcTop*pro) { pro->next_ = procs_; procs_ = pro; } void Design::add_process(NetAnalogTop*pro) { pro->next_ = aprocs_; aprocs_ = pro; } void Design::delete_process(NetProcTop*top) { assert(top); if (procs_ == top) { procs_ = top->next_; } else { NetProcTop*cur = procs_; while (cur->next_ != top) { assert(cur->next_); cur = cur->next_; } cur->next_ = top->next_; } if (procs_idx_ == top) procs_idx_ = top->next_; delete top; } void Design::join_islands(void) { if (nodes_ == 0) return; NetNode*cur = nodes_->node_next_; do { join_island(cur); cur = cur->node_next_; } while (cur != nodes_->node_next_); } iverilog-10_1/net_event.cc000066400000000000000000000236031265551621300156440ustar00rootroot00000000000000/* * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "netlist.h" # include "ivl_assert.h" /* * NOTE: The name_ is perm-allocated by the caller. */ NetEvent::NetEvent(perm_string n) : name_(n) { scope_ = 0; snext_ = 0; probes_ = 0; trig_ = 0; waitref_ = 0; exprref_ = 0; wlist_ = 0; } NetEvent::~NetEvent() { assert(waitref_ == 0); if (scope_) scope_->rem_event(this); while (probes_) { NetEvProbe*tmp = probes_->enext_; delete probes_; probes_ = tmp; } /* name_ is lex_strings. */ } perm_string NetEvent::name() const { return name_; } NetScope* NetEvent::scope() { assert(scope_); return scope_; } const NetScope* NetEvent::scope() const { assert(scope_); return scope_; } unsigned NetEvent::nprobe() const { unsigned cnt = 0; NetEvProbe*cur = probes_; while (cur) { cnt += 1; cur = cur->enext_; } return cnt; } NetEvProbe* NetEvent::probe(unsigned idx) { NetEvProbe*cur = probes_; while (cur && idx) { cur = cur->enext_; idx -= 1; } return cur; } const NetEvProbe* NetEvent::probe(unsigned idx) const { NetEvProbe*cur = probes_; while (cur && idx) { cur = cur->enext_; idx -= 1; } return cur; } unsigned NetEvent::ntrig() const { unsigned cnt = 0; NetEvTrig*cur = trig_; while (cur) { cnt += 1; cur = cur->enext_; } return cnt; } unsigned NetEvent::nwait() const { return waitref_; } unsigned NetEvent::nexpr() const { return exprref_; } /* * A "similar" event is one that has an identical non-nil set of * probes. */ void NetEvent::find_similar_event(list&event_list) { if (probes_ == 0) return; set candidate_events; /* First, get a list of all the NetEvProbes that are connected to my first probe. Then use that to create a set of candidate events. These candidate events are a superset of the similar events, so I will be culling this list later. */ listfirst_probes; probes_->find_similar_probes(first_probes); for (list::iterator idx = first_probes.begin() ; idx != first_probes.end() ; ++ idx ) { candidate_events.insert( (*idx)->event() ); } if (candidate_events.empty()) return; /* Now scan the remaining probes, in each case checking that the probe event is a candidate event. After each iteration, events that don't have a similar probe will be removed from the candidate_events set. If the candidate_events set becomes empty, then give up. */ unsigned probe_count = 1; for (NetEvProbe*cur = probes_->enext_ ; cur; cur = cur->enext_) { listsimilar_probes; cur->find_similar_probes(similar_probes); set candidate_tmp; for (list::iterator idx = similar_probes.begin() ; idx != similar_probes.end() ; ++ idx ) { NetEvent*tmp = (*idx)->event(); if (candidate_events.find(tmp) != candidate_events.end()) candidate_tmp .insert(tmp); } // None of the candidate events match this probe? Give up! if (candidate_tmp.empty()) return; candidate_events = candidate_tmp; probe_count += 1; } /* Scan the surviving candidate events. We know that they all have probes that match the current event's probes. Check for remaining compatibility details and save the survivors in the event_list that the caller passed. */ for (set::iterator idx = candidate_events.begin() ; idx != candidate_events.end() ; ++ idx ) { NetEvent*tmp = *idx; // This shouldn't be possible? if (tmp == this) continue; /* For automatic tasks, the VVP runtime holds state for events in the automatically allocated context. This means we can't merge similar events in different automatic tasks. */ if (scope()->is_auto() && (tmp->scope() != scope())) continue; unsigned tcnt = 0; for (NetEvProbe*cur = tmp->probes_ ; cur ; cur = cur->enext_) tcnt += 1; if (tcnt == probe_count) event_list .push_back(tmp); } } void NetEvent::replace_event(NetEvent*that) { while (wlist_) { wlist_->obj->replace_event(this, that); } } NexusSet* NetEvent::nex_async_() { /* If there are behavioral trigger statements attached to me, then this is not an asynchronous event. */ if (trig_ != 0) return 0; NexusSet*tmp = new NexusSet; for (NetEvProbe*cur = probes_ ; cur != 0 ; cur = cur->enext_) { if (cur->edge() != NetEvProbe::ANYEDGE) { delete tmp; return 0; } for (unsigned idx = 0 ; idx < cur->pin_count() ; idx += 1) { Nexus*nex = cur->pin(idx).nexus(); tmp->add(nex, 0, nex->vector_width()); } } return tmp; } NetEvTrig::NetEvTrig(NetEvent*ev) : event_(ev) { enext_ = event_->trig_; event_->trig_ = this; } NetEvTrig::~NetEvTrig() { if (event_->trig_ == this) { event_->trig_ = enext_; } else { NetEvTrig*cur = event_->trig_; while (cur->enext_ != this) { assert(cur->enext_); cur = cur->enext_; } cur->enext_ = this->enext_; } } const NetEvent* NetEvTrig::event() const { return event_; } NetEvProbe::NetEvProbe(NetScope*s, perm_string n, NetEvent*tgt, edge_t t, unsigned p) : NetNode(s, n, p), event_(tgt), edge_(t) { for (unsigned idx = 0 ; idx < p ; idx += 1) { pin(idx).set_dir(Link::INPUT); } enext_ = event_->probes_; event_->probes_ = this; } NetEvProbe::~NetEvProbe() { if (event_->probes_ == this) { event_->probes_ = enext_; } else { NetEvProbe*cur = event_->probes_; while (cur->enext_ != this) { assert(cur->enext_); cur = cur->enext_; } cur->enext_ = this->enext_; } } NetEvProbe::edge_t NetEvProbe::edge() const { return edge_; } NetEvent* NetEvProbe::event() { return event_; } const NetEvent* NetEvProbe::event() const { return event_; } /* * A similar NetEvProbe is one that is connected to all the same nexa * that this probe is connected to, and also is the same edge * type. Don't count myself as a similar probe. */ void NetEvProbe::find_similar_probes(list&plist) { Nexus*nex = pin(0).nexus(); for (Link*lcur = nex->first_nlink(); lcur; lcur = lcur->next_nlink()) { NetPins*obj = lcur->get_obj(); // Skip NexusSet objects if (obj == 0) continue; if (obj->pin_count() != pin_count()) continue; NetEvProbe*tmp = dynamic_cast(obj); if (tmp == 0) continue; if (tmp == this) continue; if (edge() != tmp->edge()) continue; bool ok_flag = true; for (unsigned idx = 1 ; ok_flag && idx < pin_count() ; idx += 1) if (! pin(idx).is_linked(tmp->pin(idx))) ok_flag = false; if (ok_flag == true) plist .push_back(tmp); } } NetEvWait::NetEvWait(NetProc*pr) : statement_(pr) { } NetEvWait::~NetEvWait() { if (! events_.empty()) { for (unsigned idx = 0 ; idx < events_.size() ; idx += 1) { NetEvent*tgt = events_[idx]; tgt->waitref_ -= 1; struct NetEvent::wcell_*tmp = tgt->wlist_; if (tmp->obj == this) { tgt->wlist_ = tmp->next; delete tmp; } else { assert(tmp->next); while (tmp->next->obj != this) { tmp = tmp->next; assert(tmp->next); } tmp->next = tmp->next->next; delete tmp; } } events_.clear(); } delete statement_; } void NetEvWait::add_event(NetEvent*tgt) { /* A wait fork is an empty event. */ if (! tgt) { assert(events_.empty()); events_.push_back(0); return; } events_.push_back(tgt); // Remember to tell the NetEvent that there is someone // pointing to it. tgt->waitref_ += 1; struct NetEvent::wcell_*tmp = new NetEvent::wcell_; tmp->obj = this; tmp->next = tgt->wlist_; tgt->wlist_ = tmp; } void NetEvWait::replace_event(NetEvent*src, NetEvent*repl) { unsigned idx; for (idx = 0 ; idx < events_.size() ; idx += 1) { if (events_[idx] == src) break; } assert(idx < events_.size()); // First, remove me from the list held by the src NetEvent. assert(src->waitref_ > 0); src->waitref_ -= 1; struct NetEvent::wcell_*tmp = src->wlist_; if (tmp->obj == this) { src->wlist_ = tmp->next; delete tmp; } else { assert(tmp->next); while (tmp->next->obj != this) { tmp = tmp->next; assert(tmp->next); } tmp->next = tmp->next->next; delete tmp; } // Replace the src pointer with the repl pointer. events_[idx] = repl; // Remember to tell the replacement NetEvent that there is // someone pointing to it. repl->waitref_ += 1; tmp = new NetEvent::wcell_; tmp->obj = this; tmp->next = repl->wlist_; repl->wlist_ = tmp; } NetProc* NetEvWait::statement() { return statement_; } iverilog-10_1/net_expr.cc000066400000000000000000000262771265551621300155130ustar00rootroot00000000000000/* * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "netlist.h" # include "netenum.h" # include "netclass.h" # include "netdarray.h" # include "compiler.h" # include "netmisc.h" # include # include "ivl_assert.h" NetExpr::NetExpr(unsigned w) : net_type_(0), width_(w), signed_flag_(false) { } NetExpr::NetExpr(ivl_type_t t) : net_type_(t), width_(0), signed_flag_(false) { } NetExpr::~NetExpr() { } ivl_type_t NetExpr::net_type() const { return net_type_; } void NetExpr::cast_signed(bool flag) { cast_signed_base_(flag); } bool NetExpr::has_width() const { return true; } /* * the grand default data type is a logic vector. */ ivl_variable_type_t NetExpr::expr_type() const { if (net_type_) return net_type_->base_type(); else return IVL_VT_LOGIC; } const netenum_t*NetExpr::enumeration() const { return 0; } NetEArrayPattern::NetEArrayPattern(ivl_type_t lv_type, vector&items) : NetExpr(lv_type), items_(items) { } NetEArrayPattern::~NetEArrayPattern() { for (size_t idx = 0 ; idx < items_.size() ; idx += 1) delete items_[idx]; } /* * Create an add/sub node from the two operands. */ NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBAdd::~NetEBAdd() { } ivl_variable_type_t NetEBAdd::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; if (right_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; return IVL_VT_LOGIC; } /* * Create a comparison operator with two sub-expressions. */ NetEBComp::NetEBComp(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r, 1, false) { } NetEBComp::~NetEBComp() { } bool NetEBComp::has_width() const { return true; } ivl_variable_type_t NetEBComp::expr_type() const { // Case compare always returns BOOL if (op() == 'E' || op() == 'N') return IVL_VT_BOOL; if (left()->expr_type() == IVL_VT_LOGIC) return IVL_VT_LOGIC; if (right()->expr_type() == IVL_VT_LOGIC) return IVL_VT_LOGIC; return IVL_VT_BOOL; } NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBDiv::~NetEBDiv() { } ivl_variable_type_t NetEBDiv::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; if (right_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; return IVL_VT_LOGIC; } NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBMinMax::~NetEBMinMax() { } ivl_variable_type_t NetEBMinMax::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; if (right_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; return IVL_VT_LOGIC; } NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBMult::~NetEBMult() { } ivl_variable_type_t NetEBMult::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; if (right_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; return IVL_VT_LOGIC; } NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBPow::~NetEBPow() { } ivl_variable_type_t NetEBPow::expr_type() const { if (right_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; if (left_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; return IVL_VT_LOGIC; } NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBShift::~NetEBShift() { } bool NetEBShift::has_width() const { return left_->has_width(); } NetEConcat::NetEConcat(unsigned cnt, unsigned r, ivl_variable_type_t vt) : parms_(cnt), repeat_(r), expr_type_(vt) { expr_width(0); } NetEConcat::~NetEConcat() { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; } ivl_variable_type_t NetEConcat::expr_type() const { return expr_type_; } bool NetEConcat::has_width() const { return true; } void NetEConcat::set(unsigned idx, NetExpr*e) { assert(idx < parms_.size()); assert(parms_[idx] == 0); parms_[idx] = e; expr_width( expr_width() + repeat_ * e->expr_width() ); } NetEConstEnum::NetEConstEnum(Definitions*s, perm_string n, const netenum_t*eset, const verinum&v) : NetEConst(v), scope_(s), enum_set_(eset), name_(n) { assert(has_width()); } NetEConstEnum::~NetEConstEnum() { } const netenum_t*NetEConstEnum::enumeration() const { return enum_set_; } NetECReal::NetECReal(const verireal&val) : value_(val) { expr_width(1); cast_signed_base_(true); } NetECReal::~NetECReal() { } const verireal& NetECReal::value() const { return value_; } bool NetECReal::has_width() const { return true; } ivl_variable_type_t NetECReal::expr_type() const { return IVL_VT_REAL; } NetECRealParam::NetECRealParam(NetScope*s, perm_string n, const verireal&v) : NetECReal(v), scope_(s), name_(n) { } NetECRealParam::~NetECRealParam() { } perm_string NetECRealParam::name() const { return name_; } const NetScope* NetECRealParam::scope() const { return scope_; } NetELast::NetELast(NetNet*s) : sig_(s) { } NetELast::~NetELast() { } ivl_variable_type_t NetELast::expr_type() const { return IVL_VT_BOOL; } NetENetenum::NetENetenum(const netenum_t*s) : netenum_(s) { } NetENetenum::~NetENetenum() { } const netenum_t* NetENetenum::netenum() const { return netenum_; } NetENew::NetENew(ivl_type_t t) : obj_type_(t), size_(0), init_val_(0) { } NetENew::NetENew(ivl_type_t t, NetExpr*size, NetExpr*init_val) : obj_type_(t), size_(size), init_val_(init_val) { } NetENew::~NetENew() { } ivl_variable_type_t NetENew::expr_type() const { return size_ ? IVL_VT_DARRAY : IVL_VT_CLASS; } NetENull::NetENull() { } NetENull::~NetENull() { } NetEProperty::NetEProperty(NetNet*net, perm_string pnam, NetExpr*idx) : net_(net), index_(idx) { const netclass_t*use_type = dynamic_cast(net->net_type()); assert(use_type); pidx_ = use_type->property_idx_from_name(pnam); ivl_type_t prop_type = use_type->get_prop_type(pidx_); expr_width(prop_type->packed_width()); cast_signed(prop_type->get_signed()); } NetEProperty::~NetEProperty() { } ivl_variable_type_t NetEProperty::expr_type() const { const netclass_t*use_type = dynamic_cast(net_->net_type()); assert(use_type); ivl_type_t prop_type = use_type->get_prop_type(pidx_); return prop_type->base_type(); } NetESelect::NetESelect(NetExpr*exp, NetExpr*base, unsigned wid, ivl_select_type_t sel_type) : expr_(exp), base_(base), sel_type_(sel_type) { expr_width(wid); } NetESelect::~NetESelect() { delete expr_; delete base_; } const NetExpr*NetESelect::sub_expr() const { return expr_; } const NetExpr*NetESelect::select() const { return base_; } ivl_select_type_t NetESelect::select_type() const { return sel_type_; } ivl_variable_type_t NetESelect::expr_type() const { ivl_variable_type_t type = expr_->expr_type(); // Special case: If the sub-expression is an IVL_VT_STRING, // then this node is representing a character select. The // width is the width of a byte, and the data type is BOOL. if (type == IVL_VT_STRING && expr_width()==8) return IVL_VT_BOOL; if (type != IVL_VT_DARRAY) return type; ivl_assert(*this, type == IVL_VT_DARRAY); // Special case: If the expression is a DARRAY, then the // sub-expression must be a NetESignal and the type of the // NetESelect expression is the element type of the arrayed signal. NetESignal*sig = dynamic_cast(expr_); ivl_assert(*this, sig); const netarray_t*array_type = dynamic_cast (sig->sig()->net_type()); ivl_assert(*this, array_type); return array_type->element_type()->base_type(); } bool NetESelect::has_width() const { return true; } NetESFunc::NetESFunc(const char*n, ivl_variable_type_t t, unsigned width, unsigned np) : name_(0), type_(t), enum_type_(0), parms_(np) { name_ = lex_strings.add(n); expr_width(width); } NetESFunc::NetESFunc(const char*n, ivl_type_t rtype, unsigned np) : NetExpr(rtype), name_(0), type_(IVL_VT_NO_TYPE), enum_type_(0), parms_(np) { name_ = lex_strings.add(n); expr_width(rtype->packed_width()); // FIXME: For now, assume that all uses of this constructor // are for the IVL_VT_DARRAY type. Eventually, the type_ // member will go away. if (dynamic_cast(rtype)) type_ = IVL_VT_DARRAY; else if (dynamic_cast(rtype)) type_ = IVL_VT_CLASS; else ivl_assert(*this, 0); } NetESFunc::NetESFunc(const char*n, const netenum_t*enum_type, unsigned np) : name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np) { name_ = lex_strings.add(n); expr_width(enum_type->packed_width()); } NetESFunc::~NetESFunc() { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) if (parms_[idx]) delete parms_[idx]; /* name_ string ls lex_strings allocated. */ } const char* NetESFunc::name() const { return name_; } unsigned NetESFunc::nparms() const { return parms_.size(); } void NetESFunc::parm(unsigned idx, NetExpr*v) { assert(idx < parms_.size()); if (parms_[idx]) delete parms_[idx]; parms_[idx] = v; } const NetExpr* NetESFunc::parm(unsigned idx) const { assert(idx < parms_.size()); return parms_[idx]; } NetExpr* NetESFunc::parm(unsigned idx) { assert(idx < parms_.size()); return parms_[idx]; } ivl_variable_type_t NetESFunc::expr_type() const { return type_; } const netenum_t* NetESFunc::enumeration() const { return enum_type_; } NetEShallowCopy::NetEShallowCopy(NetExpr*arg1, NetExpr*arg2) : arg1_(arg1), arg2_(arg2) { } NetEShallowCopy::~NetEShallowCopy() { } ivl_variable_type_t NetEShallowCopy::expr_type() const { return arg1_->expr_type(); } NetEAccess::NetEAccess(NetBranch*br, ivl_nature_t nat) : branch_(br), nature_(nat) { } NetEAccess::~NetEAccess() { } ivl_variable_type_t NetEAccess::expr_type() const { return IVL_VT_REAL; } iverilog-10_1/net_func.cc000066400000000000000000000100521265551621300154500ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "netlist.h" # include "compiler.h" # include "PExpr.h" # include /* * To make a NetUserFunc device, make as many pins as there are ports * in the function. Get the port count from the function definition, * which accounts for all the inputs, plus one for the phantom output * that is the result. */ NetUserFunc::NetUserFunc(NetScope*s, perm_string n, NetScope*d, NetEvWait*trigger__) : NetNode(s, n, d->func_def()->port_count()+1), def_(d), trigger_(trigger__) { pin(0).set_dir(Link::OUTPUT); for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) { pin(idx).set_dir(Link::INPUT); pin(idx).drive0(IVL_DR_HiZ); pin(idx).drive1(IVL_DR_HiZ); } } NetUserFunc::~NetUserFunc() { } ivl_variable_type_t NetUserFunc::data_type(unsigned port) const { NetFuncDef*fdef = def_->func_def(); /* Port 0 is the return port. */ if (port == 0) { const NetNet*sig = fdef->return_sig(); assert(sig); return sig->data_type(); } port -= 1; assert(port < fdef->port_count()); const NetNet*port_sig = fdef->port(port); return port_sig->data_type(); } unsigned NetUserFunc::port_width(unsigned port) const { NetFuncDef*fdef = def_->func_def(); /* Port 0 is the return port. */ if (port == 0) { const NetNet*sig = fdef->return_sig(); assert(sig); return sig->vector_width(); } port -= 1; assert(port < fdef->port_count()); const NetNet*port_sig = fdef->port(port); return port_sig->vector_width(); } const NetScope* NetUserFunc::def() const { return def_; } /* * This method of the PECallFunction class checks that the parameters * of the PECallFunction match the function definition. This is used * during elaboration to validate the parameters before using them. */ bool PECallFunction::check_call_matches_definition_(Design*des, NetScope*dscope) const { assert(dscope); /* How many parameters have I got? Normally the size of the list is correct, but there is the special case of a list of 1 nil pointer. This is how the parser tells me of no parameter. In other words, ``func()'' is 1 nil parameter. */ unsigned parms_count = parms_.size(); if ((parms_count == 1) && (parms_[0] == 0)) parms_count = 0; if (dscope->type() != NetScope::FUNC) { cerr << get_fileline() << ": error: Attempt to call scope " << scope_path(dscope) << " as a function." << endl; des->errors += 1; return false; } return true; } NetSysFunc::NetSysFunc(NetScope*s, perm_string n, const struct sfunc_return_type*def, unsigned ports, NetEvWait*trigger__) : NetNode(s, n, ports), def_(def), trigger_(trigger__) { pin(0).set_dir(Link::OUTPUT); // Q for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) { pin(idx).set_dir(Link::INPUT); pin(idx).drive0(IVL_DR_HiZ); pin(idx).drive1(IVL_DR_HiZ); } } NetSysFunc::~NetSysFunc() { } const char*NetSysFunc::func_name() const { return def_->name; } ivl_variable_type_t NetSysFunc::data_type() const { return def_->type; } unsigned NetSysFunc::vector_width() const { return def_->wid; } iverilog-10_1/net_func_eval.cc000066400000000000000000000661131265551621300164700ustar00rootroot00000000000000/* * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netlist.h" # include "netmisc.h" # include "compiler.h" # include # include "ivl_assert.h" using namespace std; /* * We only evaluate one function at a time, so to support the disable * statement, we just need to record the target block and then early * terminate each enclosing block or loop statement until we get back * to the target block. */ static const NetScope*disable = 0; static NetExpr* fix_assign_value(const NetNet*lhs, NetExpr*rhs) { NetEConst*ce = dynamic_cast(rhs); if (ce == 0) return rhs; unsigned lhs_width = lhs->vector_width(); unsigned rhs_width = rhs->expr_width(); if (rhs_width < lhs_width) { rhs = pad_to_width(rhs, lhs_width, *rhs); } else if (rhs_width > lhs_width) { verinum value(ce->value(), lhs_width); ce = new NetEConst(value); ce->set_line(*rhs); delete rhs; rhs = ce; } rhs->cast_signed(lhs->get_signed()); return rhs; } NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vector&args) const { // Make the context map. map::iterator ptr; mapcontext_map; if (debug_eval_tree) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "Evaluate function " << scope()->basename() << endl; } // Put the return value into the map... LocalVar&return_var = context_map[scope()->basename()]; return_var.nwords = 0; return_var.value = 0; // Load the input ports into the map... ivl_assert(loc, port_count() == args.size()); for (size_t idx = 0 ; idx < port_count() ; idx += 1) { const NetNet*pnet = port(idx); perm_string aname = pnet->name(); LocalVar&input_var = context_map[aname]; input_var.nwords = 0; input_var.value = fix_assign_value(pnet, args[idx]); if (debug_eval_tree) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << " input " << aname << " = " << *args[idx] << endl; } } // Ask the scope to collect definitions for local values. This // fills in the context_map with local variables held by the scope. scope()->evaluate_function_find_locals(loc, context_map); if (debug_eval_tree && proc_==0) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "Function " << scope_path(scope()) << " has no statement?" << endl; } // Perform the evaluation. Note that if there were errors // when compiling the function definition, we may not have // a valid statement. bool flag = proc_ && proc_->evaluate_function(loc, context_map); if (debug_eval_tree && !flag) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "Cannot evaluate " << scope_path(scope()) << "." << endl; } // Extract the result... ptr = context_map.find(scope()->basename()); NetExpr*res = ptr->second.value; context_map.erase(ptr); // Cleanup the rest of the context. for (ptr = context_map.begin() ; ptr != context_map.end() ; ++ptr) { unsigned nwords = ptr->second.nwords; if (nwords > 0) { NetExpr**array = ptr->second.array; for (unsigned idx = 0 ; idx < nwords ; idx += 1) { delete array[idx]; } delete [] ptr->second.array; } else { delete ptr->second.value; } } if (disable) { if (debug_eval_tree) cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "disable of " << scope_path(disable) << " trapped in function " << scope_path(scope()) << "." << endl; ivl_assert(loc, disable==scope()); disable = 0; } // Done. if (flag) { if (debug_eval_tree) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "Evaluated to "; if (res) cerr << *res; else cerr << ""; cerr << endl; } return res; } if (debug_eval_tree) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "Evaluation failed." << endl; } delete res; return 0; } void NetScope::evaluate_function_find_locals(const LineInfo&loc, map&context_map) const { for (map::const_iterator cur = signals_map_.begin() ; cur != signals_map_.end() ; ++cur) { const NetNet*tmp = cur->second; // Skip ports, which are handled elsewhere. if (tmp->port_type() != NetNet::NOT_A_PORT) continue; unsigned nwords = 0; if (tmp->unpacked_dimensions() > 0) nwords = tmp->unpacked_count(); LocalVar&local_var = context_map[tmp->name()]; local_var.nwords = nwords; if (nwords > 0) { NetExpr**array = new NetExpr*[nwords]; for (unsigned idx = 0 ; idx < nwords ; idx += 1) { array[idx] = 0; } local_var.array = array; } else { local_var.value = 0; } if (debug_eval_tree) { cerr << loc.get_fileline() << ": debug: " << " (local) " << tmp->name() << (nwords > 0 ? "[]" : "") << endl; } } } NetExpr* NetExpr::evaluate_function(const LineInfo&, map&) const { cerr << get_fileline() << ": sorry: I don't know how to evaluate this expression at compile time." << endl; cerr << get_fileline() << ": : Expression type:" << typeid(*this).name() << endl; return 0; } bool NetProc::evaluate_function(const LineInfo&, map&) const { cerr << get_fileline() << ": sorry: I don't know how to evaluate this statement at compile time." << endl; cerr << get_fileline() << ": : Statement type:" << typeid(*this).name() << endl; return false; } bool NetAssign::eval_func_lval_(const LineInfo&loc, map&context_map, const NetAssign_*lval, NetExpr*rval_result) const { map::iterator ptr = context_map.find(lval->name()); ivl_assert(*this, ptr != context_map.end()); LocalVar*var = & ptr->second; while (var->nwords == -1) { assert(var->ref); var = var->ref; } NetExpr*old_lval; int word = 0; if (var->nwords > 0) { NetExpr*word_result = lval->word()->evaluate_function(loc, context_map); if (word_result == 0) { delete rval_result; return false; } NetEConst*word_const = dynamic_cast(word_result); ivl_assert(loc, word_const); if (!word_const->value().is_defined()) return true; word = word_const->value().as_long(); if (word >= var->nwords) return true; old_lval = var->array[word]; } else { assert(var->nwords == 0); old_lval = var->value; } if (const NetExpr*base_expr = lval->get_base()) { NetExpr*base_result = base_expr->evaluate_function(loc, context_map); if (base_result == 0) { delete rval_result; return false; } NetEConst*base_const = dynamic_cast(base_result); ivl_assert(loc, base_const); long base = base_const->value().as_long(); listprefix (0); base = lval->sig()->sb_to_idx(prefix, base); if (old_lval == 0) old_lval = make_const_x(lval->sig()->vector_width()); ivl_assert(loc, base + lval->lwidth() <= old_lval->expr_width()); NetEConst*lval_const = dynamic_cast(old_lval); verinum lval_v = lval_const->value(); NetEConst*rval_const = dynamic_cast(rval_result); verinum rval_v = cast_to_width(rval_const->value(), lval->lwidth()); for (unsigned idx = 0 ; idx < rval_v.len() ; idx += 1) lval_v.set(idx+base, rval_v[idx]); delete base_result; delete rval_result; rval_result = new NetEConst(lval_v); } else { rval_result = fix_assign_value(lval->sig(), rval_result); } if (old_lval) delete old_lval; if (debug_eval_tree) { cerr << get_fileline() << ": NetAssign::evaluate_function: " << lval->name() << " = " << *rval_result << endl; } if (var->nwords > 0) { var->array[word] = rval_result; } else { assert(var->nwords == 0); var->value = rval_result; } return true; } bool NetAssign::evaluate_function(const LineInfo&loc, map&context_map) const { // Evaluate the r-value expression. NetExpr*rval_result = rval()->evaluate_function(loc, context_map); if (rval_result == 0) return false; // Handle the easy case of a single variable on the LHS. if (l_val_count() == 1) return eval_func_lval_(loc, context_map, l_val(0), rval_result); // If we get here, the LHS must be a concatenation, so we // expect the RHS to be a vector value. NetEConst*rval_const = dynamic_cast(rval_result); ivl_assert(*this, rval_const); verinum rval_full = rval_const->value(); delete rval_result; unsigned base = 0; for (unsigned ldx = 0 ; ldx < l_val_count() ; ldx += 1) { const NetAssign_*lval = l_val(ldx); verinum rval_part(verinum::Vx, lval->lwidth()); for (unsigned idx = 0 ; idx < rval_part.len() ; idx += 1) rval_part.set(idx, rval_full[base+idx]); bool flag = eval_func_lval_(loc, context_map, lval, new NetEConst(rval_part)); if (!flag) return false; base += lval->lwidth(); } return true; } /* * Evaluating a NetBlock in a function is a simple matter of * evaluating the statements in order. */ bool NetBlock::evaluate_function(const LineInfo&loc, map&context_map) const { if (last_ == 0) return true; // If we need to make a local scope, then this context map // will be filled in and used for statements within this block. maplocal_context_map; bool use_local_context_map = false; if (subscope_!=0) { // First, copy the containing scope symbols into the new // scope as references. for (map::iterator cur = context_map.begin() ; cur != context_map.end() ; ++cur) { LocalVar&cur_var = local_context_map[cur->first]; cur_var.nwords = -1; if (cur->second.nwords == -1) cur_var.ref = cur->second.ref; else cur_var.ref = &cur->second; } // Now collect the new locals. subscope_->evaluate_function_find_locals(loc, local_context_map); use_local_context_map = true; } // Now use the local context map if there is any local // context, or the containing context map. map&use_context_map = use_local_context_map? local_context_map : context_map; bool flag = true; NetProc*cur = last_; do { cur = cur->next_; if (debug_eval_tree) { cerr << get_fileline() << ": NetBlock::evaluate_function: " << "Execute statement (" << typeid(*cur).name() << ") at " << cur->get_fileline() << "." << endl; } bool cur_flag = cur->evaluate_function(loc, use_context_map); flag = flag && cur_flag; } while (cur != last_ && !disable); if (debug_eval_tree) { cerr << get_fileline() << ": NetBlock::evaluate_function: " << "subscope_=" << subscope_ << ", disable=" << disable << ", flag=" << (flag?"true":"false") << endl; } if (disable == subscope_) disable = 0; return flag; } bool NetCase::evaluate_function_vect_(const LineInfo&loc, map&context_map) const { NetExpr*case_expr = expr_->evaluate_function(loc, context_map); if (case_expr == 0) return false; NetEConst*case_const = dynamic_cast (case_expr); ivl_assert(loc, case_const); verinum case_val = case_const->value(); delete case_expr; NetProc*default_statement = 0; for (unsigned cnt = 0 ; cnt < items_.size() ; cnt += 1) { const Item*item = &items_[cnt]; if (item->guard == 0) { default_statement = item->statement; continue; } NetExpr*item_expr = item->guard->evaluate_function(loc, context_map); if (item_expr == 0) return false; NetEConst*item_const = dynamic_cast (item_expr); ivl_assert(loc, item_const); verinum item_val = item_const->value(); delete item_expr; ivl_assert(loc, item_val.len() == case_val.len()); bool match = true; for (unsigned idx = 0 ; idx < item_val.len() ; idx += 1) { verinum::V bit_a = case_val.get(idx); verinum::V bit_b = item_val.get(idx); if (bit_a == verinum::Vx && type_ == EQX) continue; if (bit_b == verinum::Vx && type_ == EQX) continue; if (bit_a == verinum::Vz && type_ != EQ) continue; if (bit_b == verinum::Vz && type_ != EQ) continue; if (bit_a != bit_b) { match = false; break; } } if (!match) continue; return item->statement->evaluate_function(loc, context_map); } if (default_statement) return default_statement->evaluate_function(loc, context_map); return true; } bool NetCase::evaluate_function_real_(const LineInfo&loc, map&context_map) const { NetExpr*case_expr = expr_->evaluate_function(loc, context_map); if (case_expr == 0) return false; NetECReal*case_const = dynamic_cast (case_expr); ivl_assert(loc, case_const); double case_val = case_const->value().as_double(); delete case_expr; NetProc*default_statement = 0; for (unsigned cnt = 0 ; cnt < items_.size() ; cnt += 1) { const Item*item = &items_[cnt]; if (item->guard == 0) { default_statement = item->statement; continue; } NetExpr*item_expr = item->guard->evaluate_function(loc, context_map); if (item_expr == 0) return false; NetECReal*item_const = dynamic_cast (item_expr); ivl_assert(loc, item_const); double item_val = item_const->value().as_double(); delete item_expr; if (item_val != case_val) continue; return item->statement->evaluate_function(loc, context_map); } if (default_statement) return default_statement->evaluate_function(loc, context_map); return true; } bool NetCase::evaluate_function(const LineInfo&loc, map&context_map) const { if (expr_->expr_type() == IVL_VT_REAL) return evaluate_function_real_(loc, context_map); else return evaluate_function_vect_(loc, context_map); } bool NetCondit::evaluate_function(const LineInfo&loc, map&context_map) const { NetExpr*cond = expr_->evaluate_function(loc, context_map); if (cond == 0) { if (debug_eval_tree) { cerr << get_fileline() << ": NetCondit::evaluate_function: " << "Unable to evaluate condition (" << *expr_ <<")" << endl; } return false; } NetEConst*cond_const = dynamic_cast (cond); ivl_assert(loc, cond_const); long val = cond_const->value().as_long(); delete cond; bool flag; if (val) // The condition is true, so evaluate the if clause flag = (if_ == 0) || if_->evaluate_function(loc, context_map); else // The condition is false, so evaluate the else clause flag = (else_ == 0) || else_->evaluate_function(loc, context_map); if (debug_eval_tree) { cerr << get_fileline() << ": NetCondit::evaluate_function: " << "Finished, flag=" << (flag?"true":"false") << endl; } return flag; } bool NetDisable::evaluate_function(const LineInfo&, map&) const { disable = target_; if (debug_eval_tree) { cerr << get_fileline() << ": NetDisable::evaluate_function: " << "disable " << scope_path(disable) << endl; } return true; } bool NetDoWhile::evaluate_function(const LineInfo&loc, map&context_map) const { bool flag = true; if (debug_eval_tree) { cerr << get_fileline() << ": NetDoWhile::evaluate_function: " << "Start loop" << endl; } while (!disable) { // Evaluate the statement. flag = proc_->evaluate_function(loc, context_map); if (! flag) break; // Evaluate the condition expression to try and get the // condition for the loop. NetExpr*cond = cond_->evaluate_function(loc, context_map); if (cond == 0) { flag = false; break; } NetEConst*cond_const = dynamic_cast (cond); ivl_assert(loc, cond_const); long val = cond_const->value().as_long(); delete cond; // If the condition is false, then the loop is done. if (val == 0) break; } if (debug_eval_tree) { cerr << get_fileline() << ": NetDoWhile::evaluate_function: " << "Done loop, flag=" << (flag?"true":"false") << endl; } return flag; } bool NetForever::evaluate_function(const LineInfo&loc, map&context_map) const { bool flag = true; if (debug_eval_tree) { cerr << get_fileline() << ": debug: NetForever::evaluate_function: " << "Start loop" << endl; } while (flag && !disable) { flag = flag && statement_->evaluate_function(loc, context_map); } if (debug_eval_tree) { cerr << get_fileline() << ": debug: NetForever::evaluate_function: " << "Done loop" << endl; } return flag; } /* * For now, resort to the block form of the statement until we learn * to do this directly. */ bool NetForLoop::evaluate_function(const LineInfo&loc, map&context_map) const { return as_block_->evaluate_function(loc, context_map); } bool NetRepeat::evaluate_function(const LineInfo&loc, map&context_map) const { bool flag = true; // Evaluate the condition expression to try and get the // condition for the loop. NetExpr*count_expr = expr_->evaluate_function(loc, context_map); if (count_expr == 0) return false; NetEConst*count_const = dynamic_cast (count_expr); ivl_assert(loc, count_const); long count = count_const->value().as_long(); delete count_expr; if (debug_eval_tree) { cerr << get_fileline() << ": debug: NetRepeat::evaluate_function: " << "Repeating " << count << " times." << endl; } while ((count > 0) && flag && !disable) { flag = flag && statement_->evaluate_function(loc, context_map); count -= 1; } if (debug_eval_tree) { cerr << get_fileline() << ": debug: NetRepeat::evaluate_function: " << "Finished loop" << endl; } return flag; } bool NetSTask::evaluate_function(const LineInfo&, map&) const { // system tasks within a constant function are ignored return true; } bool NetWhile::evaluate_function(const LineInfo&loc, map&context_map) const { bool flag = true; if (debug_eval_tree) { cerr << get_fileline() << ": NetWhile::evaluate_function: " << "Start loop" << endl; } while (flag && !disable) { // Evaluate the condition expression to try and get the // condition for the loop. NetExpr*cond = cond_->evaluate_function(loc, context_map); if (cond == 0) { flag = false; break; } NetEConst*cond_const = dynamic_cast (cond); ivl_assert(loc, cond_const); long val = cond_const->value().as_long(); delete cond; // If the condition is false, then break. if (val == 0) break; // The condition is true, so evaluate the statement // another time. bool tmp_flag = proc_->evaluate_function(loc, context_map); if (! tmp_flag) flag = false; } if (debug_eval_tree) { cerr << get_fileline() << ": NetWhile::evaluate_function: " << "Done loop, flag=" << (flag?"true":"false") << endl; } return flag; } NetExpr* NetEBinary::evaluate_function(const LineInfo&loc, map&context_map) const { NetExpr*lval = left_->evaluate_function(loc, context_map); NetExpr*rval = right_->evaluate_function(loc, context_map); if (lval == 0 || rval == 0) { delete lval; delete rval; return 0; } NetExpr*res = eval_arguments_(lval, rval); delete lval; delete rval; return res; } NetExpr* NetEConcat::evaluate_function(const LineInfo&loc, map&context_map) const { vectorvals(parms_.size()); unsigned gap = 0; unsigned valid_vals = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { ivl_assert(*this, parms_[idx]); vals[idx] = parms_[idx]->evaluate_function(loc, context_map); if (vals[idx] == 0) continue; gap += vals[idx]->expr_width(); valid_vals += 1; } NetExpr*res = 0; if (valid_vals == parms_.size()) { res = eval_arguments_(vals, gap); } for (unsigned idx = 0 ; idx < vals.size() ; idx += 1) { delete vals[idx]; } return res; } NetExpr* NetEConst::evaluate_function(const LineInfo&, map&) const { NetEConst*res = new NetEConst(value_); res->set_line(*this); return res; } NetExpr* NetECReal::evaluate_function(const LineInfo&, map&) const { NetECReal*res = new NetECReal(value_); res->set_line(*this); return res; } NetExpr* NetESelect::evaluate_function(const LineInfo&loc, map&context_map) const { NetExpr*sub_exp = expr_->evaluate_function(loc, context_map); ivl_assert(loc, sub_exp); NetEConst*sub_const = dynamic_cast (sub_exp); ivl_assert(loc, sub_exp); verinum sub = sub_const->value(); delete sub_exp; long base = 0; if (base_) { NetExpr*base_val = base_->evaluate_function(loc, context_map); ivl_assert(loc, base_val); NetEConst*base_const = dynamic_cast(base_val); ivl_assert(loc, base_const); base = base_const->value().as_long(); delete base_val; } else { sub.has_sign(has_sign()); sub = pad_to_width(sub, expr_width()); } verinum res (verinum::Vx, expr_width()); for (unsigned idx = 0 ; idx < res.len() ; idx += 1) res.set(idx, sub[base+idx]); NetEConst*res_const = new NetEConst(res); return res_const; } NetExpr* NetESignal::evaluate_function(const LineInfo&loc, map&context_map) const { map::iterator ptr = context_map.find(name()); if (ptr == context_map.end()) { cerr << get_fileline() << ": error: Cannot evaluate " << name() << " in this context." << endl; return 0; } // Follow indirect references to the actual variable. LocalVar*var = & ptr->second; while (var->nwords == -1) { assert(var->ref); var = var->ref; } NetExpr*value = 0; if (var->nwords > 0) { ivl_assert(loc, word_); NetExpr*word_result = word_->evaluate_function(loc, context_map); if (word_result == 0) return 0; NetEConst*word_const = dynamic_cast(word_result); ivl_assert(loc, word_const); int word = word_const->value().as_long(); if (word_const->value().is_defined() && (word < var->nwords)) value = var->array[word]; } else { value = var->value; } if (value == 0) { switch (expr_type()) { case IVL_VT_REAL: return new NetECReal( verireal(0.0) ); case IVL_VT_BOOL: return make_const_0(expr_width()); case IVL_VT_LOGIC: return make_const_x(expr_width()); default: cerr << get_fileline() << ": sorry: I don't know how to initialize " << *this << endl; return 0; } } return value->dup_expr(); } NetExpr* NetETernary::evaluate_function(const LineInfo&loc, map&context_map) const { auto_ptr cval (cond_->evaluate_function(loc, context_map)); switch (const_logical(cval.get())) { case C_0: return false_val_->evaluate_function(loc, context_map); case C_1: return true_val_->evaluate_function(loc, context_map); case C_X: break; default: cerr << get_fileline() << ": error: Condition expression is not constant here." << endl; return 0; } NetExpr*tval = true_val_->evaluate_function(loc, context_map); NetExpr*fval = false_val_->evaluate_function(loc, context_map); NetExpr*res = blended_arguments_(tval, fval); delete tval; delete fval; return res; } NetExpr* NetEUnary::evaluate_function(const LineInfo&loc, map&context_map) const { NetExpr*val = expr_->evaluate_function(loc, context_map); if (val == 0) return 0; NetExpr*res = eval_arguments_(val); delete val; return res; } NetExpr* NetESFunc::evaluate_function(const LineInfo&loc, map&context_map) const { ID id = built_in_id_(); ivl_assert(*this, id != NOT_BUILT_IN); NetExpr*val0 = 0; NetExpr*val1 = 0; NetExpr*res = 0; switch (parms_.size()) { case 1: val0 = parms_[0]->evaluate_function(loc, context_map); if (val0 == 0) break; res = evaluate_one_arg_(id, val0); break; case 2: val0 = parms_[0]->evaluate_function(loc, context_map); val1 = parms_[1]->evaluate_function(loc, context_map); if (val0 == 0 || val1 == 0) break; res = evaluate_two_arg_(id, val0, val1); break; default: ivl_assert(*this, 0); break; } delete val0; delete val1; return res; } NetExpr* NetEUFunc::evaluate_function(const LineInfo&loc, map&context_map) const { NetFuncDef*def = func_->func_def(); ivl_assert(*this, def); vectorargs(parms_.size()); for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) args[idx] = parms_[idx]->evaluate_function(loc, context_map); NetExpr*res = def->evaluate_function(*this, args); return res; } iverilog-10_1/net_link.cc000066400000000000000000000346531265551621300154670ustar00rootroot00000000000000/* * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include "netlist.h" # include # include # include # include # include # include "ivl_alloc.h" void Nexus::connect(Link&r) { Nexus*r_nexus = r.next_? r.find_nexus_() : 0; if (this == r_nexus) return; delete[] name_; name_ = 0; // Special case: This nexus is empty. Simply copy all the // links of the other nexus to this one, and delete the old // nexus. if (list_ == 0) { if (r.next_ == 0) { list_ = &r; r.next_ = &r; r.nexus_ = this; driven_ = NO_GUESS; } else { driven_ = r_nexus->driven_; list_ = r_nexus->list_; list_->nexus_ = this; r_nexus->list_ = 0; delete r_nexus; } return; } // Special case: The Link is unconnected. Put it at the end of // the current list and move the list_ pointer and nexus_ back // pointer to suit. if (r.next_ == 0) { if (r.get_dir() != Link::INPUT) driven_ = NO_GUESS; r.nexus_ = this; r.next_ = list_->next_; list_->next_ = &r; list_->nexus_ = 0; list_ = &r; return; } if (r_nexus->driven_ != Vz) driven_ = NO_GUESS; // Splice the list of links from the "tmp" nexus to the end of // this nexus. Adjust the nexus pointers as needed. Link*save_first = list_->next_; list_->next_ = r_nexus->list_->next_; r_nexus->list_->next_ = save_first; list_->nexus_ = 0; list_ = r_nexus->list_; list_->nexus_ = this; r_nexus->list_ = 0; delete r_nexus; } void connect(Link&l, Link&r) { Nexus*tmp; assert(&l != &r); // If either the l or r link already are part of a Nexus, then // re-use that nexus. Go through some effort so that we are // not gratuitously creating Nexus object. if (l.next_ && (tmp=l.find_nexus_())) { connect(tmp, r); } else if (r.next_ && (tmp=r.find_nexus_())) { connect(tmp, l); } else { // No existing Nexus (both links are so far unconnected) // so start one. tmp = new Nexus(l); tmp->connect(r); } } Link::Link() : dir_(PASSIVE), drive0_(IVL_DR_STRONG), drive1_(IVL_DR_STRONG), next_(0), nexus_(0) { node_ = 0; pin_zero_ = true; } Link::~Link() { if (next_) { Nexus*tmp = nexus(); tmp->unlink(this); if (tmp->list_ == 0) delete tmp; } } Nexus* Link::find_nexus_() const { assert(next_); if (nexus_) return nexus_; for (Link*cur = next_ ; cur != this ; cur = cur->next_) { if (cur->nexus_) return cur->nexus_; } return 0; } Nexus* Link::nexus() { if (next_ == 0) { assert(nexus_ == 0); Nexus*tmp = new Nexus(*this); return tmp; } return find_nexus_(); } const Nexus* Link::nexus() const { if (next_ == 0) return 0; return find_nexus_(); } void Link::set_dir(DIR d) { dir_ = d; } Link::DIR Link::get_dir() const { return dir_; } void Link::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay) { find_nexus_()->drivers_delays(rise, fall, decay); } void Link::drivers_drive(ivl_drive_t drive0__, ivl_drive_t drive1__) { find_nexus_()->drivers_drive(drive0__, drive1__); } void Link::drive0(ivl_drive_t str) { drive0_ = str; } void Link::drive1(ivl_drive_t str) { drive1_ = str; } ivl_drive_t Link::drive0() const { return drive0_; } ivl_drive_t Link::drive1() const { return drive1_; } void Link::cur_link(NetPins*&net, unsigned &pin) { net = get_obj(); pin = get_pin(); } void Link::cur_link(const NetPins*&net, unsigned &pin) const { net = get_obj(); pin = get_pin(); } void Link::unlink() { if (! is_linked()) return; find_nexus_()->unlink(this); } bool Link::is_equal(const Link&that) const { return (get_obj() == that.get_obj()) && (get_pin() == that.get_pin()); } bool Link::is_linked() const { if (next_ == 0) return false; if (next_ == this) return false; return true; } bool Link::is_linked(const Link&that) const { // If this or that link is linked to nothing, then they cannot // be linked to each other. if (! this->is_linked()) return false; if (! that.is_linked()) return false; const Link*cur = next_; while (cur != this) { if (cur == &that) return true; cur = cur->next_; } return false; } Nexus::Nexus(Link&that) { name_ = 0; driven_ = NO_GUESS; t_cookie_ = 0; if (that.next_ == 0) { list_ = &that; that.next_ = &that; that.nexus_ = this; driven_ = NO_GUESS; } else { Nexus*tmp = that.find_nexus_(); list_ = tmp->list_; list_->nexus_ = this; driven_ = tmp->driven_; name_ = tmp->name_; tmp->list_ = 0; tmp->name_ = 0; delete tmp; } } Nexus::~Nexus() { assert(list_ == 0); delete[] name_; } bool Nexus::assign_lval() const { for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { const NetPins*obj; unsigned pin; cur->cur_link(obj, pin); const NetNet*net = dynamic_cast (obj); if (net == 0) continue; if (net->peek_lref() > 0) return true; } return false; } void Nexus::count_io(unsigned&inp, unsigned&out) const { for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { switch (cur->get_dir()) { case Link::INPUT: inp += 1; break; case Link::OUTPUT: out += 1; break; default: break; } } } bool Nexus::drivers_present() const { for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { if (cur->get_dir() == Link::OUTPUT) return true; if (cur->get_dir() == Link::INPUT) continue; // Must be PASSIVE, so if it is some kind of net, see if // it is the sort that might drive the nexus. Note that // supply0/1 and tri0/1 nets are classified as OUTPUT. const NetPins*obj; unsigned pin; cur->cur_link(obj, pin); if (const NetNet*net = dynamic_cast(obj)) switch (net->type()) { case NetNet::WAND: case NetNet::WOR: case NetNet::TRIAND: case NetNet::TRIOR: case NetNet::REG: return true; default: break; } } return false; } void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay) { for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { if (cur->get_dir() != Link::OUTPUT) continue; NetObj*obj = dynamic_cast(cur->get_obj()); if (obj == 0) continue; obj->rise_time(rise); obj->fall_time(fall); obj->decay_time(decay); } } void Nexus::drivers_drive(ivl_drive_t drive0, ivl_drive_t drive1) { for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { if (cur->get_dir() != Link::OUTPUT) continue; cur->drive0(drive0); cur->drive1(drive1); } } void Nexus::unlink(Link*that) { delete[] name_; name_ = 0; assert(that); // Special case: the Link is the only link in the nexus. In // this case, the unlink is trivial. Also clear the Nexus // pointers. if (that->next_ == that) { assert(that->nexus_ == this); assert(list_ == that); list_ = 0; driven_ = NO_GUESS; that->nexus_ = 0; that->next_ = 0; return; } // If the link I'm removing was a driver for this nexus, then // cancel my guess of the driven value. if (that->get_dir() != Link::INPUT) driven_ = NO_GUESS; // Look for the Link that points to "that". We know that there // will be one because the list is a circle. When we find the // prev pointer, then remove that from the list. Link*prev = list_; while (prev->next_ != that) prev = prev->next_; prev->next_ = that->next_; // If "that" was the last item in the list, then change the // list_ pointer to point to the new end of the list. if (list_ == that) { assert(that->nexus_ == this); list_ = prev; list_->nexus_ = this; } that->nexus_ = 0; that->next_ = 0; } Link* Nexus::first_nlink() { if (list_) return list_->next_; else return 0; } const Link* Nexus::first_nlink() const { if (list_) return list_->next_; else return 0; } /* * The t_cookie can be set exactly once. This attaches an ivl_nexus_t * object to the Nexus, and causes the Link list to be marked up for * efficient use by the code generator. The change is to give all the * links a valid nexus_ pointer. This breaks most of the other * methods, but they are not used during code generation. */ void Nexus::t_cookie(ivl_nexus_t val) const { assert(val && !t_cookie_); t_cookie_ = val; for (Link*cur = list_->next_ ; cur->nexus_ == 0 ; cur = cur->next_) cur->nexus_ = const_cast (this); } unsigned Nexus::vector_width() const { for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { const NetNet*sig = dynamic_cast(cur->get_obj()); if (sig == 0) continue; return sig->vector_width(); } return 0; } NetNet* Nexus::pick_any_net() { for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { NetNet*sig = dynamic_cast(cur->get_obj()); if (sig != 0) return sig; } return 0; } const char* Nexus::name() const { if (name_) return name_; const NetNet*sig = 0; unsigned pin = 0; for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { const NetNet*cursig = dynamic_cast(cur->get_obj()); if (cursig == 0) continue; if (sig == 0) { sig = cursig; pin = cur->get_pin(); continue; } if ((cursig->pin_count() == 1) && (sig->pin_count() > 1)) continue; if ((cursig->pin_count() > 1) && (sig->pin_count() == 1)) { sig = cursig; pin = cur->get_pin(); continue; } if (cursig->local_flag() && !sig->local_flag()) continue; if (cursig->name() < sig->name()) continue; sig = cursig; pin = cur->get_pin(); } if (sig == 0) { const Link*lnk = first_nlink(); const NetObj*obj = dynamic_cast(lnk->get_obj()); pin = lnk->get_pin(); cerr << "internal error: No signal for nexus of " << obj->name() << " pin " << pin << " type=" << typeid(*obj).name() << "?" << endl; ostringstream tmp; tmp << "nex=" << this << ends; const string tmps = tmp.str(); name_ = new char[strlen(tmps.c_str()) + 1]; strcpy(name_, tmps.c_str()); } else { assert(sig); ostringstream tmp; tmp << scope_path(sig->scope()) << "." << sig->name(); if (sig->pin_count() > 1) tmp << "<" << pin << ">"; tmp << ends; const string tmps = tmp.str(); name_ = new char[strlen(tmps.c_str()) + 1]; strcpy(name_, tmps.c_str()); } return name_; } NexusSet::NexusSet() { } NexusSet::~NexusSet() { for (size_t idx = 0 ; idx < items_.size() ; idx += 1) delete items_[idx]; } size_t NexusSet::size() const { return items_.size(); } void NexusSet::add(Nexus*that, unsigned base, unsigned wid) { assert(that); elem_t*cur = new elem_t(that, base, wid); if (items_.size() == 0) { items_.resize(1); items_[0] = cur; return; } unsigned ptr = bsearch_(*cur); if (ptr < items_.size()) { delete cur; return; } assert(ptr == items_.size()); items_.push_back(cur); } void NexusSet::add(NexusSet&that) { for (size_t idx = 0 ; idx < that.items_.size() ; idx += 1) add(that.items_[idx]->lnk.nexus(), that.items_[idx]->base, that.items_[idx]->wid); } void NexusSet::rem_(const NexusSet::elem_t*that) { if (items_.empty()) return; unsigned ptr = bsearch_(*that); if (ptr >= items_.size()) return; if (items_.size() == 1) { delete items_[0]; items_.clear(); return; } delete items_[ptr]; for (unsigned idx = ptr ; idx < (items_.size()-1) ; idx += 1) items_[idx] = items_[idx+1]; items_.pop_back(); } void NexusSet::rem(const NexusSet&that) { for (size_t idx = 0 ; idx < that.items_.size() ; idx += 1) rem_(that.items_[idx]); } unsigned NexusSet::find_nexus(const NexusSet::elem_t&that) const { return bsearch_(that); } NexusSet::elem_t& NexusSet::at (unsigned idx) { assert(idx < items_.size()); return *items_[idx]; } size_t NexusSet::bsearch_(const NexusSet::elem_t&that) const { for (unsigned idx = 0 ; idx < items_.size() ; idx += 1) { if (*items_[idx] == that) return idx; } return items_.size(); } bool NexusSet::elem_t::contains(const struct elem_t&that) const { if (! lnk.is_linked(that.lnk)) return false; if (that.base < base) return false; if ((that.base+that.wid) > (base+wid)) return false; return true; } bool NexusSet::contains_(const NexusSet::elem_t&that) const { for (unsigned idx = 0 ; idx < items_.size() ; idx += 1) { if (items_[idx]->contains(that)) return true; } return false; } bool NexusSet::contains(const NexusSet&that) const { for (size_t idx = 0 ; idx < that.items_.size() ; idx += 1) { if (! contains_(*that.items_[idx])) return false; } return true; } bool NexusSet::intersect(const NexusSet&that) const { for (size_t idx = 0 ; idx < that.items_.size() ; idx += 1) { size_t where = bsearch_(*that.items_[idx]); if (where == items_.size()) continue; return true; } return false; } iverilog-10_1/net_modulo.cc000066400000000000000000000041041265551621300160150ustar00rootroot00000000000000/* * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include # include "netlist.h" # include "compiler.h" /* * 0 -- Result * 1 -- DataA * 2 -- DataB */ NetModulo::NetModulo(NetScope*s, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(s, n, 3), width_r_(wr), width_a_(wa), width_b_(wb) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB signed_flag_ = false; } NetModulo::~NetModulo() { } unsigned NetModulo::width_r() const { return width_r_; } unsigned NetModulo::width_a() const { return width_a_; } unsigned NetModulo::width_b() const { return width_b_; } Link& NetModulo::pin_Result() { return pin(0); } void NetModulo::set_signed(bool flag) { signed_flag_ = flag; } bool NetModulo::get_signed() const { return signed_flag_; } const Link& NetModulo::pin_Result() const { return pin(0); } Link& NetModulo::pin_DataA() { return pin(1); } const Link& NetModulo::pin_DataA() const { return pin(1); } Link& NetModulo::pin_DataB() { return pin(2); } const Link& NetModulo::pin_DataB() const { return pin(2); } iverilog-10_1/net_nex_input.cc000066400000000000000000000303621265551621300165340ustar00rootroot00000000000000/* * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include "compiler.h" # include "netlist.h" # include "netmisc.h" NexusSet* NetExpr::nex_input(bool) { cerr << get_fileline() << ": internal error: nex_input not implemented: " << *this << endl; return 0; } NexusSet* NetProc::nex_input(bool) { cerr << get_fileline() << ": internal error: NetProc::nex_input not implemented" << endl; return 0; } NexusSet* NetEArrayPattern::nex_input(bool rem_out) { NexusSet*result = new NexusSet; for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { if (items_[idx]==0) continue; NexusSet*tmp = items_[idx]->nex_input(rem_out); if (tmp == 0) continue; result->add(*tmp); delete tmp; } return result; } NexusSet* NetEBinary::nex_input(bool rem_out) { NexusSet*result = left_->nex_input(rem_out); NexusSet*tmp = right_->nex_input(rem_out); result->add(*tmp); delete tmp; return result; } NexusSet* NetEConcat::nex_input(bool rem_out) { if (parms_[0] == NULL) return NULL; NexusSet*result = parms_[0]->nex_input(rem_out); for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == NULL) { delete result; return NULL; } NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; } return result; } NexusSet* NetEAccess::nex_input(bool) { return new NexusSet; } /* * A constant has not inputs, so always return an empty set. */ NexusSet* NetEConst::nex_input(bool) { return new NexusSet; } NexusSet* NetECReal::nex_input(bool) { return new NexusSet; } NexusSet* NetEEvent::nex_input(bool) { return new NexusSet; } NexusSet* NetELast::nex_input(bool) { return new NexusSet; } NexusSet* NetENetenum::nex_input(bool) { return new NexusSet; } NexusSet* NetENew::nex_input(bool) { return new NexusSet; } NexusSet* NetENull::nex_input(bool) { return new NexusSet; } NexusSet* NetEProperty::nex_input(bool) { return new NexusSet; } NexusSet* NetEScope::nex_input(bool) { return new NexusSet; } NexusSet* NetESelect::nex_input(bool rem_out) { NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet(); NexusSet*tmp = expr_->nex_input(rem_out); if (tmp == NULL) { delete result; return NULL; } result->add(*tmp); delete tmp; /* See the comment for NetESignal below. */ if (base_ && warn_sens_entire_vec) { cerr << get_fileline() << ": warning: @* is sensitive to all " "bits in '" << *expr_ << "'." << endl; } return result; } /* * The $fread, etc. system functions can have NULL arguments. */ NexusSet* NetESFunc::nex_input(bool rem_out) { if (parms_.empty()) return new NexusSet; NexusSet*result; if (parms_[0]) result = parms_[0]->nex_input(rem_out); else result = new NexusSet; for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; } } return result; } NexusSet* NetEShallowCopy::nex_input(bool) { return new NexusSet; } NexusSet* NetESignal::nex_input(bool rem_out) { /* * This is not what I would expect for the various selects (bit, * part, index, array). This code adds all the bits/array words * instead of building the appropriate select and then using it * as the trigger. Other simulators also add everything. */ NexusSet*result = new NexusSet; /* Local signals are not added to the sensitivity list. */ if (net_->local_flag()) return result; /* If we have an array index add it to the sensitivity list. */ if (word_) { NexusSet*tmp; tmp = word_->nex_input(rem_out); result->add(*tmp); delete tmp; if (warn_sens_entire_arr) { cerr << get_fileline() << ": warning: @* is sensitive to all " << net_->unpacked_count() << " words in array '" << name() << "'." << endl; } } for (unsigned idx = 0 ; idx < net_->pin_count() ; idx += 1) result->add(net_->pin(idx).nexus(), 0, net_->vector_width()); return result; } NexusSet* NetETernary::nex_input(bool rem_out) { NexusSet*tmp; NexusSet*result = cond_->nex_input(rem_out); tmp = true_val_->nex_input(rem_out); result->add(*tmp); delete tmp; tmp = false_val_->nex_input(rem_out); result->add(*tmp); delete tmp; return result; } NexusSet* NetEUFunc::nex_input(bool rem_out) { NexusSet*result = new NexusSet; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; } return result; } NexusSet* NetEUnary::nex_input(bool rem_out) { return expr_->nex_input(rem_out); } NexusSet* NetAssign_::nex_input(bool rem_out) { NexusSet*result = new NexusSet; if (word_) { NexusSet*tmp = word_->nex_input(rem_out); result->add(*tmp); delete tmp; } if (base_) { NexusSet*tmp = base_->nex_input(rem_out); result->add(*tmp); delete tmp; } return result; } NexusSet* NetAssignBase::nex_input(bool rem_out) { NexusSet*result = rval_->nex_input(rem_out); /* It is possible that the lval_ can have nex_input values. In particular, index expressions are statement inputs as well, so should be addressed here. */ for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) { NexusSet*tmp = cur->nex_input(rem_out); result->add(*tmp); delete tmp; } return result; } /* * The nex_input of a begin/end block is the NexusSet of bits that the * block reads from outside the block. That means it is the union of * the nex_input for all the substatements. * * The input set for a sequential set is not exactly the union of the * input sets because there is the possibility of intermediate values, * that don't deserve to be in the input set. To wit: * * begin * t = a + b; * c = ~t; * end * * In this example, "t" should not be in the input set because it is * used by the sequence as a temporary value. */ NexusSet* NetBlock::nex_input(bool rem_out) { if (last_ == 0) return new NexusSet; if (type_ != SEQU) { cerr << get_fileline() << ": internal error: Sorry, " << "I don't know how to synthesize fork/join blocks." << endl; return 0; } NetProc*cur = last_->next_; /* This is the accumulated input set. */ NexusSet*result = new NexusSet; /* This is an accumulated output set. */ NexusSet*prev = new NexusSet; do { /* Get the inputs for the current statement. */ NexusSet*tmp = cur->nex_input(rem_out); /* Add the current input set to the accumulated input set. */ if (tmp != 0) result->add(*tmp); delete tmp; /* Add the current outputs to the accumulated output set if * they are going to be removed from the input set below. */ if (rem_out) cur->nex_output(*prev); cur = cur->next_; } while (cur != last_->next_); /* Remove from the input set those bits that are outputs from other statements. They aren't really inputs to the block, just internal intermediate values. */ if (rem_out) result->rem(*prev); delete prev; return result; } /* * The inputs to a case statement are the inputs to the expression, * the inputs to all the guards, and the inputs to all the guarded * statements. */ NexusSet* NetCase::nex_input(bool rem_out) { NexusSet*result = expr_->nex_input(rem_out); if (result == 0) return 0; for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { /* Skip cases that have empty statements. */ if (items_[idx].statement == 0) continue; NexusSet*tmp = items_[idx].statement->nex_input(rem_out); assert(tmp); result->add(*tmp); delete tmp; /* Usually, this is the guard expression. The default case is special and is identified by a null guard. The default guard obviously has no input. */ if (items_[idx].guard) { tmp = items_[idx].guard->nex_input(rem_out); assert(tmp); result->add(*tmp); delete tmp; } } return result; } NexusSet* NetCAssign::nex_input(bool) { cerr << get_fileline() << ": internal warning: NetCAssign::nex_input()" << " not implemented." << endl; return new NexusSet; } NexusSet* NetCondit::nex_input(bool rem_out) { NexusSet*result = expr_->nex_input(rem_out); if (if_ != 0) { NexusSet*tmp = if_->nex_input(rem_out); result->add(*tmp); delete tmp; } if (else_ != 0) { NexusSet*tmp = else_->nex_input(rem_out); result->add(*tmp); delete tmp; } return result; } NexusSet* NetDoWhile::nex_input(bool rem_out) { NexusSet*result = proc_->nex_input(rem_out); NexusSet*tmp = cond_->nex_input(rem_out); result->add(*tmp); delete tmp; return result; } NexusSet* NetForce::nex_input(bool) { cerr << get_fileline() << ": internal warning: NetForce::nex_input()" << " not implemented." << endl; return new NexusSet; } NexusSet* NetForLoop::nex_input(bool rem_out) { NexusSet*result = init_expr_->nex_input(rem_out); NexusSet*tmp = condition_->nex_input(rem_out); result->add(*tmp); delete tmp; tmp = statement_->nex_input(rem_out); result->add(*tmp); delete tmp; tmp = step_statement_->nex_input(rem_out); result->add(*tmp); delete tmp; return result; } NexusSet* NetForever::nex_input(bool rem_out) { NexusSet*result = statement_->nex_input(rem_out); return result; } /* * The NetPDelay statement is a statement of the form * * # * * The nex_input set is the input set of the . Do *not* * include the input set of the because it does not affect the * result. The statement can be omitted. */ NexusSet* NetPDelay::nex_input(bool rem_out) { if (statement_ == 0) return 0; NexusSet*result = statement_->nex_input(rem_out); return result; } NexusSet* NetRepeat::nex_input(bool rem_out) { NexusSet*result = statement_->nex_input(rem_out); NexusSet*tmp = expr_->nex_input(rem_out); result->add(*tmp); delete tmp; return result; } /* * The $display, etc. system tasks can have NULL arguments. */ NexusSet* NetSTask::nex_input(bool rem_out) { if (parms_.empty()) return new NexusSet; NexusSet*result; if (parms_[0]) result = parms_[0]->nex_input(rem_out); else result = new NexusSet; for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; } } return result; } /* * The NetUTask represents a call to a user defined task. There are no * parameters to consider, because the compiler already removed them * and converted them to blocking assignments. */ NexusSet* NetUTask::nex_input(bool) { return new NexusSet; } NexusSet* NetWhile::nex_input(bool rem_out) { NexusSet*result = proc_->nex_input(rem_out); NexusSet*tmp = cond_->nex_input(rem_out); result->add(*tmp); delete tmp; return result; } iverilog-10_1/net_nex_output.cc000066400000000000000000000104021265551621300167260ustar00rootroot00000000000000/* * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include "netlist.h" # include "netmisc.h" void NetProc::nex_output(NexusSet&) { cerr << get_fileline() << ": internal error: NetProc::nex_output not implemented" << endl; cerr << get_fileline() << ": : on object type " << typeid(*this).name() << endl; } void NetAssign_::nex_output(NexusSet&out) { assert(sig_); unsigned use_word = 0; unsigned use_base = 0; unsigned use_wid = lwidth(); if (word_) { long tmp = 0; if (eval_as_long(tmp, word_)) { // A constant word select, so add the selected word. use_word = tmp; } else { // A variable word select. The obvious thing to do // is to add the whole array, but this could cause // NetBlock::nex_input() to overprune the input set. // As array access is not yet handled in synthesis, // I'll leave this as TBD - the output set is not // otherwise used when elaborating an always @* // block. return; } } Nexus*nex = sig_->pin(use_word).nexus(); if (base_) { // Unable to evaluate the bit/part select of // the l-value, so this is a mux. Pretty // sure I don't know how to handle this yet // in synthesis, so punt for now. // Even with constant bit/part select, we want to // return the entire signal as an output. The // context will need to sort out which bits are // actually assigned. use_base = 0; use_wid = nex->vector_width(); } out.add(nex, use_base, use_wid); } /* * Assignments have as output all the bits of the concatenated signals * of the l-value. */ void NetAssignBase::nex_output(NexusSet&out) { for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) { cur->nex_output(out); } } void NetBlock::nex_output(NexusSet&out) { if (last_ == 0) return; NetProc*cur = last_; do { cur = cur->next_; cur->nex_output(out); } while (cur != last_); } void NetCase::nex_output(NexusSet&out) { for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { // Empty statements clearly have no output. if (items_[idx].statement == 0) continue; assert(items_[idx].statement); items_[idx].statement->nex_output(out); } } void NetCondit::nex_output(NexusSet&out) { if (if_ != 0) if_->nex_output(out); if (else_ != 0) else_->nex_output(out); } void NetDoWhile::nex_output(NexusSet&out) { if (proc_ != 0) proc_->nex_output(out); } void NetEvWait::nex_output(NexusSet&out) { assert(statement_); statement_->nex_output(out); } void NetForLoop::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); } void NetPDelay::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); } /* * For the purposes of synthesis, system task calls have no output at * all. This is OK because most system tasks are not synthesizable in * the first place. */ void NetSTask::nex_output(NexusSet&) { } /* * Consider a task call to not have any outputs. This is not quite * right, we should be listing as outputs all the output ports, but for * the purposes that this method is used, this will do for now. */ void NetUTask::nex_output(NexusSet&) { } void NetWhile::nex_output(NexusSet&out) { if (proc_ != 0) proc_->nex_output(out); } iverilog-10_1/net_proc.cc000066400000000000000000000137521265551621300154720ustar00rootroot00000000000000/* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "ivl_assert.h" NetBlock::NetBlock(Type t, NetScope*ss) : type_(t), subscope_(ss), last_(0) { } NetBlock::~NetBlock() { while (last_ != 0) { if (last_->next_ == last_) { delete last_; last_ = 0; } else { NetProc*cur = last_->next_; last_->next_ = cur->next_; cur->next_ = cur; delete cur; } } } void NetBlock::append(NetProc*cur) { if (last_ == 0) { last_ = cur; cur->next_ = cur; } else { cur->next_ = last_->next_; last_->next_ = cur; last_ = cur; } } const NetProc* NetBlock::proc_first() const { if (last_ == 0) return 0; return last_->next_; } const NetProc* NetBlock::proc_next(const NetProc*cur) const { if (cur == last_) return 0; return cur->next_; } NetCase::NetCase(NetCase::TYPE c, NetExpr*ex, unsigned cnt) : type_(c), expr_(ex), items_(cnt) { ivl_assert(*this, expr_); } NetCase::~NetCase() { delete expr_; for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { delete items_[idx].guard; if (items_[idx].statement) delete items_[idx].statement; } } NetCase::TYPE NetCase::type() const { return type_; } void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p) { ivl_assert(*this, idx < items_.size()); items_[idx].guard = e; items_[idx].statement = p; } void NetCase::prune() { // Test whether the case expression has been padded out NetESelect*padded_expr = dynamic_cast(expr_); if ((padded_expr == 0) || (padded_expr->select() != 0)) return; // If so, run through the case item expressions to find // the minimum number of bits needed to unambiguously // select the correct case item. const NetExpr*unpadded_expr = padded_expr->sub_expr(); unsigned padded_width = padded_expr->expr_width(); unsigned prune_width = unpadded_expr->expr_width(); for (unsigned idx = 0; idx < items_.size(); idx += 1) { // If there is no guard expression, this is the default // case, so skip it. if (items_[idx].guard == 0) continue; // If the guard expression is not constant, assume // all bits are needed, so no pruning can be done. NetEConst*gc = dynamic_cast(items_[idx].guard); if (gc == 0) return; unsigned sig_bits = gc->value().significant_bits(); if (sig_bits > prune_width) prune_width = sig_bits; // If all the padding bits are needed, no pruning // can be done. if (prune_width >= padded_width) return; } ivl_assert(*this, prune_width < padded_width); if (debug_elaborate) { cerr << get_fileline() << ": debug: pruning case expressions to " << prune_width << " bits." << endl; } // Prune the case expression expr_ = pad_to_width(unpadded_expr->dup_expr(), prune_width, *expr_); delete padded_expr; // Prune the case item expressions for (unsigned idx = 0; idx < items_.size(); idx += 1) { if (items_[idx].guard == 0) continue; NetEConst*gc = dynamic_cast(items_[idx].guard); ivl_assert(*this, gc); verinum value(gc->value(), prune_width); NetEConst*tmp = new NetEConst(value); tmp->set_line(*gc); delete gc; items_[idx].guard = tmp; } } NetDisable::NetDisable(NetScope*tgt) : target_(tgt) { } NetDisable::~NetDisable() { } const NetScope* NetDisable::target() const { return target_; } NetForever::NetForever(NetProc*p) : statement_(p) { } NetForever::~NetForever() { delete statement_; } NetForLoop::NetForLoop(NetNet*ind, NetExpr*iexpr, NetExpr*cond, NetProc*sub, NetProc*step) : index_(ind), init_expr_(iexpr), condition_(cond), statement_(sub), step_statement_(step) { } void NetForLoop::wrap_up() { NetBlock*top = new NetBlock(NetBlock::SEQU, 0); top->set_line(*this); NetAssign_*lv = new NetAssign_(index_); NetAssign*set_stmt = new NetAssign(lv, init_expr_); set_stmt->set_line(*init_expr_); top->append(set_stmt); NetBlock*internal_block = new NetBlock(NetBlock::SEQU, 0); internal_block->set_line(*this); if (statement_) internal_block->append(statement_); internal_block->append(step_statement_); NetWhile*wloop = new NetWhile(condition_, internal_block); wloop->set_line(*this); top->append(wloop); as_block_ = top; } NetForLoop::~NetForLoop() { delete init_expr_; delete condition_; delete statement_; delete step_statement_; } NetPDelay::NetPDelay(uint64_t d, NetProc*st) : delay_(d), expr_(0), statement_(st) { } NetPDelay::NetPDelay(NetExpr*d, NetProc*st) : delay_(0), expr_(d), statement_(st) { } NetPDelay::~NetPDelay() { delete expr_; } uint64_t NetPDelay::delay() const { ivl_assert(*this, expr_ == 0); return delay_; } const NetExpr* NetPDelay::expr() const { return expr_; } NetRepeat::NetRepeat(NetExpr*e, NetProc*p) : expr_(e), statement_(p) { } NetRepeat::~NetRepeat() { delete expr_; delete statement_; } const NetExpr* NetRepeat::expr() const { return expr_; } iverilog-10_1/net_scope.cc000066400000000000000000000424531265551621300156400ustar00rootroot00000000000000/* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "netlist.h" # include "netclass.h" # include "netenum.h" # include # include # include # include "ivl_assert.h" class PExpr; Definitions::Definitions() { } Definitions::~Definitions() { } void Definitions::add_enumeration_set(const enum_type_t*key, netenum_t*enum_set) { netenum_t*&tmp = enum_sets_[key]; assert(tmp == 0); tmp = enum_set; } bool Definitions::add_enumeration_name(netenum_t*enum_set, perm_string name) { netenum_t::iterator enum_val = enum_set->find_name(name); assert(enum_val != enum_set->end_name()); NetEConstEnum*val = new NetEConstEnum(this, name, enum_set, enum_val->second); pair::iterator, bool> cur; cur = enum_names_.insert(make_pair(name,val)); // Return TRUE if the name is added (i.e. is NOT a duplicate.) return cur.second; } netenum_t* Definitions::enumeration_for_key(const enum_type_t*key) const { map::const_iterator cur; cur = enum_sets_.find(key); if (cur != enum_sets_.end()) return cur->second; else return 0; } /* * This locates the enumeration TYPE for the given enumeration literal. */ const netenum_t*Definitions::enumeration_for_name(perm_string name) { NetEConstEnum*tmp = enum_names_[name]; assert(tmp != 0); return tmp->enumeration(); } /* * This locates the VALUE for the given enumeration literal. */ const NetExpr* Definitions::enumeration_expr(perm_string key) { map::const_iterator eidx; eidx = enum_names_.find(key); if (eidx != enum_names_.end()) { return eidx->second; } else { return 0; } } void Definitions::add_class(netclass_t*net_class) { classes_[net_class->get_name()] = net_class; } /* * The NetScope class keeps a scope tree organized. Each node of the * scope tree points to its parent, its right sibling and its leftmost * child. The root node has no parent or siblings. The node stores the * name of the scope. The complete hierarchical name of the scope is * formed by appending the path of scopes from the root to the scope * in question. */ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, bool program, bool interface) : type_(t), name_(n), nested_module_(nest), program_block_(program), is_interface_(interface), up_(up) { events_ = 0; lcounter_ = 0; is_auto_ = false; is_cell_ = false; calls_stask_ = false; in_final_ = false; if (up) { assert(t!=CLASS); need_const_func_ = up->need_const_func_; is_const_func_ = up->is_const_func_; time_unit_ = up->time_unit(); time_prec_ = up->time_precision(); time_from_timescale_ = up->time_from_timescale(); // Need to check for duplicate names? up_->children_[name_] = this; } else { need_const_func_ = false; is_const_func_ = false; time_unit_ = 0; time_prec_ = 0; time_from_timescale_ = false; } switch (t) { case NetScope::TASK: task_ = 0; break; case NetScope::FUNC: func_ = 0; break; case NetScope::MODULE: case NetScope::PACKAGE: module_name_ = perm_string(); break; case NetScope::CLASS: class_def_ = 0; break; default: /* BEGIN_END and FORK_JOIN, do nothing */ break; } func_pform_ = 0; elab_stage_ = 1; lineno_ = 0; def_lineno_ = 0; genvar_tmp_val = 0; } NetScope::~NetScope() { lcounter_ = 0; /* name_ and module_name_ are perm-allocated. */ } void NetScope::set_line(const LineInfo*info) { file_ = info->get_file(); def_file_ = file_; lineno_ = info->get_lineno(); def_lineno_ = lineno_; } void NetScope::set_line(perm_string file, unsigned lineno) { file_ = file; def_file_ = file; lineno_ = lineno; def_lineno_ = lineno; } void NetScope::set_line(perm_string file, perm_string def_file, unsigned lineno, unsigned def_lineno) { file_ = file; def_file_ = def_file; lineno_ = lineno; def_lineno_ = def_lineno; } /* * Look for the enumeration in the current scope and any parent scopes. */ const netenum_t*NetScope::find_enumeration_for_name(perm_string name) { NetScope *cur_scope = this; while (cur_scope) { NetEConstEnum*tmp = cur_scope->enum_names_[name]; if (tmp) break; cur_scope = cur_scope->parent(); } assert(cur_scope); return cur_scope->enum_names_[name]->enumeration(); } void NetScope::set_parameter(perm_string key, bool is_annotatable, PExpr*val, ivl_variable_type_t type__, PExpr*msb, PExpr*lsb, bool signed_flag, bool local_flag, NetScope::range_t*range_list, const LineInfo&file_line) { param_expr_t&ref = parameters[key]; ref.is_annotatable = is_annotatable; ref.msb_expr = msb; ref.lsb_expr = lsb; ref.val_expr = val; ref.val_scope = this; ref.type = type__; ref.msb = 0; ref.lsb = 0; ref.signed_flag = signed_flag; ref.local_flag = local_flag; ivl_assert(file_line, ref.range == 0); ref.range = range_list; ref.val = 0; ref.set_line(file_line); } /* * This is a simplified version of set_parameter, for use when the * parameter value is already known. It is currently only used to * add a genvar to the parameter list. */ void NetScope::set_parameter(perm_string key, NetExpr*val, const LineInfo&file_line) { param_expr_t&ref = parameters[key]; ref.is_annotatable = false; ref.msb_expr = 0; ref.lsb_expr = 0; ref.val_expr = 0; ref.val_scope = this; ref.type = IVL_VT_BOOL; ref.msb = 0; ref.lsb = 0; ref.signed_flag = false; ref.val = val; ref.set_line(file_line); } bool NetScope::auto_name(const char*prefix, char pad, const char* suffix) { // Find the current reference to myself in the parent scope. map::iterator self = up_->children_.find(name_); assert(self != up_->children_.end()); assert(self->second == this); // This is to keep the pad attempts from being stuck in some // sort of infinite loop. This should not be a practical // limit, but an extreme one. const size_t max_pad_attempts = 32 + strlen(prefix); string use_prefix = prefix; // Try a variety of potential new names. Make sure the new // name is not in the parent scope. Keep looking until we are // sure we have a unique name, or we run out of names to try. while (use_prefix.size() <= max_pad_attempts) { // Try this name... string tmp = use_prefix + suffix; hname_t new_name(lex_strings.make(tmp.c_str()), name_.peek_numbers()); if (!up_->child(new_name)) { // Ah, this name is unique. Rename myself, and // change my name in the parent scope. name_ = new_name; up_->children_.erase(self); up_->children_[name_] = this; return true; } // Name collides, so try a different name. use_prefix = use_prefix + pad; } return false; } /* * Return false if the parameter does not already exist. * A parameter is not automatically created. */ bool NetScope::replace_parameter(perm_string key, PExpr*val, NetScope*scope) { bool flag = false; if (parameters.find(key) != parameters.end()) { param_expr_t&ref = parameters[key]; ref.val_expr = val; ref.val_scope = scope; flag = true; } return flag; } bool NetScope::make_parameter_unannotatable(perm_string key) { bool flag = false; if (parameters.find(key) != parameters.end()) { param_expr_t&ref = parameters[key]; flag = ref.is_annotatable; ref.is_annotatable = false; } return flag; } /* * NOTE: This method takes a const char* as a key to lookup a * parameter, because we don't save that pointer. However, due to the * way the map<> template works, we need to *cheat* and use the * perm_string::literal method to fake the compiler into doing the * compare without actually creating a perm_string. */ const NetExpr* NetScope::get_parameter(Design*des, const char* key, const NetExpr*&msb, const NetExpr*&lsb) { return get_parameter(des, perm_string::literal(key), msb, lsb); } const NetExpr* NetScope::get_parameter(Design*des, perm_string key, const NetExpr*&msb, const NetExpr*&lsb) { map::iterator idx; idx = parameters.find(key); if (idx != parameters.end()) { if (idx->second.val_expr) evaluate_parameter_(des, idx); msb = idx->second.msb; lsb = idx->second.lsb; return idx->second.val; } msb = 0; lsb = 0; const NetExpr*tmp = enumeration_expr(key); if (tmp) return tmp; tmp = des->enumeration_expr(key); if (tmp) return tmp; return 0; } NetScope::param_ref_t NetScope::find_parameter(perm_string key) { map::iterator idx; idx = parameters.find(key); if (idx != parameters.end()) return idx; // To get here the parameter must already exist, so we should // never get here. assert(0); // But return something to avoid a compiler warning. return idx; } NetScope::TYPE NetScope::type() const { return type_; } void NetScope::print_type(ostream&stream) const { switch (type_) { case BEGIN_END: stream << "sequential block"; break; case FORK_JOIN: stream << "parallel block"; break; case FUNC: stream << "function"; break; case MODULE: stream << "module <" << module_name_ << "> instance"; break; case TASK: stream << "task"; break; case GENBLOCK: stream << "generate block"; break; case PACKAGE: stream << "package " << module_name_; break; case CLASS: stream << "class"; break; } } void NetScope::set_task_def(NetTaskDef*def) { assert( type_ == TASK ); assert( task_ == 0 ); task_ = def; } NetTaskDef* NetScope::task_def() { assert( type_ == TASK ); return task_; } const NetTaskDef* NetScope::task_def() const { assert( type_ == TASK ); return task_; } void NetScope::set_func_def(NetFuncDef*def) { assert( type_ == FUNC ); assert( func_ == 0 ); func_ = def; } NetFuncDef* NetScope::func_def() { assert( type_ == FUNC ); return func_; } bool NetScope::in_func() const { return (type_ == FUNC) ? true : false; } const NetFuncDef* NetScope::func_def() const { assert( type_ == FUNC ); return func_; } void NetScope::set_class_def(netclass_t*def) { assert( type_ == CLASS ); assert( class_def_==0 ); class_def_ = def; } const netclass_t* NetScope::class_def(void) const { if (type_==CLASS) return class_def_; else return 0; } void NetScope::set_module_name(perm_string n) { assert(type_==MODULE || type_==PACKAGE); module_name_ = n; } perm_string NetScope::module_name() const { assert(type_==MODULE || type_==PACKAGE); return module_name_; } void NetScope::set_num_ports(unsigned int num_ports) { assert(type_ == MODULE); assert(ports_.empty()); ports_.resize( num_ports ); } void NetScope::add_module_port_net(NetNet*subport) { assert(type_ == MODULE); port_nets.push_back(subport); } void NetScope::add_module_port_info( unsigned idx, perm_string name, PortType::Enum ptype, unsigned long width ) { assert(type_ == MODULE); PortInfo &info = ports_[idx]; info.name = name; info.type = ptype; info.width = width; } unsigned NetScope::module_port_nets() const { assert(type_ == MODULE); return port_nets.size(); } const std::vector & NetScope::module_port_info() const { assert(type_ == MODULE); return ports_; } NetNet* NetScope::module_port_net(unsigned idx) const { assert(type_ == MODULE); assert(idx < port_nets.size()); return port_nets[idx]; } void NetScope::time_unit(int val) { time_unit_ = val; } void NetScope::time_precision(int val) { time_prec_ = val; } void NetScope::time_from_timescale(bool val) { time_from_timescale_ = val; } int NetScope::time_unit() const { return time_unit_; } int NetScope::time_precision() const { return time_prec_; } bool NetScope::time_from_timescale() const { return time_from_timescale_; } perm_string NetScope::basename() const { return name_.peek_name(); } void NetScope::add_event(NetEvent*ev) { assert(ev->scope_ == 0); ev->scope_ = this; ev->snext_ = events_; events_ = ev; } void NetScope::rem_event(NetEvent*ev) { assert(ev->scope_ == this); ev->scope_ = 0; if (events_ == ev) { events_ = ev->snext_; } else { NetEvent*cur = events_; while (cur->snext_ != ev) { assert(cur->snext_); cur = cur->snext_; } cur->snext_ = ev->snext_; } ev->snext_ = 0; } NetEvent* NetScope::find_event(perm_string name) { for (NetEvent*cur = events_; cur ; cur = cur->snext_) if (cur->name() == name) return cur; return 0; } void NetScope::add_genvar(perm_string name, LineInfo *li) { assert((type_ == MODULE) || (type_ == GENBLOCK)); genvars_[name] = li; } LineInfo* NetScope::find_genvar(perm_string name) { if (genvars_.find(name) != genvars_.end()) return genvars_[name]; else return 0; } void NetScope::add_signal(NetNet*net) { signals_map_[net->name()]=net; } void NetScope::rem_signal(NetNet*net) { assert(net->scope() == this); signals_map_.erase(net->name()); } /* * This method looks for a signal within the current scope. The name * is assumed to be the base name of the signal, so no sub-scopes are * searched. */ NetNet* NetScope::find_signal(perm_string key) { if (signals_map_.find(key)!=signals_map_.end()) return signals_map_[key]; else return 0; } netclass_t*NetScope::find_class(perm_string name) { // Special case: The scope itself is the class that we are // looking for. This may happen for example when elaborating // methods within the class. if (type_==CLASS && name_==hname_t(name)) return class_def_; // Look for the class that directly within this scope. map::const_iterator cur = classes_.find(name); if (cur != classes_.end()) return cur->second; // If this is a module scope, then look no further. if (type_==MODULE) return 0; if (up_==0 && type_==CLASS) { assert(class_def_); NetScope*def_parent = class_def_->definition_scope(); return def_parent->find_class(name); } // If there is no further to look, ... if (up_ == 0) return 0; // Try looking up for the class. return up_->find_class(name); } /* * This method locates a child scope by name. The name is the simple * name of the child, no hierarchy is searched. */ NetScope* NetScope::child(const hname_t&name) { map::iterator cur = children_.find(name); if (cur == children_.end()) return 0; else return cur->second; } const NetScope* NetScope::child(const hname_t&name) const { map::const_iterator cur = children_.find(name); if (cur == children_.end()) return 0; else return cur->second; } /* Helper function to see if the given scope is defined in a class and if * so return the class scope. */ const NetScope* NetScope::get_class_scope() const { const NetScope*scope = this; while (scope) { switch(scope->type()) { case NetScope::CLASS: return scope; case NetScope::TASK: case NetScope::FUNC: case NetScope::BEGIN_END: case NetScope::FORK_JOIN: break; case NetScope::MODULE: case NetScope::GENBLOCK: case NetScope::PACKAGE: return 0; default: assert(0); } scope = scope->parent(); } return scope; } const NetScope* NetScope::child_byname(perm_string name) const { hname_t hname (name); map::const_iterator cur = children_.lower_bound(hname); if (cur == children_.end()) return 0; if (cur->first.peek_name() == name) return cur->second; return 0; } perm_string NetScope::local_symbol() { ostringstream res; res << "_s" << (lcounter_++); return lex_strings.make(res.str()); } iverilog-10_1/net_tran.cc000066400000000000000000000114471265551621300154720ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "ivl_target_priv.h" # include "ivl_assert.h" static bool has_enable(ivl_switch_type_t tt) { switch (tt) { case IVL_SW_TRANIF0: case IVL_SW_TRANIF1: case IVL_SW_RTRANIF0: case IVL_SW_RTRANIF1: return true; default: return false; } } NetTran::NetTran(NetScope*scope__, perm_string n, ivl_switch_type_t tt, unsigned width) : NetNode(scope__, n, has_enable(tt)? 3 : 2), type_(tt), wid_(width) { pin(0).set_dir(Link::PASSIVE); pin(1).set_dir(Link::PASSIVE); if (pin_count() == 3) { pin(2).set_dir(Link::INPUT); // Enable } part_ = 0; off_ = 0; } NetTran::NetTran(NetScope*scope__, perm_string n, unsigned wid, unsigned part, unsigned off) : NetNode(scope__, n, 2), type_(IVL_SW_TRAN_VP), wid_(wid), part_(part), off_(off) { pin(0).set_dir(Link::PASSIVE); pin(1).set_dir(Link::PASSIVE); } NetTran::~NetTran() { } unsigned NetTran::vector_width() const { return wid_; } unsigned NetTran::part_width() const { return part_; } unsigned NetTran::part_offset() const { return off_; } void join_island(NetPins*obj) { IslandBranch*branch = dynamic_cast (obj); // If this is not even a branch, then stop now. if (branch == 0) return; // If this is a branch, but already given to an island, then // stop. if (branch->island_) return; list uncommitted_neighbors; // Look for neighboring objects that might already be in // islands. If we find something, then join that island. for (unsigned idx = 0 ; idx < obj->pin_count() ; idx += 1) { Nexus*nex = obj->pin(idx).nexus(); for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { unsigned pin; NetPins*tmp_pins; cur->cur_link(tmp_pins, pin); NetObj*tmp = dynamic_cast (tmp_pins); if (tmp == 0) continue; // Skip self. if (tmp == obj) continue; // If tmp is not a branch, then skip it. IslandBranch*tmp_branch = dynamic_cast (tmp); if (tmp_branch == 0) continue; // If tmp is an uncommitted branch, then save // it. When I finally choose an island for self, // these branches will be scanned so that they join // this island as well. if (tmp_branch->island_ == 0) { uncommitted_neighbors.push_back(tmp); continue; } ivl_assert(*obj, branch->island_==0 || branch->island_==tmp_branch->island_); // We found an existing island to join. Join it // now. Keep scanning in order to find more neighbors. if (branch->island_ == 0) { if (debug_elaborate) cerr << obj->get_fileline() << ": debug: " << "Join branch to existing island." << endl; branch->island_ = tmp_branch->island_; ivl_assert(*obj, branch->island_->discipline == tmp_branch->island_->discipline); } else if (branch->island_ != tmp_branch->island_) { cerr << obj->get_fileline() << ": internal error: " << "Oops, Found 2 neighboring islands." << endl; ivl_assert(*obj, 0); } } } // If after all that we did not find an island to join, then // start the island not and join it. if (branch->island_ == 0) { branch->island_ = new ivl_island_s; branch->island_->discipline = branch->discipline_; if (debug_elaborate) cerr << obj->get_fileline() << ": debug: " << "Create new island for this branch" << endl; } // Now scan all the uncommitted neighbors I found. Calling // join_island() on them will cause them to notice me in the // process, and thus they will join my island. This process // will recurse until all the connected branches join this island. for (list::iterator cur = uncommitted_neighbors.begin() ; cur != uncommitted_neighbors.end() ; ++ cur ) { join_island(*cur); } } iverilog-10_1/net_udp.cc000066400000000000000000000046071265551621300153160ustar00rootroot00000000000000/* * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) * Copyright (c) 2001 Stephan Boettcher * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "netlist.h" NetUDP::NetUDP(NetScope*s, perm_string n, unsigned pins, PUdp *u) : NetNode(s, n, pins), udp(u) { pin(0).set_dir(Link::OUTPUT); for (unsigned idx = 1 ; idx < pins ; idx += 1) { pin(idx).set_dir(Link::INPUT); } table_idx = udp->tinput.count()-1; } bool NetUDP::first(string&inp, char&out) const { table_idx = (unsigned) -1; return next(inp, out); } bool NetUDP::next(string&inp, char&out) const { table_idx++; if (table_idx >= udp->tinput.count()) return false; if (is_sequential()) { inp = string("") + udp->tcurrent[table_idx] + udp->tinput[table_idx]; assert(inp.length() == pin_count()); } else { inp = udp->tinput[table_idx]; assert(inp.length() == (pin_count()-1)); } out = udp->toutput[table_idx]; assert((out == '0') || (out == '1') || (out == 'x') || (is_sequential() && (out == '-'))); return true; } char NetUDP::get_initial() const { assert (is_sequential()); switch (udp->initial) { case verinum::V0: return '0'; case verinum::V1: return '1'; case verinum::Vx: case verinum::Vz: return 'x'; } assert(0); return 'x'; } unsigned NetUDP::port_count() const { return udp->ports.count(); } string NetUDP::port_name(unsigned idx) const { assert(idx < udp->ports.count()); return udp->ports[idx]; } iverilog-10_1/netclass.cc000066400000000000000000000117021265551621300154660ustar00rootroot00000000000000/* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netclass.h" # include "netlist.h" # include using namespace std; netclass_t::netclass_t(perm_string name, netclass_t*sup) : name_(name), super_(sup), class_scope_(0), definition_scope_(0) { } netclass_t::~netclass_t() { } bool netclass_t::set_property(perm_string pname, property_qualifier_t qual, ivl_type_s*ptype) { map::const_iterator cur; cur = properties_.find(pname); if (cur != properties_.end()) return false; prop_t tmp; tmp.name = pname; tmp.qual = qual; tmp.type = ptype; tmp.initialized_flag = false; property_table_.push_back(tmp); properties_[pname] = property_table_.size()-1; return true; } void netclass_t::set_class_scope(NetScope*class_scope__) { assert(class_scope_ == 0); class_scope_ = class_scope__; } void netclass_t::set_definition_scope(NetScope*use_definition_scope) { assert(definition_scope_ == 0); definition_scope_ = use_definition_scope; } ivl_variable_type_t netclass_t::base_type() const { return IVL_VT_CLASS; } size_t netclass_t::get_properties(void) const { size_t res = properties_.size(); if (super_) res += super_->get_properties(); return res; } int netclass_t::property_idx_from_name(perm_string pname) const { map::const_iterator cur; cur = properties_.find(pname); if (cur == properties_.end()) { if (super_) return super_->property_idx_from_name(pname); else return -1; } int pidx = cur->second; if (super_) pidx += super_->get_properties(); return pidx; } const char*netclass_t::get_prop_name(size_t idx) const { size_t super_size = 0; if (super_) super_size = super_->get_properties(); assert(idx < (super_size + property_table_.size())); if (idx < super_size) return super_->get_prop_name(idx); else return property_table_[idx-super_size].name; } property_qualifier_t netclass_t::get_prop_qual(size_t idx) const { size_t super_size = 0; if (super_) super_size = super_->get_properties(); assert(idx < (super_size+property_table_.size())); if (idx < super_size) return super_->get_prop_qual(idx); else return property_table_[idx-super_size].qual; } ivl_type_t netclass_t::get_prop_type(size_t idx) const { size_t super_size = 0; if (super_) super_size = super_->get_properties(); assert(idx < (super_size+property_table_.size())); if (idx < super_size) return super_->get_prop_type(idx); else return property_table_[idx-super_size].type; } bool netclass_t::get_prop_initialized(size_t idx) const { size_t super_size = 0; if (super_) super_size = super_->get_properties(); assert(idx < (super_size+property_table_.size())); if (idx < super_size) return super_->get_prop_initialized(idx); else return property_table_[idx].initialized_flag; } void netclass_t::set_prop_initialized(size_t idx) const { size_t super_size = 0; if (super_) super_size = super_->get_properties(); assert(idx >= super_size && idx < (super_size+property_table_.size())); idx -= super_size; assert(! property_table_[idx].initialized_flag); property_table_[idx].initialized_flag = true; } bool netclass_t::test_for_missing_initializers() const { for (size_t idx = 0 ; idx < property_table_.size() ; idx += 1) { if (property_table_[idx].initialized_flag) continue; if (property_table_[idx].qual.test_const()) return true; } return false; } NetScope*netclass_t::method_from_name(perm_string name) const { NetScope*task = class_scope_->child( hname_t(name) ); if (task == 0) return 0; return task; } NetNet* netclass_t::find_static_property(perm_string name) const { NetNet*tmp = class_scope_->find_signal(name); return tmp; } bool netclass_t::test_scope_is_method(const NetScope*scope) const { while (scope && scope != class_scope_) { scope = scope->parent(); } if (scope == 0) return false; else return true; } iverilog-10_1/netclass.h000066400000000000000000000116061265551621300153330ustar00rootroot00000000000000#ifndef IVL_netclass_H #define IVL_netclass_H /* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "ivl_target.h" # include "nettypes.h" # include "property_qual.h" # include # include class Design; class NetNet; class NetScope; class PClass; class PExpr; class netclass_t : public ivl_type_s { public: netclass_t(perm_string class_name, netclass_t*par); ~netclass_t(); // Set the property of the class during elaboration. Set the // name and type, and return true. If the name is already // present, then return false. bool set_property(perm_string pname, property_qualifier_t qual, ivl_type_s*ptype); // Set the scope for the class. The scope has no parents and // is used for the elaboration of methods // (tasks/functions). In other words, this is the class itself. void set_class_scope(NetScope*cscope); inline const NetScope* class_scope(void) const { return class_scope_; } // Set the scope for the class definition. This is the scope // where the class definition was encountered, and may be used // to locate symbols that the class definition may inherit // from its context. This can be nil, or a package or module // where a class is defined. void set_definition_scope(NetScope*dscope); NetScope*definition_scope(void); // As an ivl_type_s object, the netclass is always an // ivl_VT_CLASS object. ivl_variable_type_t base_type() const; // This is the name of the class type inline perm_string get_name() const { return name_; } // If this is derived from another class, then this method // returns a pointer to the super-class. inline const netclass_t* get_super() const { return super_; } // Get the number of properties in this class. Include // properties in the parent class. size_t get_properties(void) const; // Get information about each property. const char*get_prop_name(size_t idx) const; property_qualifier_t get_prop_qual(size_t idx) const; ivl_type_t get_prop_type(size_t idx) const; // These methods are used by the elaborator to note the // initializer for constant properties. Properties start out // as not initialized, and when elaboration detects an // assignment to the property, it is marked initialized. bool get_prop_initialized(size_t idx) const; void set_prop_initialized(size_t idx) const; bool test_for_missing_initializers(void) const; // Map the name of a property to its index. Return <0 if the // name is not a property in the class. int property_idx_from_name(perm_string pname) const; // The task method scopes from the method name. NetScope*method_from_name(perm_string mname) const; // Find the elaborated signal (NetNet) for a static // property. Search by name. The signal is created by the // elaborate_sig pass. NetNet*find_static_property(perm_string name) const; // Test if this scope is a method within the class. This is // used to check scope for handling data protection keywords // "local" and "protected". bool test_scope_is_method(const NetScope*scope) const; void elaborate_sig(Design*des, PClass*pclass); void elaborate(Design*des, PClass*pclass); void emit_scope(struct target_t*tgt) const; bool emit_defs(struct target_t*tgt) const; std::ostream& debug_dump(std::ostream&fd) const; void dump_scope(ostream&fd) const; private: perm_string name_; // If this is derived from another base class, point to it // here. netclass_t*super_; // Map property names to property table index. std::map properties_; // Vector of properties. struct prop_t { perm_string name; property_qualifier_t qual; ivl_type_s* type; mutable bool initialized_flag; }; std::vector property_table_; // This holds task/function definitions for methods. NetScope*class_scope_; // This holds the context for the class type definition. NetScope*definition_scope_; }; inline NetScope*netclass_t::definition_scope(void) { return definition_scope_; } #endif /* IVL_netclass_H */ iverilog-10_1/netdarray.cc000066400000000000000000000025161265551621300156460ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netdarray.h" # include using namespace std; netdarray_t::netdarray_t(ivl_type_t vec) : netarray_t(vec) { } netdarray_t::~netdarray_t() { } ivl_variable_type_t netdarray_t::base_type(void) const { return IVL_VT_DARRAY; } bool netdarray_t::test_compatibility(ivl_type_t that) const { const netdarray_t*that_da = dynamic_cast(that); if (that_da == 0) return false; return element_type()->type_compatible(that_da->element_type()); } iverilog-10_1/netdarray.h000066400000000000000000000040051265551621300155030ustar00rootroot00000000000000#ifndef IVL_netdarray_H #define IVL_netdarray_H /* * Copyright (c) 2012-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "nettypes.h" # include "ivl_target.h" class netdarray_t : public netarray_t { public: explicit netdarray_t(ivl_type_t vec); ~netdarray_t(); // This is the "base_type()" virtual method of the // nettype_base_t. The ivl_target api expects this to return // IVL_VT_DARRAY for dynamic arrays? ivl_variable_type_t base_type() const; // A dynamic array may have a type that is signed. inline bool get_signed() const { return element_type()->get_signed(); } // This is the base_type() of the element of the array. We // need this in some cases in order to get the base type of // the element, and not the IVL_VT_DARRAY of the array itself. inline ivl_variable_type_t element_base_type() const { return element_type()->base_type(); } // This is a convenience function for getting the width of an // element. Strictly speaking it's not necessary. inline unsigned long element_width(void) const { return element_type()->packed_width(); } std::ostream& debug_dump(std::ostream&) const; private: bool test_compatibility(ivl_type_t that) const; }; #endif /* IVL_netdarray_H */ iverilog-10_1/netenum.cc000066400000000000000000000101131265551621300153200ustar00rootroot00000000000000/* * Copyright (c) 2010-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netenum.h" # include "compiler.h" # include using namespace std; netenum_t::netenum_t(ivl_variable_type_t btype, bool signed_flag, bool integer_flag, long msb, long lsb, size_t name_count, enum_type_t*enum_type) : base_type_(btype), enum_type_(enum_type), signed_flag_(signed_flag), integer_flag_(integer_flag), msb_(msb), lsb_(lsb), names_(name_count), bits_(name_count) { } netenum_t::~netenum_t() { } bool netenum_t::get_signed() const { return signed_flag_; } bool netenum_t::get_isint() const { return integer_flag_; } /* * Enumerations are by definition always packed. */ bool netenum_t::packed() const { return true; } long netenum_t::packed_width() const { if (msb_ >= lsb_) return msb_ - lsb_ + 1; else return lsb_ - msb_ + 1; } vector netenum_t::slice_dimensions() const { vector tmp (1); tmp[0] = netrange_t(msb_, lsb_); return tmp; } bool netenum_t::insert_name(size_t name_idx, perm_string name, const verinum&val) { std::pair::iterator, bool> res; assert((msb_-lsb_+1) > 0); assert(val.has_len() && val.len() == (unsigned)(msb_-lsb_+1)); // Insert a map of the name to the value. This also gets a // flag that returns true if the name is unique, or false // otherwise. res = names_map_.insert( make_pair(name,val) ); assert(name_idx < names_.size() && names_[name_idx] == 0); names_[name_idx] = name; return res.second; } void netenum_t::insert_name_close(void) { for (size_t idx = 0 ; idx < names_.size() ; idx += 1) { // If we failed to elaborate the name then skip this step. if (names_[idx].nil()) continue; netenum_t::iterator cur = names_map_.find(names_[idx]); vectorstr (cur->second.len() + 1); for (unsigned bit = 0 ; bit < cur->second.len() ; bit += 1) { switch (cur->second.get(bit)) { case verinum::V0: str[bit] = '0'; break; case verinum::V1: str[bit] = '1'; break; case verinum::Vx: str[bit] = 'x'; break; case verinum::Vz: str[bit] = 'z'; break; } } bits_[idx] = bits_strings.make(&str[0]); } } netenum_t::iterator netenum_t::find_name(perm_string name) const { return names_map_.find(name); } /* * Check to see if the given value is already in the enumeration mapping. */ perm_string netenum_t::find_value(const verinum&val) const { perm_string res; for(netenum_t::iterator cur = names_map_.begin(); cur != names_map_.end(); ++ cur) { if (cur->second == val) { res = cur->first; break; } } return res; } netenum_t::iterator netenum_t::end_name() const { return names_map_.end(); } netenum_t::iterator netenum_t::first_name() const { return names_map_.find(names_.front()); } netenum_t::iterator netenum_t::last_name() const { return names_map_.find(names_.back()); } perm_string netenum_t::name_at(size_t idx) const { assert(idx < names_.size()); return names_[idx]; } perm_string netenum_t::bits_at(size_t idx) const { return bits_[idx]; } bool netenum_t::matches(const netenum_t*other) const { return enum_type_ == other->enum_type_; } iverilog-10_1/netenum.h000066400000000000000000000056521265551621300151760ustar00rootroot00000000000000#ifndef IVL_netenum_H #define IVL_netenum_H /* * Copyright (c) 2010-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "ivl_target.h" # include "nettypes.h" # include "verinum.h" # include "StringHeap.h" # include "LineInfo.h" # include # include class NetScope; struct enum_type_t; class netenum_t : public LineInfo, public ivl_type_s { public: explicit netenum_t(ivl_variable_type_t base_type, bool signed_flag, bool isint_flag, long msb, long lsb, size_t name_count, enum_type_t*enum_type); ~netenum_t(); virtual ivl_variable_type_t base_type() const; virtual bool packed() const; virtual long packed_width() const; std::vector slice_dimensions() const; bool get_signed() const; bool get_isint() const; // The size() is the number of enumeration literals. size_t size() const; // Insert the name (and value) at the specific place in the // enumeration. This must be done exactly once for each // enumeration value. bool insert_name(size_t idx, perm_string name, const verinum&val); // Indicate that there will be no more names to insert. void insert_name_close(void); typedef std::map::const_iterator iterator; iterator find_name(perm_string name) const; iterator end_name() const; perm_string find_value(const verinum&val) const; // These methods roughly match the .first() and .last() methods. iterator first_name() const; iterator last_name() const; perm_string name_at(size_t idx) const; perm_string bits_at(size_t idx) const; // Check if two enumerations have the same definition. bool matches(const netenum_t*other) const; private: ivl_variable_type_t base_type_; enum_type_t*enum_type_; bool signed_flag_; bool integer_flag_; long msb_, lsb_; std::map names_map_; std::vector names_; std::vector bits_; }; inline ivl_variable_type_t netenum_t::base_type() const { return base_type_; } inline size_t netenum_t::size() const { return names_.size(); } #endif /* IVL_netenum_H */ iverilog-10_1/netlist.cc000066400000000000000000001554241265551621300153460ustar00rootroot00000000000000/* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include # include # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "netclass.h" # include "netdarray.h" # include "netenum.h" # include "netparray.h" # include "netqueue.h" # include "netstruct.h" # include "netvector.h" # include "ivl_assert.h" ostream& operator<< (ostream&o, NetNet::Type t) { switch (t) { case NetNet::NONE: o << "net_none"; break; case NetNet::IMPLICIT: o << "wire /*implicit*/"; break; case NetNet::IMPLICIT_REG: o << "reg /*implicit*/"; break; case NetNet::INTEGER: o << "integer"; break; case NetNet::REG: o << "reg"; break; case NetNet::SUPPLY0: o << "supply0"; break; case NetNet::SUPPLY1: o << "supply1"; break; case NetNet::TRI: o << "tri"; break; case NetNet::TRI0: o << "tri0"; break; case NetNet::TRI1: o << "tri1"; break; case NetNet::TRIAND: o << "triand"; break; case NetNet::TRIOR: o << "trior"; break; case NetNet::WAND: o << "wand"; break; case NetNet::WOR: o << "wor"; break; case NetNet::WIRE: o << "wire"; break; case NetNet::UNRESOLVED_WIRE: o << "uwire"; } return o; } unsigned count_signals(const Link&pin) { unsigned count = 0; const Nexus*nex = pin.nexus(); for (const Link*clnk = nex->first_nlink() ; clnk ; clnk = clnk->next_nlink()) { const NetPins*cur; unsigned cpin; clnk->cur_link(cur, cpin); if (dynamic_cast(cur)) count += 1; } return count; } const NetNet* find_link_signal(const NetObj*net, unsigned pin, unsigned&bidx) { const Nexus*nex = net->pin(pin).nexus(); for (const Link*clnk = nex->first_nlink() ; clnk ; clnk = clnk->next_nlink()) { const NetPins*cur; unsigned cpin; clnk->cur_link(cur, cpin); const NetNet*sig = dynamic_cast(cur); if (sig) { bidx = cpin; return sig; } } return 0; } Link* find_next_output(Link*lnk) { Link*cur = lnk->next_nlink(); while (cur != lnk) { if (cur->get_dir() == Link::OUTPUT) return cur; cur = cur->next_nlink(); if (cur == 0) cur = lnk->nexus()->first_nlink(); } return 0; } void NetPins::devirtualize_pins(void) { if (pins_) return; if (npins_ > array_size_limit) { cerr << get_fileline() << ": error: pin count " << npins_ << " exceeds " << array_size_limit << " (set by -pARRAY_SIZE_LIMIT)" << endl; assert(0); } if (debug_optimizer && npins_ > 1000) cerr << "debug: devirtualizing " << npins_ << " pins." << endl; pins_ = new Link[npins_]; pins_[0].pin_zero_ = true; pins_[0].node_ = this; pins_[0].dir_ = default_dir_; for (unsigned idx = 1 ; idx < npins_ ; idx += 1) { pins_[idx].pin_zero_ = false; pins_[idx].pin_ = idx; pins_[idx].dir_ = default_dir_; } } bool NetPins::pins_are_virtual(void) const { return pins_ == NULL; } NetPins::NetPins(unsigned npins) : npins_(npins) { pins_ = NULL; // Wait until someone asks. if (disable_virtual_pins) devirtualize_pins(); // Ask. Bummer. } NetPins::~NetPins() { if (pins_) { assert(pins_[0].node_ == this); assert(pins_[0].pin_zero_); delete[] pins_; } } Link& NetPins::pin(unsigned idx) { if (!pins_) devirtualize_pins(); if (idx >= npins_) { cerr << get_fileline() << ": internal error: pin("< 0); * because it would happen before we get to print a useful * message in the NetNet constructor */ } NetObj::~NetObj() { } NetScope* NetObj::scope() { return scope_; } const NetScope* NetObj::scope() const { return scope_; } NetNode::NetNode(NetScope*s, perm_string n, unsigned npins) : NetObj(s, n, npins), node_next_(0), node_prev_(0), design_(0) { } NetNode::~NetNode() { if (design_) design_->del_node(this); } NetBranch::NetBranch(ivl_discipline_t dis) : NetPins(2), IslandBranch(dis) { pin(0).set_dir(Link::PASSIVE); pin(1).set_dir(Link::PASSIVE); } NetBranch::~NetBranch() { } NetBus::NetBus(NetScope*s, unsigned pin_count__) : NetObj(s, perm_string::literal(""), pin_count__) { for (unsigned idx = 0 ; idx &unpacked) { unsigned long sum = 1; for (list::const_iterator cur = unpacked.begin() ; cur != unpacked.end() ; ++cur) { // Special case: If there are any undefined dimensions, // then give up on trying to create pins for the net. if (! cur->defined()) return 0; sum *= cur->width(); } if (sum >= UINT_MAX) return 0; return sum; } template static unsigned calculate_count(T*type) { long wid = type->packed_width(); if (wid >= 0) return wid; else return 1; } void NetNet::calculate_slice_widths_from_packed_dims_(void) { ivl_assert(*this, net_type_); slice_dims_ = net_type_->slice_dimensions(); // Special case: There are no actual packed dimensions, so // build up a fake dimension of "1". if (slice_dims_.empty()) { slice_wids_.resize(1); slice_wids_[0] = net_type_->packed_width(); return; } slice_wids_.resize(slice_dims_.size()); ivl_assert(*this, ! slice_wids_.empty()); slice_wids_[0] = netrange_width(slice_dims_); vector::const_iterator cur = slice_dims_.begin(); for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1) { slice_wids_[idx] = slice_wids_[idx-1] / cur->width(); } } const list NetNet::not_an_array; NetNet::NetNet(NetScope*s, perm_string n, Type t, const list&unpacked, ivl_type_t use_net_type) : NetObj(s, n, calculate_count(unpacked)), type_(t), port_type_(NOT_A_PORT), local_flag_(false), net_type_(use_net_type), discipline_(0), unpacked_dims_(unpacked.size()), eref_count_(0), lref_count_(0) { calculate_slice_widths_from_packed_dims_(); size_t idx = 0; for (list::const_iterator cur = unpacked.begin() ; cur != unpacked.end() ; ++cur, idx += 1) { unpacked_dims_[idx] = *cur; } assert(idx == unpacked_dims_.size()); ivl_assert(*this, s); if (pin_count() == 0) { cerr << "Invalid array dimensions: " << unpacked << endl; ivl_assert(*this, 0); } initialize_dir_(); s->add_signal(this); } /* * When we create a netnet for a packed struct, create a single * vector with the msb_/lsb_ chosen to name enough bits for the entire * packed structure. */ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty) : NetObj(s, n, 1), type_(t), port_type_(NOT_A_PORT), local_flag_(false), net_type_(ty), discipline_(0), eref_count_(0), lref_count_(0) { //XXXX packed_dims_.push_back(netrange_t(calculate_count(ty)-1, 0)); calculate_slice_widths_from_packed_dims_(); initialize_dir_(); s->add_signal(this); } NetNet::NetNet(NetScope*s, perm_string n, Type t, netdarray_t*ty) : NetObj(s, n, 1), type_(t), port_type_(NOT_A_PORT), local_flag_(false), net_type_(ty), discipline_(0), eref_count_(0), lref_count_(0) { initialize_dir_(); s->add_signal(this); } NetNet::NetNet(NetScope*s, perm_string n, Type t, netvector_t*ty) : NetObj(s, n, 1), type_(t), port_type_(NOT_A_PORT), local_flag_(false), net_type_(ty), discipline_(0), eref_count_(0), lref_count_(0) { calculate_slice_widths_from_packed_dims_(); initialize_dir_(); s->add_signal(this); } NetNet::~NetNet() { if (eref_count_ > 0) { cerr << get_fileline() << ": internal error: attempt to delete " << "signal ``" << name() << "'' which has " << "expression references." << endl; dump_net(cerr, 4); } assert(eref_count_ == 0); if (lref_count_ > 0) { cerr << get_fileline() << ": internal error: attempt to delete " << "signal ``" << name() << "'' which has " << "assign references." << endl; dump_net(cerr, 4); } assert(lref_count_ == 0); if (scope()) scope()->rem_signal(this); } NetNet::Type NetNet::type() const { return type_; } void NetNet::type(NetNet::Type t) { if (type_ == t) return; type_ = t; initialize_dir_(); } NetNet::PortType NetNet::port_type() const { return port_type_; } void NetNet::port_type(NetNet::PortType t) { port_type_ = t; } int NetNet::get_module_port_index() const { return port_index_; } void NetNet::set_module_port_index(unsigned idx) { port_index_ = idx; assert( port_index_ >= 0 ); } ivl_variable_type_t NetNet::data_type() const { ivl_assert(*this, net_type_); return net_type_->base_type(); } bool NetNet::get_signed() const { ivl_assert(*this, net_type_); return net_type_->get_signed(); } bool NetNet::get_isint() const { if (const netvector_t*vec = dynamic_cast (net_type_)) return vec->get_isint(); else return false; } bool NetNet::get_scalar() const { if (const netvector_t*vec = dynamic_cast (net_type_)) return vec->get_scalar(); else return false; } const netenum_t*NetNet::enumeration(void) const { return dynamic_cast (net_type_); } const netstruct_t*NetNet::struct_type(void) const { ivl_type_t cur_type = net_type_; while (cur_type) { if (const netdarray_t*da = dynamic_cast (cur_type)) { cur_type = da->element_type(); continue; } if (const netparray_t*da = dynamic_cast (cur_type)) { cur_type = da->element_type(); continue; } if (const netstruct_t*st = dynamic_cast (cur_type)) return st; else return 0; } assert(0); return 0; } const netdarray_t* NetNet::darray_type(void) const { return dynamic_cast (net_type_); } const netqueue_t* NetNet::queue_type(void) const { return dynamic_cast (net_type_); } const netclass_t* NetNet::class_type(void) const { return dynamic_cast (net_type_); } /* * "depth" is the number of index expressions that the user is using * to index this identifier. So consider if Net was declared like so: * * reg [5:0][3:0] foo; * * In this case, slice_width(2) == 1 (slice_width(N) where N is the * number of dimensions will always be 1.) and represents * $bits(foo[a][b]). Then, slice_width(1)==4 ($bits(foo[a]) and slice_width(0)==24. * * NOTE: The caller should already have accounted for unpacked * dimensions. The "depth" is only for the packed dimensions. */ unsigned long NetNet::slice_width(size_t depth) const { if (depth > slice_wids_.size()) return 0; if (depth == slice_wids_.size()) return 1; return slice_wids_[depth]; } ivl_discipline_t NetNet::get_discipline() const { return discipline_; } void NetNet::set_discipline(ivl_discipline_t dis) { ivl_assert(*this, discipline_ == 0); discipline_ = dis; } bool NetNet::sb_is_valid(const list&indices, long sb) const { ivl_assert(*this, indices.size()+1 == packed_dims().size()); assert(packed_dims().size() == 1); const netrange_t&rng = packed_dims().back(); if (rng.get_msb() >= rng.get_lsb()) return (sb <= rng.get_msb()) && (sb >= rng.get_lsb()); else return (sb <= rng.get_lsb()) && (sb >= rng.get_msb()); } long NetNet::sb_to_idx(const list&indices, long sb) const { ivl_assert(*this, indices.size()+1 == packed_dims().size()); vector::const_iterator pcur = packed_dims().end(); -- pcur; long acc_off; long acc_wid = pcur->width(); if (pcur->get_msb() >= pcur->get_lsb()) acc_off = sb - pcur->get_lsb(); else acc_off = pcur->get_lsb() - sb; // The acc_off is the position within the innermost // dimension. If this is a multi-dimension packed array then // we need to add in the canonical address of the current slice. if (! indices.empty()) { list::const_iterator icur = indices.end(); do { -- icur; -- pcur; long tmp_off; if (pcur->get_msb() >= pcur->get_lsb()) tmp_off = *icur - pcur->get_lsb(); else tmp_off = pcur->get_lsb() - *icur; acc_off += tmp_off * acc_wid; acc_wid *= pcur->width(); } while (icur != indices.begin()); } return acc_off; } bool NetNet::sb_to_slice(const list&indices, long sb, long&loff, unsigned long&lwid) const { ivl_assert(*this, indices.size() < packed_dims().size()); return prefix_to_slice(packed_dims(), indices, sb, loff, lwid); } unsigned NetNet::unpacked_count() const { unsigned c = 1; for (size_t idx = 0 ; idx < unpacked_dims_.size() ; idx += 1) { c *= unpacked_dims_[idx].width(); } return c; } void NetNet::incr_eref() { eref_count_ += 1; } void NetNet::decr_eref() { assert(eref_count_ > 0); eref_count_ -= 1; } unsigned NetNet::peek_eref() const { return eref_count_; } /* * Test each of the bits in the range, and set them. If any bits are * already set then return true. */ bool NetNet::test_and_set_part_driver(unsigned pmsb, unsigned plsb, int widx) { if (lref_mask_.empty()) lref_mask_.resize(vector_width() * pin_count()); // If indexing a word that doesn't exist, then pretend this is // never driven. if (widx < 0) return false; if (widx >= (int)pin_count()) return false; bool rc = false; unsigned word_base = vector_width() * widx; for (unsigned idx = plsb ; idx <= pmsb ; idx += 1) { if (lref_mask_[idx+word_base]) rc = true; else lref_mask_[idx+word_base] = true; } return rc; } void NetNet::incr_lref() { lref_count_ += 1; } void NetNet::decr_lref() { assert(lref_count_ > 0); lref_count_ -= 1; } unsigned NetNet::get_refs() const { return lref_count_ + eref_count_; } void NetNet::add_delay_path(NetDelaySrc*path) { delay_paths_.push_back(path); } unsigned NetNet::delay_paths(void)const { return delay_paths_.size(); } const NetDelaySrc* NetNet::delay_path(unsigned idx) const { assert(idx < delay_paths_.size()); return delay_paths_[idx]; } NetPartSelect::NetPartSelect(NetNet*sig, unsigned off, unsigned wid, NetPartSelect::dir_t dir__, bool signed_flag__) : NetNode(sig->scope(), sig->scope()->local_symbol(), 2), off_(off), wid_(wid), dir_(dir__), signed_flag_(signed_flag__) { set_line(*sig); switch (dir_) { case NetPartSelect::VP: pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); break; case NetPartSelect::PV: pin(0).set_dir(Link::INPUT); pin(1).set_dir(Link::OUTPUT); break; } connect(pin(1), sig->pin(0)); } NetPartSelect::NetPartSelect(NetNet*sig, NetNet*sel, unsigned wid, bool signed_flag__) : NetNode(sig->scope(), sig->scope()->local_symbol(), 3), off_(0), wid_(wid), dir_(VP), signed_flag_(signed_flag__) { switch (dir_) { case NetPartSelect::VP: pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); break; case NetPartSelect::PV: /* Only a vector to part can be a variable select. */ assert(0); } pin(2).set_dir(Link::INPUT); connect(pin(1), sig->pin(0)); connect(pin(2), sel->pin(0)); } NetPartSelect::~NetPartSelect() { } unsigned NetPartSelect::width() const { return wid_; } unsigned NetPartSelect::base() const { return off_; } NetSubstitute::NetSubstitute(NetNet*sig, NetNet*sub, unsigned wid, unsigned off) : NetNode(sig->scope(), sig->scope()->local_symbol(), 3), wid_(wid), off_(off) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); pin(2).set_dir(Link::INPUT); connect(pin(1), sig->pin(0)); connect(pin(2), sub->pin(0)); } NetSubstitute::~NetSubstitute() { } NetProc::NetProc() : next_(0) { } NetProc::~NetProc() { } NetProcTop::NetProcTop(NetScope*s, ivl_process_type_t t, NetProc*st) : type_(t), statement_(st), scope_(s) { } NetProcTop::~NetProcTop() { delete statement_; } NetProc* NetProcTop::statement() { return statement_; } const NetProc* NetProcTop::statement() const { return statement_; } NetScope* NetProcTop::scope() { return scope_; } const NetScope* NetProcTop::scope() const { return scope_; } NetAnalogTop::NetAnalogTop(NetScope*scope__, ivl_process_type_t t, NetProc*st) : type_(t), statement_(st), scope_(scope__) { next_ = 0; } NetAnalogTop::~NetAnalogTop() { } NetProc* NetAnalogTop::statement() { return statement_; } const NetProc* NetAnalogTop::statement() const { return statement_; } NetScope* NetAnalogTop::scope() { return scope_; } const NetScope* NetAnalogTop::scope() const { return scope_; } NetCastInt2::NetCastInt2(NetScope*scope__, perm_string n, unsigned width__) : NetNode(scope__, n, 2), width_(width__) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetCastInt4::NetCastInt4(NetScope*scope__, perm_string n, unsigned width__) : NetNode(scope__, n, 2), width_(width__) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetCastReal::NetCastReal(NetScope*scope__, perm_string n, bool signed_flag__) : NetNode(scope__, n, 2), signed_flag_(signed_flag__) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetConcat::NetConcat(NetScope*scope__, perm_string n, unsigned wid, unsigned cnt, bool trans_flag) : NetNode(scope__, n, cnt+1), width_(wid), transparent_(trans_flag) { pin(0).set_dir(Link::OUTPUT); for (unsigned idx = 1 ; idx < cnt+1 ; idx += 1) { pin(idx).set_dir(Link::INPUT); } } NetConcat::~NetConcat() { } unsigned NetConcat::width() const { return width_; } NetReplicate::NetReplicate(NetScope*scope__, perm_string n, unsigned wid, unsigned rpt) : NetNode(scope__, n, 2), width_(wid), repeat_(rpt) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetReplicate::~NetReplicate() { } unsigned NetReplicate::width() const { return width_; } unsigned NetReplicate::repeat() const { return repeat_; } /* * The NetFF class represents an LPM_FF device. The pinout is assigned * like so: * 0 -- Clock * 1 -- Enable * 2 -- Aset * 3 -- Aclr * 4 -- Sset * 5 -- Sclr * 6 -- Data * 7 -- Q * ... */ NetFF::NetFF(NetScope*s, perm_string n, bool negedge__, unsigned width__) : NetNode(s, n, 8), negedge_(negedge__), width_(width__) { pin_Clock().set_dir(Link::INPUT); pin_Enable().set_dir(Link::INPUT); pin_Aset().set_dir(Link::INPUT); pin_Aclr().set_dir(Link::INPUT); pin_Sset().set_dir(Link::INPUT); pin_Sclr().set_dir(Link::INPUT); pin_Data().set_dir(Link::INPUT); pin_Q().set_dir(Link::OUTPUT); } NetFF::~NetFF() { } bool NetFF::is_negedge() const { return negedge_; } unsigned NetFF::width() const { return width_; } Link& NetFF::pin_Clock() { return pin(0); } const Link& NetFF::pin_Clock() const { return pin(0); } Link& NetFF::pin_Enable() { return pin(1); } const Link& NetFF::pin_Enable() const { return pin(1); } Link& NetFF::pin_Aset() { return pin(2); } const Link& NetFF::pin_Aset() const { return pin(2); } Link& NetFF::pin_Aclr() { return pin(3); } const Link& NetFF::pin_Aclr() const { return pin(3); } Link& NetFF::pin_Sset() { return pin(4); } const Link& NetFF::pin_Sset() const { return pin(4); } Link& NetFF::pin_Sclr() { return pin(5); } const Link& NetFF::pin_Sclr() const { return pin(5); } Link& NetFF::pin_Data() { return pin(6); } const Link& NetFF::pin_Data() const { return pin(6); } Link& NetFF::pin_Q() { return pin(7); } const Link& NetFF::pin_Q() const { return pin(7); } void NetFF::aset_value(const verinum&val) { aset_value_ = val; } const verinum& NetFF::aset_value() const { return aset_value_; } void NetFF::sset_value(const verinum&val) { sset_value_ = val; } const verinum& NetFF::sset_value() const { return sset_value_; } NetAbs::NetAbs(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 2), width_(w) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetAbs::~NetAbs() { } unsigned NetAbs::width() const { return width_; } /* * The NetAddSub class represents an LPM_ADD_SUB device. The pinout is * assigned like so: * 0 -- Cout * 1 -- DataA (normally a vector) * 2 -- DataB (normally a vector) * 3 -- Result (normally a vector) */ NetAddSub::NetAddSub(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 4), width_(w) { pin(0).set_dir(Link::OUTPUT); // Cout pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB pin(3).set_dir(Link::OUTPUT); // Result } NetAddSub::~NetAddSub() { } unsigned NetAddSub::width()const { return width_; } Link& NetAddSub::pin_Cout() { return pin(0); } const Link& NetAddSub::pin_Cout() const { return pin(0); } Link& NetAddSub::pin_DataA() { return pin(1); } const Link& NetAddSub::pin_DataA() const { return pin(1); } Link& NetAddSub::pin_DataB() { return pin(2); } const Link& NetAddSub::pin_DataB() const { return pin(2); } Link& NetAddSub::pin_Result() { return pin(3); } const Link& NetAddSub::pin_Result() const { return pin(3); } NetArrayDq::NetArrayDq(NetScope*s, perm_string n, NetNet*mem__, unsigned awid) : NetNode(s, n, 2), mem_(mem__), awidth_(awid) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // Address // Increment the expression reference count for the target // memory so that it is not deleted underneath me. mem_->incr_eref(); } NetArrayDq::~NetArrayDq() { } unsigned NetArrayDq::width() const { return mem_->vector_width(); } unsigned NetArrayDq::awidth() const { return awidth_; } const NetNet* NetArrayDq::mem() const { return mem_; } Link& NetArrayDq::pin_Result() { return pin(0); } Link& NetArrayDq::pin_Address() { return pin(1); } const Link& NetArrayDq::pin_Result() const { return pin(0); } const Link& NetArrayDq::pin_Address() const { return pin(1); } /* * The pinout for the NetCLShift is: * 0 -- Result * 1 -- Data * 2 -- Distance */ NetCLShift::NetCLShift(NetScope*s, perm_string n, unsigned width__, unsigned width_dist__, bool right_flag__, bool signed_flag__) : NetNode(s, n, 3), width_(width__), width_dist_(width_dist__), right_flag_(right_flag__), signed_flag_(signed_flag__) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // Data pin(2).set_dir(Link::INPUT); // Distance } NetCLShift::~NetCLShift() { } unsigned NetCLShift::width() const { return width_; } unsigned NetCLShift::width_dist() const { return width_dist_; } bool NetCLShift::right_flag() const { return right_flag_; } bool NetCLShift::signed_flag() const { return signed_flag_; } Link& NetCLShift::pin_Data() { return pin(1); } const Link& NetCLShift::pin_Data() const { return pin(1); } Link& NetCLShift::pin_Result() { return pin(0); } const Link& NetCLShift::pin_Result() const { return pin(0); } Link& NetCLShift::pin_Distance() { return pin(2); } const Link& NetCLShift::pin_Distance() const { return pin(2); } NetCompare::NetCompare(NetScope*s, perm_string n, unsigned wi) : NetNode(s, n, 8), width_(wi) { signed_flag_ = false; pin(0).set_dir(Link::OUTPUT); // AGB pin(1).set_dir(Link::OUTPUT); // AGEB pin(2).set_dir(Link::OUTPUT); // AEB pin(3).set_dir(Link::OUTPUT); // ANEB pin(4).set_dir(Link::OUTPUT); // ALB pin(5).set_dir(Link::OUTPUT); // ALEB pin(6).set_dir(Link::INPUT); // DataA pin(7).set_dir(Link::INPUT); // DataB } NetCompare::~NetCompare() { } unsigned NetCompare::width() const { return width_; } bool NetCompare::get_signed() const { return signed_flag_; } void NetCompare::set_signed(bool flag) { signed_flag_ = flag; } Link& NetCompare::pin_AGB() { return pin(0); } const Link& NetCompare::pin_AGB() const { return pin(0); } Link& NetCompare::pin_AGEB() { return pin(1); } const Link& NetCompare::pin_AGEB() const { return pin(1); } Link& NetCompare::pin_AEB() { return pin(2); } const Link& NetCompare::pin_AEB() const { return pin(2); } Link& NetCompare::pin_ANEB() { return pin(3); } const Link& NetCompare::pin_ANEB() const { return pin(3); } Link& NetCompare::pin_ALB() { return pin(4); } const Link& NetCompare::pin_ALB() const { return pin(4); } Link& NetCompare::pin_ALEB() { return pin(5); } const Link& NetCompare::pin_ALEB() const { return pin(5); } Link& NetCompare::pin_DataA() { return pin(6); } const Link& NetCompare::pin_DataA() const { return pin(6); } Link& NetCompare::pin_DataB() { return pin(7); } const Link& NetCompare::pin_DataB() const { return pin(7); } NetDivide::NetDivide(NetScope*sc, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(sc, n, 3), width_r_(wr), width_a_(wa), width_b_(wb), signed_flag_(false) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB } NetDivide::~NetDivide() { } unsigned NetDivide::width_r() const { return width_r_; } unsigned NetDivide::width_a() const { return width_a_; } unsigned NetDivide::width_b() const { return width_b_; } void NetDivide::set_signed(bool flag) { signed_flag_ = flag; } bool NetDivide::get_signed() const { return signed_flag_; } Link& NetDivide::pin_Result() { return pin(0); } const Link& NetDivide::pin_Result() const { return pin(0); } Link& NetDivide::pin_DataA() { return pin(1); } const Link& NetDivide::pin_DataA() const { return pin(1); } Link& NetDivide::pin_DataB() { return pin(2); } const Link& NetDivide::pin_DataB() const { return pin(2); } NetLiteral::NetLiteral(NetScope*sc, perm_string n, const verireal&val) : NetNode(sc, n, 1), real_(val) { pin(0).set_dir(Link::OUTPUT); } NetLiteral::~NetLiteral() { } ivl_variable_type_t NetLiteral::data_type() const { return IVL_VT_REAL; } const verireal& NetLiteral::value_real() const { return real_; } NetMult::NetMult(NetScope*sc, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(sc, n, 3), signed_(false), width_r_(wr), width_a_(wa), width_b_(wb) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB } NetMult::~NetMult() { } void NetMult::set_signed(bool flag) { signed_ = flag; } bool NetMult::get_signed() const { return signed_; } unsigned NetMult::width_r() const { return width_r_; } unsigned NetMult::width_a() const { return width_a_; } unsigned NetMult::width_b() const { return width_b_; } Link& NetMult::pin_Result() { return pin(0); } const Link& NetMult::pin_Result() const { return pin(0); } Link& NetMult::pin_DataA() { return pin(1); } const Link& NetMult::pin_DataA() const { return pin(1); } Link& NetMult::pin_DataB() { return pin(2); } const Link& NetMult::pin_DataB() const { return pin(2); } NetPow::NetPow(NetScope*sc, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(sc, n, 3), signed_(false), width_r_(wr), width_a_(wa), width_b_(wb) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB } NetPow::~NetPow() { } void NetPow::set_signed(bool flag) { signed_ = flag; } bool NetPow::get_signed() const { return signed_; } unsigned NetPow::width_r() const { return width_r_; } unsigned NetPow::width_a() const { return width_a_; } unsigned NetPow::width_b() const { return width_b_; } Link& NetPow::pin_Result() { return pin(0); } const Link& NetPow::pin_Result() const { return pin(0); } Link& NetPow::pin_DataA() { return pin(1); } const Link& NetPow::pin_DataA() const { return pin(1); } Link& NetPow::pin_DataB() { return pin(2); } const Link& NetPow::pin_DataB() const { return pin(2); } /* * The NetMux class represents an LPM_MUX device. The pinout is assigned * like so: * 0 -- Result * 1 -- Sel * 2+N -- Data[N] (N is the size of the mux) */ NetMux::NetMux(NetScope*s, perm_string n, unsigned wi, unsigned si, unsigned sw) : NetNode(s, n, 2+si), width_(wi), size_(si), swidth_(sw) { pin(0).set_dir(Link::OUTPUT); // Q pin(1).set_dir(Link::INPUT); // Sel for (unsigned idx = 0 ; idx < size_ ; idx += 1) { pin_Data(idx).set_dir(Link::INPUT); // Data[idx] } } NetMux::~NetMux() { } unsigned NetMux::width()const { return width_; } unsigned NetMux::size() const { return size_; } unsigned NetMux::sel_width() const { return swidth_; } Link& NetMux::pin_Result() { return pin(0); } const Link& NetMux::pin_Result() const { return pin(0); } Link& NetMux::pin_Sel() { return pin(1); } const Link& NetMux::pin_Sel() const { return pin(1); } Link& NetMux::pin_Data(unsigned s) { assert(s < size_); return pin(2+s); } const Link& NetMux::pin_Data(unsigned s) const { assert(s < size_); return pin(2+s); } NetSignExtend::NetSignExtend(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 2), width_(w) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetSignExtend::~NetSignExtend() { } unsigned NetSignExtend::width() const { return width_; } NetBUFZ::NetBUFZ(NetScope*s, perm_string n, unsigned w, bool trans) : NetNode(s, n, 2), width_(w), transparent_(trans) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetBUFZ::~NetBUFZ() { } unsigned NetBUFZ::width() const { return width_; } NetCaseCmp::NetCaseCmp(NetScope*s, perm_string n, unsigned wid, kind_t k) : NetNode(s, n, 3), width_(wid), kind_(k) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); pin(2).set_dir(Link::INPUT); } NetCaseCmp::~NetCaseCmp() { } unsigned NetCaseCmp::width() const { return width_; } NetCondit::NetCondit(NetExpr*ex, NetProc*i, NetProc*e) : expr_(ex), if_(i), else_(e) { } NetCondit::~NetCondit() { delete expr_; delete if_; delete else_; } const NetExpr* NetCondit::expr() const { return expr_; } NetExpr* NetCondit::expr() { return expr_; } void NetCondit::set_expr(NetExpr*ex) { delete expr_; expr_ = ex; } NetProc* NetCondit::if_clause() { return if_; } NetProc* NetCondit::else_clause() { return else_; } NetConst::NetConst(NetScope*s, perm_string n, verinum::V v) : NetNode(s, n, 1), value_(v, 1) { pin(0).set_dir(Link::OUTPUT); } NetConst::NetConst(NetScope*s, perm_string n, const verinum&val) : NetNode(s, n, 1), value_(val) { pin(0).set_dir(Link::OUTPUT); } NetConst::~NetConst() { } verinum::V NetConst::value(unsigned idx) const { assert(idx < width()); return value_[idx]; } NetBaseDef::NetBaseDef(NetScope*s, const vector&po, const std::vector&pd) : scope_(s), ports_(po), pdefaults_(pd) { proc_ = 0; } NetBaseDef::~NetBaseDef() { } const NetScope* NetBaseDef::scope() const { return scope_; } NetScope*NetBaseDef::scope() { return scope_; } unsigned NetBaseDef::port_count() const { return ports_.size(); } NetNet* NetBaseDef::port(unsigned idx) const { assert(idx < ports_.size()); return ports_[idx]; } NetExpr* NetBaseDef::port_defe(unsigned idx) const { assert(idx < pdefaults_.size()); return pdefaults_[idx]; } void NetBaseDef::set_proc(NetProc*st) { assert(proc_ == 0); assert(st != 0); proc_ = st; } const NetProc* NetBaseDef::proc() const { return proc_; } NetFuncDef::NetFuncDef(NetScope*s, NetNet*result, const vector&po, const vector&pd) : NetBaseDef(s, po, pd), result_sig_(result) { } NetFuncDef::~NetFuncDef() { } const NetNet* NetFuncDef::return_sig() const { return result_sig_; } NetSTask::NetSTask(const char*na, ivl_sfunc_as_task_t sfat, const vector&pa) : name_(0), sfunc_as_task_(sfat), parms_(pa) { name_ = lex_strings.add(na); assert(name_[0] == '$'); } NetSTask::~NetSTask() { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; /* The name_ string is perm-allocated in lex_strings. */ } const char*NetSTask::name() const { return name_; } ivl_sfunc_as_task_t NetSTask::sfunc_as_task() const { return sfunc_as_task_; } unsigned NetSTask::nparms() const { return parms_.size(); } const NetExpr* NetSTask::parm(unsigned idx) const { return parms_[idx]; } NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, vector&p, bool nc) : scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc) { expr_width(result_sig_->expr_width()); } NetEUFunc::~NetEUFunc() { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; } #if 0 const string NetEUFunc::name() const { return func_->name(); } #endif const NetESignal*NetEUFunc::result_sig() const { return result_sig_; } unsigned NetEUFunc::parm_count() const { return parms_.size(); } const NetExpr* NetEUFunc::parm(unsigned idx) const { assert(idx < parms_.size()); return parms_[idx]; } const NetScope* NetEUFunc::func() const { return func_; } ivl_variable_type_t NetEUFunc::expr_type() const { if (result_sig_) return result_sig_->expr_type(); return IVL_VT_VOID; } NetUTask::NetUTask(NetScope*def) : task_(def) { } NetUTask::~NetUTask() { } const NetScope* NetUTask::task() const { return task_; } NetAlloc::NetAlloc(NetScope*scope__) : scope_(scope__) { } NetAlloc::~NetAlloc() { } #if 0 const string NetAlloc::name() const { return scope_->name(); } #endif const NetScope* NetAlloc::scope() const { return scope_; } NetFree::NetFree(NetScope*scope__) : scope_(scope__) { } NetFree::~NetFree() { } #if 0 const string NetFree::name() const { return scope_->name(); } #endif const NetScope* NetFree::scope() const { return scope_; } /* * Create a bitwise operator node from the opcode and the left and * right expressions. */ NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBBits::~NetEBBits() { } NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : op_(op__), left_(l), right_(r) { expr_width(wid); cast_signed_base_(signed_flag); } NetEBinary::~NetEBinary() { delete left_; delete right_; } bool NetEBinary::has_width() const { return left_->has_width() && right_->has_width(); } NetEBLogic::NetEBLogic(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r, 1, false) { } NetEBLogic::~NetEBLogic() { } NetEConst::NetEConst(const verinum&val) : NetExpr(val.len()), value_(val) { cast_signed_base_(value_.has_sign()); } NetEConst::~NetEConst() { } void NetEConst::cast_signed(bool flag) { cast_signed_base_(flag); value_.has_sign(flag); } const verinum& NetEConst::value() const { return value_; } bool NetEConst::has_width() const { return value_.has_len(); } ivl_variable_type_t NetEConst::expr_type() const { if (value_.len() == 0) return IVL_VT_LOGIC; if (value_.is_string()) return IVL_VT_BOOL; if (value_.is_defined()) return IVL_VT_BOOL; return IVL_VT_LOGIC; } void NetEConst::trim() { if (value_.is_string()) return; value_.has_len(false); value_ = trim_vnum(value_); expr_width(value_.len()); } NetEConstParam::NetEConstParam(NetScope*s, perm_string n, const verinum&v) : NetEConst(v), scope_(s), name_(n) { cast_signed_base_(v.has_sign()); } NetEConstParam::~NetEConstParam() { } perm_string NetEConstParam::name() const { return name_; } const NetScope* NetEConstParam::scope() const { return scope_; } NetEEvent::NetEEvent(NetEvent*e) : event_(e) { e->exprref_ += 1; } NetEEvent::~NetEEvent() { } const NetEvent* NetEEvent::event() const { return event_; } NetEScope::NetEScope(NetScope*s) : scope_(s) { } NetEScope::~NetEScope() { } const NetScope* NetEScope::scope() const { return scope_; } NetESignal::NetESignal(NetNet*n) : NetExpr(n->vector_width()), net_(n), enum_type_(n->enumeration()), word_(0) { net_->incr_eref(); set_line(*n); cast_signed_base_(net_->get_signed()); } NetESignal::NetESignal(NetNet*n, NetExpr*w) : NetExpr(n->vector_width()), net_(n), word_(w) { net_->incr_eref(); set_line(*n); cast_signed_base_(net_->get_signed()); } NetESignal::~NetESignal() { net_->decr_eref(); } perm_string NetESignal::name() const { return net_->name(); } const netenum_t* NetESignal::enumeration() const { return enum_type_; } const NetExpr* NetESignal::word_index() const { return word_; } unsigned NetESignal::vector_width() const { return net_->vector_width(); } const NetNet* NetESignal::sig() const { return net_; } NetNet* NetESignal::sig() { return net_; } /* * The lsi() and msi() methods should be removed from the NetESignal * class, to be replaced with packed dimensions aware methods of * getting at dimensions. */ long NetESignal::lsi() const { const vector&packed = net_->packed_dims(); ivl_assert(*this, packed.size() == 1); return packed.back().get_lsb(); } long NetESignal::msi() const { const vector&packed = net_->packed_dims(); ivl_assert(*this, packed.size() == 1); return packed.back().get_msb(); } ivl_variable_type_t NetESignal::expr_type() const { if (net_->darray_type()) return IVL_VT_DARRAY; else return net_->data_type(); } /* * Make a ternary operator from all the sub-expressions. The condition * expression is self-determined, but the true and false expressions * should have the same width. NOTE: This matching of the widths really * has to be done in elaboration. */ NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag) : cond_(c), true_val_(t), false_val_(f) { expr_width(wid); cast_signed_base_(signed_flag); } NetETernary::~NetETernary() { delete cond_; delete true_val_; delete false_val_; } const NetExpr* NetETernary::cond_expr() const { return cond_; } const NetExpr* NetETernary::true_expr() const { return true_val_; } const NetExpr* NetETernary::false_expr() const { return false_val_; } ivl_variable_type_t NetETernary::expr_type() const { ivl_assert(*this, true_val_); ivl_assert(*this, false_val_); ivl_variable_type_t tru = true_val_->expr_type(); ivl_variable_type_t fal = false_val_->expr_type(); ivl_variable_type_t sel = cond_->expr_type(); if (tru == IVL_VT_LOGIC && fal == IVL_VT_BOOL) return IVL_VT_LOGIC; if (tru == IVL_VT_BOOL && fal == IVL_VT_LOGIC) return IVL_VT_LOGIC; if (sel == IVL_VT_LOGIC && (tru == IVL_VT_LOGIC || tru == IVL_VT_BOOL) && (fal == IVL_VT_LOGIC || fal == IVL_VT_BOOL)) return IVL_VT_LOGIC; if (tru == IVL_VT_REAL && (fal == IVL_VT_LOGIC || fal == IVL_VT_BOOL)) return IVL_VT_REAL; if (fal == IVL_VT_REAL && (tru == IVL_VT_LOGIC || tru == IVL_VT_BOOL)) return IVL_VT_REAL; if (tru != fal) { cerr << get_fileline() << ": internal error:" << " Unexpected ?: type clash:" << " tru=" << tru << ", fal=" << fal << endl; } ivl_assert(*this, tru == fal); return tru; } NetEUnary::NetEUnary(char op__, NetExpr*ex, unsigned wid, bool signed_flag) : NetExpr(wid), op_(op__), expr_(ex) { cast_signed_base_(signed_flag); } NetEUnary::~NetEUnary() { delete expr_; } ivl_variable_type_t NetEUnary::expr_type() const { return expr_->expr_type(); } NetEUBits::NetEUBits(char op__, NetExpr*ex, unsigned wid, bool signed_flag) : NetEUnary(op__, ex, wid, signed_flag) { } NetEUBits::~NetEUBits() { } ivl_variable_type_t NetEUBits::expr_type() const { return expr_->expr_type(); } NetEUReduce::NetEUReduce(char op__, NetExpr*ex) : NetEUnary(op__, ex, 1, false) { } NetEUReduce::~NetEUReduce() { } ivl_variable_type_t NetEUReduce::expr_type() const { return expr_->expr_type(); } NetECast::NetECast(char op__, NetExpr*ex, unsigned wid, bool signed_flag) : NetEUnary(op__, ex, wid, signed_flag) { } NetECast::~NetECast() { } ivl_variable_type_t NetECast::expr_type() const { ivl_variable_type_t ret = IVL_VT_NO_TYPE; switch (op_) { case 'v': ret = IVL_VT_LOGIC; break; case 'r': ret = IVL_VT_REAL; break; case '2': ret = IVL_VT_BOOL; break; default: assert(0); } return ret; } NetLogic::NetLogic(NetScope*s, perm_string n, unsigned pins, TYPE t, unsigned wid, bool is_cassign__) : NetNode(s, n, pins), type_(t), width_(wid), is_cassign_(is_cassign__) { pin(0).set_dir(Link::OUTPUT); for (unsigned idx = 1 ; idx < pins ; idx += 1) { pin(idx).set_dir(Link::INPUT); } } NetLogic::TYPE NetLogic::type() const { return type_; } unsigned NetLogic::width() const { return width_; } bool NetLogic::is_cassign() const { return is_cassign_; } NetUReduce::NetUReduce(NetScope*scope__, perm_string n, NetUReduce::TYPE t, unsigned wid) : NetNode(scope__, n, 2), type_(t), width_(wid) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetUReduce::TYPE NetUReduce::type() const { return type_; } unsigned NetUReduce::width() const { return width_; } NetTaskDef::NetTaskDef(NetScope*n, const vector&po, const vector&pd) : NetBaseDef(n, po, pd) { } NetTaskDef::~NetTaskDef() { delete proc_; } /* * These are the delay_type() functions. They are used to determine * the type of delay for the given object. */ /* * This function implements the following table: * * in_A in_B out * NO NO NO * NO ZERO ZERO * NO POS POS * NO DEF POS * ZERO NO ZERO * ZERO ZERO ZERO * ZERO POS POS * ZERO DEF POS * POS NO POS * POS ZERO POS * POS POS POS * POS DEF POS * DEF NO POS * DEF ZERO POS * DEF POS POS * DEF DEF DEF * * It is used to combine two delay values. */ static DelayType combine_delays(const DelayType a, const DelayType b) { /* The default is POSSIBLE_DELAY. */ DelayType result = POSSIBLE_DELAY; /* If both are no or zero delay then we return ZERO_DELAY. */ if ((a == NO_DELAY || a == ZERO_DELAY) && (b == NO_DELAY || b == ZERO_DELAY)) { result = ZERO_DELAY; } /* Except if both are no delay then we return NO_DELAY. */ if (a == NO_DELAY && b == NO_DELAY) { result = NO_DELAY; } /* If both are definite delay then we return DEFINITE_DELAY. */ if (a == DEFINITE_DELAY && b == DEFINITE_DELAY) { result = DEFINITE_DELAY; } return result; } /* * This is used to see what we can find out about the delay when it * is given as an expression. We also use this for loop expressions. */ static DelayType delay_type_from_expr(const NetExpr*expr) { DelayType result = POSSIBLE_DELAY; if (const NetEConst*e = dynamic_cast(expr)) { if (e->value().is_zero()) result = ZERO_DELAY; else result = DEFINITE_DELAY; } if (const NetECReal*e = dynamic_cast(expr)) { if (e->value().as_double() == 0.0) result = ZERO_DELAY; else result = DEFINITE_DELAY; } return result; } /* * The looping structures can use the same basic code so put it here * instead of duplicating it for each one (repeat and while). */ static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc) { DelayType result; switch (delay_type_from_expr(expr)) { /* We have a constant false expression so the body never runs. */ case ZERO_DELAY: result = NO_DELAY; break; /* We have a constant true expression so the body always runs. */ case DEFINITE_DELAY: result = proc->delay_type(); break; /* We don't know if the body will run so reduce a DEFINITE_DELAY * to a POSSIBLE_DELAY. All other stay the same. */ case POSSIBLE_DELAY: result = combine_delays(NO_DELAY, proc->delay_type()); break; /* This should never happen since delay_type_from_expr() only * returns three different values. */ default: result = NO_DELAY; assert(0); } return result; } /* The default object does not have any delay. */ DelayType NetProc::delay_type() const { return NO_DELAY; } DelayType NetBlock::delay_type() const { DelayType result = NO_DELAY; for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { DelayType dt = cur->delay_type(); if (dt > result) result = dt; if (dt == DEFINITE_DELAY) break; } return result; } DelayType NetCase::delay_type() const { DelayType result = NO_DELAY; bool def_stmt = false; unsigned nstmts = nitems(); for (unsigned idx = 0; idx < nstmts; idx += 1) { if (!expr(idx)) def_stmt = true; DelayType dt = stat(idx) ? stat(idx)->delay_type() : NO_DELAY; if (idx == 0) { result = dt; } else { result = combine_delays(result, dt); } } /* If we don't have a default statement we don't know for sure * that we have a delay. */ if (!def_stmt) result = combine_delays(NO_DELAY, result); return result; } DelayType NetCondit::delay_type() const { DelayType if_type = if_ ? if_->delay_type() : NO_DELAY; DelayType el_type = else_? else_->delay_type() : NO_DELAY; return combine_delays(if_type, el_type); } /* * A do/while will execute the body at least once. */ DelayType NetDoWhile::delay_type() const { ivl_assert(*this, proc_); return proc_->delay_type(); } DelayType NetEvWait::delay_type() const { return DEFINITE_DELAY; } DelayType NetForever::delay_type() const { ivl_assert(*this, statement_); return statement_->delay_type(); } DelayType NetForLoop::delay_type() const { ivl_assert(*this, statement_); return get_loop_delay_type(condition_, statement_); } DelayType NetPDelay::delay_type() const { if (expr_) { return delay_type_from_expr(expr_); } else { if (delay() > 0) { return DEFINITE_DELAY; } else { if (statement_) { return statement_->delay_type(); } else { return NO_DELAY; } } } } DelayType NetRepeat::delay_type() const { ivl_assert(*this, statement_); return get_loop_delay_type(expr_, statement_); } DelayType NetTaskDef::delay_type() const { return proc_->delay_type(); } DelayType NetUTask::delay_type() const { return task()->task_def()->delay_type(); } DelayType NetWhile::delay_type() const { ivl_assert(*this, proc_); return get_loop_delay_type(cond_, proc_); } iverilog-10_1/netlist.h000066400000000000000000004610441265551621300152060ustar00rootroot00000000000000#ifndef IVL_netlist_H #define IVL_netlist_H /* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The netlist types, as described in this header file, are intended * to be the output from elaboration of the source design. The design * can be passed around in this form to the various stages and design * processors. */ # include # include # include # include # include # include # include # include "ivl_target.h" # include "ivl_target_priv.h" # include "pform_types.h" # include "config.h" # include "nettypes.h" # include "verinum.h" # include "verireal.h" # include "StringHeap.h" # include "HName.h" # include "LineInfo.h" # include "Attrib.h" # include "PUdp.h" #ifdef HAVE_IOSFWD # include #else class ostream; #endif class Design; class Link; class Nexus; class NetEvent; class NetNet; class NetNode; class NetObj; class NetPins; class NetProc; class NetProcTop; class NetRelease; class NetScope; class NetEvProbe; class NetExpr; class NetEAccess; class NetEConstEnum; class NetESignal; class NetFuncDef; class NetRamDq; class NetTaskDef; class NetEvTrig; class NetEvWait; class PClass; class PExpr; class PFunction; class PTaskFunc; struct enum_type_t; class netclass_t; class netdarray_t; class netparray_t; class netqueue_t; class netenum_t; class netstruct_t; class netvector_t; struct target; struct functor_t; #if defined(__cplusplus) && defined(_MSC_VER) # define ENUM_UNSIGNED_INT : unsigned int #else # define ENUM_UNSIGNED_INT #endif ostream& operator << (ostream&o, ivl_variable_type_t val); extern void join_island(NetPins*obj); class Link { friend void connect(Link&, Link&); friend class NetPins; friend class Nexus; friend class NexusSet; public: enum DIR ENUM_UNSIGNED_INT { PASSIVE, INPUT, OUTPUT }; private: // Only NetPins can create/delete Link objects Link(); ~Link(); public: // Manipulate the link direction. void set_dir(DIR d); DIR get_dir() const; // Set the delay for all the drivers to this nexus. void drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay); // A link has a drive strength for 0 and 1 values. The drive0 // strength is for when the link has the value 0, and drive1 // strength is for when the link has a value 1. void drive0(ivl_drive_t); void drive1(ivl_drive_t); // This sets the drives for all drivers of this link, and not // just the current link. void drivers_drive(ivl_drive_t d0, ivl_drive_t d1); ivl_drive_t drive0() const; ivl_drive_t drive1() const; void cur_link(NetPins*&net, unsigned &pin); void cur_link(const NetPins*&net, unsigned &pin) const; // Get a pointer to the nexus that represents all the links // connected to me. Nexus* nexus(); const Nexus* nexus()const; // Return a pointer to the next link in the nexus. Link* next_nlink(); const Link* next_nlink() const; // Remove this link from the set of connected pins. The // destructor will automatically do this if needed. void unlink(); // Return true if this link is connected to anything else. bool is_linked() const; // Return true if these pins are connected. bool is_linked(const Link&that) const; // Return true if this is the same pin of the same object of // that link. bool is_equal(const Link&that) const; // Return information about the object that this link is // a part of. Note that the get_obj() method can return NIL if // this Link is part of a NexusSet. That should be OK, because // they are collection variables, and not functional parts of // a design. const NetPins*get_obj() const; NetPins*get_obj(); unsigned get_pin() const; void dump_link(ostream&fd, unsigned ind) const; private: // The NetNode manages these. They point back to the // NetNode so that following the links can get me here. union { NetPins *node_; unsigned pin_; }; bool pin_zero_ : 1; DIR dir_ : 2; ivl_drive_t drive0_ : 3; ivl_drive_t drive1_ : 3; private: Nexus* find_nexus_() const; private: // The Nexus uses these to maintain its list of Link // objects. If this link is not connected to anything, // then these pointers are both nil. Link *next_; Nexus*nexus_; private: // not implemented Link(const Link&); Link& operator= (const Link&); }; class NetPins : public LineInfo { public: explicit NetPins(unsigned npins); virtual ~NetPins(); unsigned pin_count() const { return npins_; } Link&pin(unsigned idx); const Link&pin(unsigned idx) const; void dump_node_pins(ostream&, unsigned, const char**pin_names =0) const; void set_default_dir(Link::DIR d); bool is_linked(); bool pins_are_virtual(void) const; void devirtualize_pins(void); // This is for showing a brief description of the object to // the stream. It is used for debug and diagnostics. virtual void show_type(std::ostream&fd) const; private: Link*pins_; const unsigned npins_; Link::DIR default_dir_; }; /* ========= * A NetObj is anything that has any kind of behavior in the * netlist. Nodes can be gates, registers, etc. and are linked * together to form a design web. * * The web of nodes that makes up a circuit is held together by the * Link class. There is a link for each pin. All mutually connected * pins form a ring of links. * * A link can be INPUT, OUTPUT or PASSIVE. An input never drives the * signal, and PASSIVE never receives the value of the signal. Wires * are PASSIVE, for example. * * A NetObj also has delays specified as rise_time, fall_time and * decay_time. The rise and fall time are the times to transition to 1 * or 0 values. The decay_time is the time needed to decay to a 'bz * value, or to decay of the net is a trireg. The exact and precise * interpretation of the rise/fall/decay times is typically left to * the target to properly interpret. */ class NetObj : public NetPins, public Attrib { public: public: // The name of the object must be a permallocated string. A // lex_strings string, for example. explicit NetObj(NetScope*s, perm_string n, unsigned npins); virtual ~NetObj(); NetScope* scope(); const NetScope* scope() const; perm_string name() const { return name_; } const NetExpr* rise_time() const { return delay1_; } const NetExpr* fall_time() const { return delay2_; } const NetExpr* decay_time() const { return delay3_; } void rise_time(const NetExpr* d) { delay1_ = d; } void fall_time(const NetExpr* d) { delay2_ = d; } void decay_time(const NetExpr* d) { delay3_ = d; } void dump_obj_attr(ostream&, unsigned) const; virtual void show_type(std::ostream&fd) const; private: NetScope*scope_; perm_string name_; const NetExpr* delay1_; const NetExpr* delay2_; const NetExpr* delay3_; }; /* * Objects that can be island branches are derived from this. (It is * possible for an object to be a NetObj and an IslandBranch.) This is * used to collect island information about the node. */ class IslandBranch { public: IslandBranch(ivl_discipline_t dis =0) : island_(0), discipline_(dis) { } ivl_island_t get_island() const { return island_; } friend void join_island(NetPins*); private: ivl_island_t island_; ivl_discipline_t discipline_; }; /* * A NetBranch is a construct of Verilog-A that is a branch between * two nodes. The branch has exactly 2 pins and a discipline. * * pin(0) is the source of flow through a branch and the plus side of * potential. Pin(1) is the sink of flow and the minus (or ground) of * potential. */ class NetBranch : public NetPins, public IslandBranch { public: explicit NetBranch(ivl_discipline_t dis); explicit NetBranch(ivl_discipline_t dis, perm_string name); ~NetBranch(); // If the branch is named, this returns the name. perm_string name() const { return name_; } ivl_branch_s* target_obj() const { return &target_obj_; } void dump(ostream&, unsigned) const; private: perm_string name_; mutable ivl_branch_s target_obj_; // The design class uses this member to list the branches. friend class Design; NetBranch*next_; }; /* * The Nexus represents a collection of links that are joined * together. Each link has its own properties, this class holds the * properties of the group. * * The links in a nexus are grouped into a circularly linked list, * with the nexus pointing to the last Link. Each link in turn points * to the next link in the nexus, with the last link pointing back to * the first. The last link also has a non-nil nexus_ pointer back to * this nexus. * * The t_cookie() is an ivl_nexus_t that the code generator uses to * store data in the nexus. When a Nexus is created, this cookie is * set to nil. The code generator may set the cookie once. This locks * the nexus, and rewrites the Link list to be optimal for the code * generator. In the process, *all* of the other methods are no longer * functional. */ class Nexus { friend void connect(Link&, Link&); friend class Link; private: // Only Link objects can create (or delete) Nexus objects explicit Nexus(Link&r); ~Nexus(); public: void connect(Link&r); const char* name() const; void drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay); void drivers_drive(ivl_drive_t d0, ivl_drive_t d1); Link*first_nlink(); const Link* first_nlink()const; /* Get the width of the Nexus, or 0 if there are no vectors (in the form of NetNet objects) linked. */ unsigned vector_width() const; NetNet* pick_any_net(); /* This method counts the number of input and output links for this nexus, and assigns the results to the output arguments. */ void count_io(unsigned&inp, unsigned&out) const; /* This method returns true if there are any assignments that use this nexus as an l-value. This can be true if the nexus is a variable, but also if this is a net with a force. */ bool assign_lval() const; /* This method returns true if there are any drivers (including variables) attached to this nexus. */ bool drivers_present() const; /* This method returns true if all the possible drivers of this nexus are constant. It will also return true if there are no drivers at all. */ bool drivers_constant() const; /* Given the nexus has constant drivers, this method returns the value that has been driven. */ verinum::V driven_value() const; verinum driven_vector() const; /* Return a mask of the bits of this vector that are driven. This is usually all false or all true, but in special cases it may be a blend. */ std::vector driven_mask(void)const; /* The code generator sets an ivl_nexus_t to attach code generation details to the nexus. */ ivl_nexus_t t_cookie() const { return t_cookie_; } void t_cookie(ivl_nexus_t) const; private: Link*list_; void unlink(Link*); mutable char* name_; /* Cache the calculated name for the Nexus. */ mutable ivl_nexus_t t_cookie_; enum VALUE { NO_GUESS, V0, V1, Vx, Vz, VAR }; mutable VALUE driven_; private: // not implemented Nexus(const Nexus&); Nexus& operator= (const Nexus&); }; inline void connect(Nexus*l, Link&r) { l->connect(r); } class NexusSet { public: struct elem_t { inline elem_t(Nexus*n, unsigned b, unsigned w) : base(b), wid(w) { lnk.set_dir(Link::PASSIVE); n->connect(lnk); } inline elem_t() : base(0), wid(0) { } inline bool operator == (const struct elem_t&that) const { return lnk.is_linked(that.lnk) && base==that.base && wid==that.wid; } bool contains(const struct elem_t&that) const; Link lnk; unsigned base; unsigned wid; private: elem_t(const elem_t&); elem_t& operator= (elem_t&); }; public: ~NexusSet(); NexusSet(); size_t size() const; // Add the nexus/part to the set, if it is not already present. void add(Nexus*that, unsigned base, unsigned wid); void add(NexusSet&that); // Remove the nexus from the set, if it is present. void rem(const NexusSet&that); unsigned find_nexus(const elem_t&that) const; elem_t& at(unsigned idx); inline elem_t& operator[] (unsigned idx) { return at(idx); } // Return true if this set contains every nexus/part in that // set. That means that every bit of that set is accounted for // this set. bool contains(const NexusSet&that) const; // Return true if this set contains any nexus in that set. bool intersect(const NexusSet&that) const; private: // NexSet items are canonical part selects of vectors. std::vector items_; size_t bsearch_(const struct elem_t&that) const; void rem_(const struct elem_t*that); bool contains_(const elem_t&that) const; private: // not implemented NexusSet(const NexusSet&); NexusSet& operator= (const NexusSet&); }; /* * A NetBus is a transparent device that is merely a bunch of pins * used to tie some pins to. It is a convenient way to collect a * bundle of pins and pass that bundle around. */ class NetBus : public NetObj { public: NetBus(NetScope*scope, unsigned pin_count); ~NetBus(); unsigned find_link(const Link&that) const; private: // not implemented NetBus(const NetBus&); NetBus& operator= (const NetBus&); }; /* * A NetNode is a device of some sort, where each pin has a different * meaning. (i.e., pin(0) is the output to an and gate.) NetNode * objects are listed in the nodes_ of the Design object. */ class NetNode : public NetObj { public: // The name parameter must be a permallocated string. explicit NetNode(NetScope*s, perm_string n, unsigned npins); virtual ~NetNode(); virtual bool emit_node(struct target_t*) const; virtual void dump_node(ostream&, unsigned) const; // This is used to scan a modifiable netlist, one node at a time. virtual void functor_node(Design*, functor_t*); private: friend class Design; NetNode*node_next_, *node_prev_; Design*design_; }; /* * A NetDelaySrc is an input-only device that calculates a path delay * based on the time that the inputs change. This class is used by the * NetNet class, and NetDelaySrc objects cannot exist outside of its * association with NetNet objects. */ class NetDelaySrc : public NetObj { public: explicit NetDelaySrc(NetScope*s, perm_string n, unsigned nsrc, bool condit_src, bool conditional); ~NetDelaySrc(); // These functions set the delays from the values in the // source. These set_delays functions implement the various // rules wrt collections of transitions. // One transition specified. void set_delays(uint64_t del); // Two transitions: rise and fall void set_delays(uint64_t rise, uint64_t fall); // Three transitions void set_delays(uint64_t rise, uint64_t fall, uint64_t tz); void set_delays(uint64_t t01, uint64_t t10, uint64_t t0z, uint64_t tz1, uint64_t t1z, uint64_t tz0); void set_delays(uint64_t t01, uint64_t t10, uint64_t t0z, uint64_t tz1, uint64_t t1z, uint64_t tz0, uint64_t t0x, uint64_t tx1, uint64_t t1x, uint64_t tx0, uint64_t txz, uint64_t tzx); uint64_t get_delay(unsigned pe) const; void set_posedge(); void set_negedge(); bool is_posedge() const; bool is_negedge() const; unsigned src_count() const; Link&src_pin(unsigned); const Link&src_pin(unsigned) const; bool is_condit() const; bool has_condit() const; Link&condit_pin(); const Link&condit_pin() const; void dump(ostream&, unsigned ind) const; private: uint64_t transition_delays_[12]; bool condit_flag_; bool conditional_; bool posedge_; bool negedge_; private: // Not implemented NetDelaySrc(const NetDelaySrc&); NetDelaySrc& operator= (const NetDelaySrc&); }; /* * NetNet is a special kind of NetObj that doesn't really do anything, * but carries the properties of the wire/reg/trireg, including its * name. Scalars and vectors are all the same thing here, a NetNet * with a single pin. The difference between a scalar and vector is * the width of the atomic vector datum it carries. * * NetNet objects can also appear as side effects of synthesis or * other abstractions. * * Note that INTEGER types are an alias for a ``reg signed [31:0]''. * * NetNet objects have a name and exist within a scope, so the * constructor takes a pointer to the containing scope. The object * automatically adds itself to the scope. * * NetNet objects are located by searching NetScope objects. * * The pins of a NetNet object are usually PASSIVE: they do not drive * anything and they are not a data sink, per se. The pins follow the * values on the nexus. The exceptions are reg, trireg, tri0, tri1, * supply0, and supply1 objects, whose pins are classed as OUTPUT. */ class PortType { public: enum Enum ENUM_UNSIGNED_INT { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT, PREF }; /* * Merge Port types (used to construct a sane combined port-type * for module ports with complex defining expressions). * */ static Enum merged( Enum lhs, Enum rhs ); }; extern std::ostream& operator << (std::ostream&, PortType::Enum); /* * Information on actual ports (rather than port-connected signals) of * module. * N.b. must be POD as passed through a "C" interface in the t-dll-api. */ struct PortInfo { PortType::Enum type; unsigned long width; perm_string name; }; class NetNet : public NetObj, public PortType { public: enum Type ENUM_UNSIGNED_INT { NONE, IMPLICIT, IMPLICIT_REG, INTEGER, WIRE, TRI, TRI1, SUPPLY0, SUPPLY1, WAND, TRIAND, TRI0, WOR, TRIOR, REG, UNRESOLVED_WIRE }; typedef PortType::Enum PortType; static const std::listnot_an_array; public: // This form is the more generic form of the constructor. For // now, the unpacked type is not buried into an ivl_type_s object. explicit NetNet(NetScope*s, perm_string n, Type t, const std::list&unpacked, ivl_type_t type); // This form builds a NetNet from its record/enum/darray // definition. They should probably be replaced with a single // version that takes an ivl_type_s* base. explicit NetNet(NetScope*s, perm_string n, Type t, netstruct_t*type); explicit NetNet(NetScope*s, perm_string n, Type t, netdarray_t*type); explicit NetNet(NetScope*s, perm_string n, Type t, netvector_t*type); virtual ~NetNet(); Type type() const; void type(Type t); PortType port_type() const; void port_type(PortType t); // If this net net is a port (i.e. a *sub*port net of a module port) // its port index is number of the module it connects through int get_module_port_index() const; // -1 Not connected to port... void set_module_port_index(unsigned idx); ivl_variable_type_t data_type() const; /* If a NetNet is signed, then its value is to be treated as signed. Otherwise, it is unsigned. */ bool get_signed() const; /* Used to maintain original type of net since integers are implemented as 'reg signed [31:0]' in Icarus */ bool get_isint() const; bool get_scalar() const; inline const ivl_type_s* net_type(void) const { return net_type_; } const netenum_t*enumeration(void) const; const netstruct_t*struct_type(void) const; const netdarray_t*darray_type(void) const; const netqueue_t*queue_type(void) const; const netclass_t*class_type(void) const; /* Attach a discipline to the net. */ ivl_discipline_t get_discipline() const; void set_discipline(ivl_discipline_t dis); /* This method returns a reference to the packed dimensions for the vector. These are arranged as a list where the first range in the list (front) is the left-most range in the verilog declaration. These packed dims are compressed to represent the dimensions of all the subtypes. */ const std::vector& packed_dims() const { return slice_dims_; } const std::vector& unpacked_dims() const { return unpacked_dims_; } /* The vector_width returns the bit width of the packed array, vector or scalar that is this NetNet object. */ inline unsigned long vector_width() const { return slice_width(0); } /* Given a prefix of indices, figure out how wide the resulting slice would be. This is a generalization of the vector_width(), where the depth would be 0. */ unsigned long slice_width(size_t depth) const; /* This method converts a signed index (the type that might be found in the Verilog source) to canonical. It accounts for variation in the definition of the reg/wire/whatever. Note that a canonical index of a multi-dimensioned packed array is a single dimension. For example, "reg [4:1][3:0]..." has the canonical dimension [15:0] and the sb_to_idx() method will convert [2][2] to the canonical index [6]. */ long sb_to_idx(const std::list&prefix, long sb) const; /* This method converts a partial packed indices list and a tail index, and generates a canonical slice offset and width. */ bool sb_to_slice(const std::list&prefix, long sb, long&off, unsigned long&wid) const; /* This method checks that the signed index is valid for this signal. If it is, the above sb_to_idx can be used to get the pin# from the index. */ bool sb_is_valid(const std::list&prefix, long sb) const; /* This method returns 0 for scalars and vectors, and greater for arrays. The value is the number of array indices. (Currently only one array index is supported.) */ inline unsigned unpacked_dimensions() const { return unpacked_dims_.size(); } /* This method returns 0 for scalars, but vectors and other PACKED arrays have packed dimensions. */ inline size_t packed_dimensions() const { return slice_dims_.size(); } // This is the number of array elements. unsigned unpacked_count() const; bool local_flag() const { return local_flag_; } void local_flag(bool f) { local_flag_ = f; } // NetESignal objects may reference this object. Keep a // reference count so that I keep track of them. void incr_eref(); void decr_eref(); unsigned peek_eref() const; // Assignment statements count their lrefs here. And by // assignment statements, we mean BEHAVIORAL assignments. void incr_lref(); void decr_lref(); unsigned peek_lref() const { return lref_count_; } // Treating this node as a uwire, this function tests whether // any bits in the canonical part are already driven. This is // only useful for UNRESOLVED_WIRE objects. The msb and lsb // are the part select of the signal, and the widx is the word // index if this is an unpacked array. bool test_and_set_part_driver(unsigned msb, unsigned lsb, int widx =0); unsigned get_refs() const; /* Manage path delays */ void add_delay_path(class NetDelaySrc*path); unsigned delay_paths(void) const; const class NetDelaySrc*delay_path(unsigned idx) const; virtual void dump_net(ostream&, unsigned) const; private: void initialize_dir_(); private: Type type_ : 5; PortType port_type_ : 3; bool local_flag_: 1; ivl_type_t net_type_; ivl_discipline_t discipline_; std::vector unpacked_dims_; // These are the widths of the various slice depths. There is // one entry in this vector for each packed dimension. The // value at N is the slice width if N indices are provided. // // For example: slice_wids_[0] is vector_width(). void calculate_slice_widths_from_packed_dims_(void); std::vector slice_dims_; std::vector slice_wids_; unsigned eref_count_; unsigned lref_count_; // When the signal is an unresolved wire, we need more detail // which bits are assigned. This mask is true for each bit // that is known to be driven. std::vector lref_mask_; vector delay_paths_; int port_index_; }; /* * This object type is used for holding local variable values when * evaluating constant user functions. */ struct LocalVar { int nwords; // zero for a simple variable, -1 for reference union { NetExpr* value; // a simple variable NetExpr** array; // an array variable LocalVar* ref; // A reference to a previous scope }; }; class NetBaseDef { public: NetBaseDef(NetScope*n, const vector&po, const std::vector&pd); virtual ~NetBaseDef(); const NetScope*scope() const; NetScope*scope(); unsigned port_count() const; NetNet*port(unsigned idx) const; NetExpr*port_defe(unsigned idx) const; void set_proc(NetProc*p); //const string& name() const; const NetProc*proc() const; private: NetScope*scope_; std::vectorports_; std::vectorpdefaults_; protected: NetProc*proc_; }; /* * Some definitions (and methods to manipulate them) are common to a * couple of types. Keep them here. */ class Definitions { public: Definitions(); ~Definitions(); // Add the enumeration to the set of enumerations in this // scope. Include a key that the elaboration can use to look // up this enumeration based on the pform type. void add_enumeration_set(const enum_type_t*key, netenum_t*enum_set); bool add_enumeration_name(netenum_t*enum_set, perm_string enum_name); // Look up the enumeration literal in this scope. if the name // is present, then return the enumeration type that declares it. const netenum_t* enumeration_for_name(perm_string name); // Look up the enumeration set that was added with the given // key. This is used by enum_type_t::elaborate_type to locate // a previously elaborated enumeration. netenum_t* enumeration_for_key(const enum_type_t*key) const; // Look up an enumeration literal in this scope. If the // literal is present, return the expression that defines its // value. const NetExpr* enumeration_expr(perm_string key); // Definitions scopes can also hold classes, by name. void add_class(netclass_t*class_type); protected: // Enumerations. The enum_sets_ is a list of all the // enumerations present in this scope. The enum_names_ is a // map of all the enumeration names back to the sets that // contain them. std::map enum_sets_; std::map enum_names_; // This is a map of all the classes (by name) in this scope. std::map classes_; }; /* * This object type is used to contain a logical scope within a * design. The scope doesn't represent any executable hardware, but is * just a handle that netlist processors can use to grab at the design. */ class NetScope : public Definitions, public Attrib { public: enum TYPE { MODULE, CLASS, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK, PACKAGE }; /* Create a new scope, and attach it to the given parent. The name is expected to have been permallocated. */ NetScope(NetScope*up, const hname_t&name, TYPE t, bool nest=false, bool program=false, bool interface=false); ~NetScope(); /* Rename the scope using the name generated by inserting as many pad characters as required between prefix and suffix to make the name unique in the parent scope. Return false if a unique name couldn't be generated. */ bool auto_name(const char* prefix, char pad, const char* suffix); /* Routine to search for the enumeration given a name. It basically * does what enumeration_for_name() does but searched the hierarchy. */ const netenum_t*find_enumeration_for_name(perm_string name); /* Parameters exist within a scope, and these methods allow one to manipulate the set. In these cases, the name is the *simple* name of the parameter, the hierarchy is implicit in the scope. */ struct range_t; void set_parameter(perm_string name, bool is_annotatable, PExpr*val, ivl_variable_type_t type, PExpr*msb, PExpr*lsb, bool signed_flag, bool local_flag, NetScope::range_t*range_list, const LineInfo&file_line); void set_parameter(perm_string name, NetExpr*val, const LineInfo&file_line); const NetExpr*get_parameter(Design*des, const char* name, const NetExpr*&msb, const NetExpr*&lsb); const NetExpr*get_parameter(Design*des, perm_string name, const NetExpr*&msb, const NetExpr*&lsb); /* These are used by defparam elaboration to replace the expression with a new expression, without affecting the range or signed_flag. Return false if the name does not exist. */ bool replace_parameter(perm_string name, PExpr*val, NetScope*scope); /* This is used to ensure the value of a parameter cannot be changed at run-time. This is required if a specparam is used in an expression that must be evaluated at compile-time. Returns true if the named parameter is a specparam and has not already been set to be unannotatable. */ bool make_parameter_unannotatable(perm_string name); /* These methods set or access events that live in this scope. */ void add_event(NetEvent*); void rem_event(NetEvent*); NetEvent*find_event(perm_string name); /* These methods add or find a genvar that lives in this scope. */ void add_genvar(perm_string name, LineInfo *li); LineInfo* find_genvar(perm_string name); /* These methods manage signals. The add_ and rem_signal methods are used by the NetNet objects to make themselves available to the scope, and the find_signal method can be used to locate signals within a scope. */ void add_signal(NetNet*); void rem_signal(NetNet*); NetNet* find_signal(perm_string name); netclass_t* find_class(perm_string name); /* The parent and child() methods allow users of NetScope objects to locate nearby scopes. */ NetScope* parent() { return up_; } NetScope* child(const hname_t&name); const NetScope* parent() const { return up_; } const NetScope* child(const hname_t&name) const; /* A helper function to find the enclosing class scope. */ const NetScope* get_class_scope() const; // Look for a child scope by name. This ignores the number // part of the child scope name, so there may be multiple // matches. Only return one. This function is only really // useful for some elaboration error checking. const NetScope* child_byname(perm_string name) const; // Nested modules have slightly different scope search rules. inline bool nested_module() const { return nested_module_; } // Program blocks and interfaces have elaboration constraints. inline bool program_block() const { return program_block_; } inline bool is_interface() const { return is_interface_; } TYPE type() const; void print_type(ostream&) const; void set_task_def(NetTaskDef*); void set_func_def(NetFuncDef*); void set_class_def(netclass_t*); void set_module_name(perm_string); NetTaskDef* task_def(); NetFuncDef* func_def(); // This is used by the evaluate_function setup to collect // local variables from the scope. void evaluate_function_find_locals(const LineInfo&loc, map&ctx) const; void set_line(perm_string file, perm_string def_file, unsigned lineno, unsigned def_lineno); void set_line(perm_string file, unsigned lineno); void set_line(const LineInfo *info); perm_string get_file() const { return file_; }; perm_string get_def_file() const { return def_file_; }; unsigned get_lineno() const { return lineno_; }; unsigned get_def_lineno() const { return def_lineno_; }; bool in_func() const; /* Provide a link back to the pform to allow early elaboration of constant functions. */ void set_func_pform(const PFunction*pfunc) { func_pform_ = pfunc; }; const PFunction*func_pform() const { return func_pform_; }; /* Allow tracking of elaboration stages. The three stages are: 1 - scope elaboration 2 - signal elaboration 3 - statement elaboration This is only used for functions, to support early elaboration. */ void set_elab_stage(unsigned stage) { elab_stage_ = stage; }; unsigned elab_stage() const { return elab_stage_; }; /* Is this a function called in a constant expression. */ void need_const_func(bool need_const) { need_const_func_ = need_const; }; bool need_const_func() const { return need_const_func_; }; /* Is this a constant function. */ void is_const_func(bool is_const) { is_const_func_ = is_const; }; bool is_const_func() const { return is_const_func_; }; /* Is the task or function automatic. */ void is_auto(bool is_auto__) { is_auto_ = is_auto__; }; bool is_auto() const { return is_auto_; }; /* Is the module a cell (is in a `celldefine) */ void is_cell(bool is_cell__) { is_cell_ = is_cell__; }; bool is_cell() const { return is_cell_; }; /* Is there a call to a system task in this scope. */ void calls_sys_task(bool calls_stask__) { calls_stask_ = calls_stask__; }; bool calls_sys_task() const { return calls_stask_; }; /* Is this scope elaborating a final procedure? */ void in_final(bool in_final__) { in_final_ = in_final__; }; bool in_final() const { return in_final_; }; const NetTaskDef* task_def() const; const NetFuncDef* func_def() const; const netclass_t* class_def() const; /* If the scope represents a module instance, the module_name is the name of the module itself. */ perm_string module_name() const; /* If the scope is a module then it may have ports that we need * to keep track of. */ void set_num_ports(unsigned int num_ports); void add_module_port_net(NetNet*port); unsigned module_port_nets() const; NetNet*module_port_net(unsigned idx) const; void add_module_port_info( unsigned idx, perm_string name, // May be "" for undeclared port PortType::Enum type, unsigned long width ); const std::vector &module_port_info() const; /* Scopes have their own time units and time precision. The unit and precision are given as power of 10, i.e., -3 is units of milliseconds. If a NetScope is created with a parent scope, the new scope will initially inherit the unit and precision of the parent scope. */ void time_unit(int); void time_precision(int); void time_from_timescale(bool); int time_unit() const; int time_precision() const; bool time_from_timescale() const; /* The fullname of the scope is the hierarchical name component (which includes the name and array index) whereas the basename is just my name. */ perm_string basename() const; const hname_t& fullname() const { return name_; } void run_defparams(class Design*); void run_defparams_later(class Design*); void evaluate_parameters(class Design*); // Look for defparams that never matched, and print warnings. void residual_defparams(class Design*); /* This method generates a non-hierarchical name that is guaranteed to be unique within this scope. */ perm_string local_symbol(); void dump(ostream&) const; void emit_scope(struct target_t*tgt) const; bool emit_defs(struct target_t*tgt) const; /* This method runs the functor on me. Recurse through the children of this node as well. */ void run_functor(Design*des, functor_t*fun); /* This member is used during elaboration to pass defparam assignments from the scope pass to the parameter evaluation step. After that, it is not used. */ list > defparams; list,PExpr*> > defparams_later; public: struct range_t { bool exclude_flag; // Lower bound bool low_open_flag; NetExpr*low_expr; // Upper bound bool high_open_flag; NetExpr*high_expr; // Link to the next range specification struct range_t*next; }; /* After everything is all set up, the code generators like access to these things to make up the parameter lists. */ struct param_expr_t : public LineInfo { param_expr_t() : msb_expr(0), lsb_expr(0), val_expr(0), val_scope(0), solving(false), is_annotatable(false), type(IVL_VT_NO_TYPE), signed_flag(false), local_flag(false), msb(0), lsb(0), range(0), val(0) { } // Source expressions PExpr*msb_expr; PExpr*lsb_expr; PExpr*val_expr; // Scope information NetScope*val_scope; // Evaluation status bool solving; // specparam status bool is_annotatable; // Type information ivl_variable_type_t type; bool signed_flag; bool local_flag; NetExpr*msb; NetExpr*lsb; // range constraints struct range_t*range; // Expression value NetExpr*val; }; mapparameters; typedef map::iterator param_ref_t; param_ref_t find_parameter(perm_string name); /* Module instance arrays are collected here for access during the multiple elaboration passes. */ typedef vector scope_vec_t; mapinstance_arrays; /* Loop generate uses this as scratch space during elaboration. Expression evaluation can use this to match names. */ perm_string genvar_tmp; long genvar_tmp_val; std::map loop_index_tmp; private: void evaluate_parameter_logic_(Design*des, param_ref_t cur); void evaluate_parameter_real_(Design*des, param_ref_t cur); void evaluate_parameter_(Design*des, param_ref_t cur); private: TYPE type_; hname_t name_; // True if the scope is a nested module/program block bool nested_module_; // True if the scope is a program block bool program_block_; // True if the scope is an interface bool is_interface_; perm_string file_; perm_string def_file_; unsigned lineno_; unsigned def_lineno_; signed char time_unit_, time_prec_; bool time_from_timescale_; NetEvent *events_; map genvars_; typedef std::map::const_iterator signals_map_iter_t; std::map signals_map_; perm_string module_name_; vector port_nets; vector ports_; union { NetTaskDef*task_; NetFuncDef*func_; netclass_t*class_def_; }; const PFunction*func_pform_; unsigned elab_stage_; NetScope*up_; map children_; unsigned lcounter_; bool need_const_func_, is_const_func_, is_auto_, is_cell_, calls_stask_; /* Final procedures sets this to notify statements that they are part of a final procedure. */ bool in_final_; }; /* * This class implements the LPM_ABS component. The node has a single * input, a signed expression, that it converts to the absolute * value. The gate is simple: pin(0) is the output and pin(1) is the input. */ class NetAbs : public NetNode { public: NetAbs(NetScope*s, perm_string n, unsigned width); ~NetAbs(); unsigned width() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: unsigned width_; }; /* * This class implements the LPM_ADD_SUB component as described in the * EDIF LPM Version 2 1 0 standard. It is used as a structural * implementation of the + and - operators. */ class NetAddSub : public NetNode { public: NetAddSub(NetScope*s, perm_string n, unsigned width); ~NetAddSub(); // Get the width of the device (that is, the width of the // operands and results). unsigned width() const; Link& pin_Cout(); Link& pin_DataA(); Link& pin_DataB(); Link& pin_Result(); const Link& pin_Cout() const; const Link& pin_DataA() const; const Link& pin_DataB() const; const Link& pin_Result() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: unsigned width_; }; /* * The NetArrayDq node represents an array dereference. The NetNet * that this object refers to is an array, and the Address pin selects * which word of the array to place on the Result. */ class NetArrayDq : public NetNode { public: NetArrayDq(NetScope*s, perm_string name, NetNet*mem, unsigned awid); ~NetArrayDq(); unsigned width() const; unsigned awidth() const; unsigned size() const; const NetNet*mem() const; Link& pin_Address(); Link& pin_Result(); const Link& pin_Address() const; const Link& pin_Result() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: NetNet*mem_; unsigned awidth_; }; /* * Convert an IVL_VT_REAL input to a logical value with the * given width. The input is pin(1) and the output is pin(0). */ class NetCastInt4 : public NetNode { public: NetCastInt4(NetScope*s, perm_string n, unsigned width); unsigned width() const { return width_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: unsigned width_; }; class NetCastInt2 : public NetNode { public: NetCastInt2(NetScope*s, perm_string n, unsigned width); unsigned width() const { return width_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: unsigned width_; }; /* * Convert an input to IVL_VT_REAL. The input is pin(1), which can be * any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0), * which is IVL_VT_REAL. The conversion interprets the input as an * unsigned value unless the signed_flag is true. */ class NetCastReal : public NetNode { public: NetCastReal(NetScope*s, perm_string n, bool signed_flag); bool signed_flag() const { return signed_flag_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: bool signed_flag_; }; /* * This type represents the LPM_CLSHIFT device. */ class NetCLShift : public NetNode { public: NetCLShift(NetScope*s, perm_string n, unsigned width, unsigned width_dist, bool right_flag, bool signed_flag); ~NetCLShift(); unsigned width() const; unsigned width_dist() const; bool right_flag() const; bool signed_flag() const; Link& pin_Data(); Link& pin_Result(); Link& pin_Distance(); const Link& pin_Data() const; const Link& pin_Result() const; const Link& pin_Distance() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: unsigned width_; unsigned width_dist_; bool right_flag_; bool signed_flag_; }; /* * This class supports the LPM_COMPARE device. * * The width of the device is the width of the inputs. If one of the * inputs is narrower than the other, it is up to the generator to * make sure all the data pins are properly driven. * * The signed() property is true if the comparison is to be done to * signed arguments. The result is always UNsigned. * * NOTE: This is not the same as the device used to support case * compare. Case comparisons handle Vx and Vz values, whereas this * device need not. */ class NetCompare : public NetNode { public: NetCompare(NetScope*scope, perm_string n, unsigned width); ~NetCompare(); unsigned width() const; bool get_signed() const; void set_signed(bool); Link& pin_AGB(); Link& pin_AGEB(); Link& pin_AEB(); Link& pin_ANEB(); Link& pin_ALB(); Link& pin_ALEB(); Link& pin_DataA(); Link& pin_DataB(); const Link& pin_AGB() const; const Link& pin_AGEB() const; const Link& pin_AEB() const; const Link& pin_ANEB() const; const Link& pin_ALB() const; const Link& pin_ALEB() const; const Link& pin_DataA() const; const Link& pin_DataB() const; virtual void functor_node(Design*, functor_t*); virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: unsigned width_; bool signed_flag_; }; /* * This node is a means to connect net inputs together to form a wider * vector. The output (pin 0) is a concatenation of the input vectors, * with pin-1 at the LSB, pin-2 next, and so on. This node is most * like the NetLogic node, as it has one output at pin 0 and the * remaining pins are the input that are combined to make the * output. It is separated out because it it generally a special case * for the code generators. * * When constructing the node, the width is the vector_width of the * output, and the cnt is the number of pins. (the number of input * vectors.) */ class NetConcat : public NetNode { public: NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt, bool transparent_flag = false); ~NetConcat(); unsigned width() const; // This is true if the concatenation is a transparent // concatenation, meaning strengths are passed through as // is. In this case, the output strengths of this node will be // ignored. bool transparent() const { return transparent_; } void dump_node(ostream&, unsigned ind) const; bool emit_node(struct target_t*) const; void functor_node(Design*des, functor_t*fun); private: unsigned width_; bool transparent_; }; /* * This class represents a theoretical (though not necessarily * practical) integer divider gate. This is not to represent any real * hardware, but to support the / operator in Verilog, when it shows * up in structural contexts. * * The operands of the operation are the DataA and DataB inputs, * and the Result output reflects the value DataA/DataB. */ class NetDivide : public NetNode { public: NetDivide(NetScope*scope, perm_string n, unsigned width, unsigned wa, unsigned wb); ~NetDivide(); unsigned width_r() const; unsigned width_a() const; unsigned width_b() const; void set_signed(bool); bool get_signed() const; Link& pin_DataA(); Link& pin_DataB(); Link& pin_Result(); const Link& pin_DataA() const; const Link& pin_DataB() const; const Link& pin_Result() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: unsigned width_r_; unsigned width_a_; unsigned width_b_; bool signed_flag_; }; /* * This class represents a theoretical (though not necessarily * practical) integer modulo gate. This is not to represent any real * hardware, but to support the % operator in Verilog, when it shows * up in structural contexts. * * The operands of the operation are the DataA and DataB inputs, * and the Result output reflects the value DataA%DataB. */ class NetModulo : public NetNode { public: NetModulo(NetScope*s, perm_string n, unsigned width, unsigned wa, unsigned wb); ~NetModulo(); unsigned width_r() const; unsigned width_a() const; unsigned width_b() const; void set_signed(bool); bool get_signed() const; Link& pin_DataA(); Link& pin_DataB(); Link& pin_Result(); const Link& pin_DataA() const; const Link& pin_DataB() const; const Link& pin_Result() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: unsigned width_r_; unsigned width_a_; unsigned width_b_; bool signed_flag_; }; /* * This class represents an LPM_FF device. There is no literal gate * type in Verilog that maps, but gates of this type can be inferred. */ class NetFF : public NetNode { public: NetFF(NetScope*s, perm_string n, bool negedge, unsigned vector_width); ~NetFF(); bool is_negedge() const; unsigned width() const; Link& pin_Clock(); Link& pin_Enable(); Link& pin_Aset(); Link& pin_Aclr(); Link& pin_Sset(); Link& pin_Sclr(); Link& pin_Data(); Link& pin_Q(); const Link& pin_Clock() const; const Link& pin_Enable() const; const Link& pin_Aset() const; const Link& pin_Aclr() const; const Link& pin_Sset() const; const Link& pin_Sclr() const; const Link& pin_Data() const; const Link& pin_Q() const; void aset_value(const verinum&val); const verinum& aset_value() const; void sset_value(const verinum&val); const verinum& sset_value() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: bool negedge_; unsigned width_; verinum aset_value_; verinum sset_value_; }; /* * This class implements a basic LPM_MULT combinational multiplier. It * is used as a structural representation of the * operator. The * device has inputs A and B and output Result all with independent * widths. * * NOTE: Check this width thing. I think that the independence of the * widths is not necessary or even useful. */ class NetMult : public NetNode { public: NetMult(NetScope*sc, perm_string n, unsigned width, unsigned wa, unsigned wb); ~NetMult(); bool get_signed() const; void set_signed(bool); // Get the width of the device bussed inputs. There are these // parameterized widths: unsigned width_r() const; // Result unsigned width_a() const; // DataA unsigned width_b() const; // DataB Link& pin_DataA(); Link& pin_DataB(); Link& pin_Result(); const Link& pin_DataA() const; const Link& pin_DataB() const; const Link& pin_Result() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: bool signed_; unsigned width_r_; unsigned width_a_; unsigned width_b_; }; /* * This class represents an LPM_MUX device. This device has some * number of Result points (the width of the device) and some number * of input choices. There is also a selector of some width. The * parameters are: * * width -- Width of the result and each possible Data input * size -- Number of Data input (each of width) * selw -- Width in bits of the select input * * All the data inputs must have the same type, and are the type of * the result. The actual type does not matter, as the mux does not * process data, just selects alternatives. * * The select input must be an integral type of some sort. Not real. */ class NetMux : public NetNode { public: NetMux(NetScope*scope, perm_string n, unsigned width, unsigned size, unsigned selw); ~NetMux(); unsigned width() const; unsigned size() const; unsigned sel_width() const; Link& pin_Result(); Link& pin_Data(unsigned si); Link& pin_Sel(); const Link& pin_Result() const; const Link& pin_Data(unsigned) const; const Link& pin_Sel() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: unsigned width_; unsigned size_; unsigned swidth_; }; /* * This class implements a basic LPM_POW combinational power. It * is used as a structural representation of the ** operator. The * device has inputs A and B and output Result all with independent * widths. * * NOTE: Check this width thing. I think that the independence of the * widths is not necessary or even useful. */ class NetPow : public NetNode { public: NetPow(NetScope*sc, perm_string n, unsigned width, unsigned wa, unsigned wb); ~NetPow(); bool get_signed() const; void set_signed(bool); // Get the width of the device bussed inputs. There are these // parameterized widths: unsigned width_r() const; // Result unsigned width_a() const; // DataA unsigned width_b() const; // DataB Link& pin_DataA(); Link& pin_DataB(); Link& pin_Result(); const Link& pin_DataA() const; const Link& pin_DataB() const; const Link& pin_Result() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*des, functor_t*fun); private: bool signed_; unsigned width_r_; unsigned width_a_; unsigned width_b_; }; /* * The NetReplicate node takes a vector input and makes it into a larger * vector by repeating the input vector some number of times. The * repeat count is a fixed value. This is just like the repeat * concatenation of Verilog: {{}}. * * When constructing this node, the wid is the vector width of the * output, and the rpt is the repeat count. The wid must be an even * multiple of the cnt, and wid/cnt is the expected input width. * * The device has exactly 2 pins: pin(0) is the output and pin(1) the * input. */ class NetReplicate : public NetNode { public: NetReplicate(NetScope*scope, perm_string n, unsigned wid, unsigned rpt); ~NetReplicate(); unsigned width() const; unsigned repeat() const; void dump_node(ostream&, unsigned ind) const; bool emit_node(struct target_t*) const; private: unsigned width_; unsigned repeat_; }; /* * This node represents the call of a user defined function in a * structural context. The pin count is the same as the port count, * with pin0 the return value. */ class NetUserFunc : public NetNode { public: NetUserFunc(NetScope*s, perm_string n, NetScope*def, NetEvWait*trigger__); ~NetUserFunc(); ivl_variable_type_t data_type(unsigned port) const; unsigned port_width(unsigned port) const; const NetScope* def() const; const NetEvWait* trigger() const { return trigger_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: NetScope*def_; NetEvWait*trigger_; }; /* * The number of ports includes the return value, so will always be at * least 1. */ class NetSysFunc : public NetNode { public: NetSysFunc(NetScope*s, perm_string n, const struct sfunc_return_type*def, unsigned ports, NetEvWait*trigger__); ~NetSysFunc(); ivl_variable_type_t data_type() const; unsigned vector_width() const; const char* func_name() const; const NetEvWait* trigger() const { return trigger_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: const struct sfunc_return_type*def_; NetEvWait*trigger_; }; class NetTran : public NetNode, public IslandBranch { public: // Tran devices other than TRAN_VP NetTran(NetScope*scope, perm_string n, ivl_switch_type_t type, unsigned wid); // Create a TRAN_VP NetTran(NetScope*scope, perm_string n, unsigned wid, unsigned part, unsigned off); ~NetTran(); ivl_switch_type_t type() const { return type_; } // These are only used for IVL_SW_TRAN_PV unsigned vector_width() const; unsigned part_width() const; unsigned part_offset() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: ivl_switch_type_t type_; unsigned wid_; unsigned part_; unsigned off_; }; /* ========= * There are cases where expressions need to be represented. The * NetExpr class is the root of a hierarchy that serves that purpose. * * The expr_width() is the width of the expression, which is calculated * before the expression is elaborated. */ class NetExpr : public LineInfo { public: explicit NetExpr(unsigned w =0); explicit NetExpr(ivl_type_t t); virtual ~NetExpr() =0; virtual void expr_scan(struct expr_scan_t*) const =0; virtual void dump(ostream&) const; // This is the advanced description of the type. I think I // want to replace the other type description members with // this single method. The default for this method returns // nil. ivl_type_t net_type() const; // Expressions have type. virtual ivl_variable_type_t expr_type() const; // How wide am I? unsigned expr_width() const { return width_; } // This method returns true if the expression is // signed. Unsigned expressions return false. bool has_sign() const { return signed_flag_; } virtual void cast_signed(bool flag); // This returns true if the expression has a definite // width. This is generally true, but in some cases the // expression is amorphous and desires a width from its // environment. For example, 'd5 has indefinite width, but // 5'd5 has a definite width. // This method is only really used within concatenation // expressions to check validity. virtual bool has_width() const; // Return the enumeration set that defines this expressions // enumeration type, or return nil if the expression is not // part of the enumeration. virtual const netenum_t*enumeration() const; // This method evaluates the expression and returns an // equivalent expression that is reduced as far as compile // time knows how. Essentially, this is designed to fold // constants. virtual NetExpr*eval_tree(); // Make a duplicate of myself, and subexpressions if I have // any. This is a deep copy operation. virtual NetExpr*dup_expr() const =0; // Evaluate the expression at compile time, a la within a // constant function. This is used by the constant function // evaluation function code, and the return value is an // allocated constant, or nil if the expression cannot be // evaluated for any reason. virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; // Get the Nexus that are the input to this // expression. Normally this descends down to the reference to // a signal that reads from its input. virtual NexusSet* nex_input(bool rem_out = true) =0; // Return a version of myself that is structural. This is used // for converting expressions to gates. The arguments are: // // des, scope: The context where this work is done // // root: The root expression of which this expression is a part. // // rise/fall/decay: Attach these delays to the driver for the // expression output. // // drive0/drive1: Attach these strengths to the driver for // the expression output. virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); protected: void expr_width(unsigned wid) { width_ = wid; } void cast_signed_base_(bool flag) { signed_flag_ = flag; } private: ivl_type_t net_type_; unsigned width_; bool signed_flag_; private: // not implemented NetExpr(const NetExpr&); NetExpr& operator=(const NetExpr&); }; class NetEArrayPattern : public NetExpr { public: NetEArrayPattern(ivl_type_t lv_type, std::vector&items); ~NetEArrayPattern(); inline size_t item_size() const { return items_.size(); } const NetExpr* item(size_t idx) const { return items_[idx]; } void expr_scan(struct expr_scan_t*) const; void dump(ostream&) const; NetEArrayPattern* dup_expr() const; NexusSet* nex_input(bool rem_out =true); private: std::vector items_; }; /* * The expression constant is slightly special, and is sometimes * returned from other classes that can be evaluated at compile * time. This class represents constant values in expressions. */ class NetEConst : public NetExpr { public: explicit NetEConst(const verinum&val); ~NetEConst(); const verinum&value() const; virtual void cast_signed(bool flag); virtual bool has_width() const; virtual ivl_variable_type_t expr_type() const; /* This method allows the constant value to be converted to an unsized value. This is used after evaluating a unsized constant expression. */ void trim(); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetEConst* dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); virtual NexusSet* nex_input(bool rem_out = true); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; private: verinum value_; }; class NetEConstEnum : public NetEConst { public: explicit NetEConstEnum(Definitions*scope, perm_string name, const netenum_t*enum_set, const verinum&val); ~NetEConstEnum(); perm_string name() const; const netenum_t*enumeration() const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetEConstEnum* dup_expr() const; private: Definitions*scope_; const netenum_t*enum_set_; perm_string name_; }; class NetEConstParam : public NetEConst { public: explicit NetEConstParam(NetScope*scope, perm_string name, const verinum&val); ~NetEConstParam(); perm_string name() const; const NetScope*scope() const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetEConstParam* dup_expr() const; private: NetScope*scope_; perm_string name_; }; /* * This class represents a constant real value. */ class NetECReal : public NetExpr { public: explicit NetECReal(const verireal&val); ~NetECReal(); const verireal&value() const; // This type has no self-determined width. This is false. virtual bool has_width() const; // The type of this expression is ET_REAL ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetECReal* dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); virtual NexusSet* nex_input(bool rem_out = true); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; private: verireal value_; }; class NetECRealParam : public NetECReal { public: explicit NetECRealParam(NetScope*scope, perm_string name, const verireal&val); ~NetECRealParam(); perm_string name() const; const NetScope*scope() const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetECRealParam* dup_expr() const; private: NetScope*scope_; perm_string name_; }; /* * The NetPartSelect device represents a netlist part select of a * signal vector. Pin 0 is a vector that is a part select of pin 1, * which connected to the NetNet of the signal being selected from. * * The part to be selected is the canonical (0-based) offset and the * specified number of bits (wid). * * If the offset is non-constant, then pin(2) is the input vector for * the selector. If this pin is present, then use the non-constant * selector as the input. * * The NetPartSelect can be output from the signal (i.e. reading a * part) or input into the signal. The DIR method gives the type of * the node. * * VP (Vector-to-Part) * Output pin 0 is the part select, and input pin 1 is connected to * the NetNet object. * * PV (Part-to-Vector) * Output pin 1 is connected to the NetNet, and input pin 0 is the * part select. In this case, the node is driving the NetNet. * * Note that whatever the direction that data is intended to flow, * pin-0 is the part select and pin-1 is connected to the NetNet. */ class NetPartSelect : public NetNode { public: // enum for the device direction enum dir_t { VP, PV}; explicit NetPartSelect(NetNet*sig, unsigned off, unsigned wid, dir_t dir, bool signed_flag__ = false); explicit NetPartSelect(NetNet*sig, NetNet*sel, unsigned wid, bool signed_flag__ = false); ~NetPartSelect(); unsigned base() const; unsigned width() const; inline dir_t dir() const { return dir_; } /* Is the select signal signed? */ inline bool signed_flag() const { return signed_flag_; } virtual void dump_node(ostream&, unsigned ind) const; bool emit_node(struct target_t*tgt) const; virtual void functor_node(Design*des, functor_t*fun); private: unsigned off_; unsigned wid_; dir_t dir_; bool signed_flag_; }; /* * This device supports simple substitution of a part within a wider * vector. For example, this: * * wire [7:0] foo = NetSubstitute(bar, bat, off); * * means that bar is a vector the same width as foo, bat is a narrower * vector. The off is a constant offset into the bar vector. This * looks something like this: * * foo = bar; * foo[off +: ] = bat; * * There is no direct way in Verilog to express this (as a single * device), it instead turns up in certain synthesis situation, * i.e. the example above. */ class NetSubstitute : public NetNode { public: NetSubstitute(NetNet*sig, NetNet*sub, unsigned wid, unsigned off); ~NetSubstitute(); inline unsigned width() const { return wid_; } inline unsigned base() const { return off_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*tgt) const; private: unsigned wid_; unsigned off_; }; /* * The NetBUFZ is a magic device that represents the continuous * assign, with the output being the target register and the input * the logic that feeds it. The netlist preserves the directional * nature of that assignment with the BUFZ. The target may elide it if * that makes sense for the technology. * * A NetBUFZ is transparent if strengths are passed through it without * change. A NetBUFZ is non-transparent if values other than HiZ are * converted to the strength of the output. */ class NetBUFZ : public NetNode { public: explicit NetBUFZ(NetScope*s, perm_string n, unsigned wid, bool transp); ~NetBUFZ(); unsigned width() const; bool transparent() const { return transparent_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: unsigned width_; bool transparent_; }; /* * This node is used to represent case equality in combinational * logic. Although this is not normally synthesizable, it makes sense * to support an abstract gate that can compare x and z. This node * always generates a single bit result, no matter the width of the * input. The elaboration, btw, needs to make sure the input widths * match. * * The case compare can be generated to handle ===/!==, or also * to test guards in the case/casez/casex statements. * * This pins are assigned as: * * 0 -- Output (always returns 0 or 1) * 1 -- Input * 2 -- Input (wildcard input for EQX and EQZ variants) */ class NetCaseCmp : public NetNode { public: enum kind_t { EEQ, // === NEQ, // !== XEQ, // casex guard tests ZEQ // casez guard tests }; public: explicit NetCaseCmp(NetScope*s, perm_string n, unsigned wid, kind_t eeq); ~NetCaseCmp(); unsigned width() const; // What kind of case compare? inline kind_t kind() const { return kind_; } virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: unsigned width_; const kind_t kind_; }; extern ostream& operator << (ostream&fd, NetCaseCmp::kind_t that); /* NOTE: This class should be replaced with the NetLiteral class * below, that is more general in that it supports different types of * values. * * This class represents instances of the LPM_CONSTANT device. The * node has only outputs and a constant value. The width is available * by getting the pin_count(), and the value bits are available one at * a time. There is no meaning to the aggregation of bits to form a * wide NetConst object, although some targets may have an easier time * detecting interesting constructs if they are combined. */ class NetConst : public NetNode { public: explicit NetConst(NetScope*s, perm_string n, verinum::V v); explicit NetConst(NetScope*s, perm_string n, const verinum&val); ~NetConst(); inline const verinum&value(void) const { return value_; } verinum::V value(unsigned idx) const; inline unsigned width() const { return value_.len(); } inline bool is_string() const { return value_.is_string(); } virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*, functor_t*); virtual void dump_node(ostream&, unsigned ind) const; private: verinum value_; }; /* * This class represents instances of the LPM_CONSTANT device. The * node has only outputs and a constant value. The width is available * by getting the pin_count(), and the value bits are available one at * a time. There is no meaning to the aggregation of bits to form a * wide NetConst object, although some targets may have an easier time * detecting interesting constructs if they are combined. */ class NetLiteral : public NetNode { public: // A read-valued literal. explicit NetLiteral(NetScope*s, perm_string n, const verireal&val); ~NetLiteral(); ivl_variable_type_t data_type() const; const verireal& value_real() const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*, functor_t*); virtual void dump_node(ostream&, unsigned ind) const; private: verireal real_; }; /* * This class represents all manner of logic gates. Pin 0 is OUTPUT and * all the remaining pins are INPUT. The BUFIF[01] gates have the * more specific pinout as follows: * * bufif * 0 -- output * 1 -- input data * 2 -- enable * * The pullup and pulldown gates have no inputs at all, and pin0 is * the output 1 or 0, depending on the gate type. It is the strength * of that value that is important. * * All these devices process vectors bitwise, so each bit can be * logically separated. The exception is the CONCAT gate, which is * really an abstract gate that takes the inputs and turns it into a * vector of bits. */ class NetLogic : public NetNode { public: enum TYPE { AND, BUF, BUFIF0, BUFIF1, CMOS, NAND, NMOS, NOR, NOT, NOTIF0, NOTIF1, OR, PULLDOWN, PULLUP, RCMOS, RNMOS, RPMOS, PMOS, XNOR, XOR }; explicit NetLogic(NetScope*s, perm_string n, unsigned pins, TYPE t, unsigned wid, bool is_cassign__=false); TYPE type() const; unsigned width() const; bool is_cassign() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*, functor_t*); private: TYPE type_; unsigned width_; bool is_cassign_; }; /* * This class represents a structural sign extension. The pin-0 is a * vector of the input pin-1 sign-extended. The input is taken to be * signed. This generally matches a hardware implementation of * replicating the top bit enough times to create the desired output * width. */ class NetSignExtend : public NetNode { public: explicit NetSignExtend(NetScope*s, perm_string n, unsigned wid); ~NetSignExtend(); unsigned width() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*, functor_t*); private: unsigned width_; }; /* * This class represents *reduction* logic operators. Certain boolean * logic operators have reduction forms which take in a vector and * return a single bit that is calculated by applying the logic * operation through the width of the input vector. These correspond * to reduction unary operators in Verilog. */ class NetUReduce : public NetNode { public: enum TYPE {NONE, AND, OR, XOR, NAND, NOR, XNOR}; NetUReduce(NetScope*s, perm_string n, TYPE t, unsigned wid); TYPE type() const; unsigned width() const; virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; virtual void functor_node(Design*, functor_t*); private: TYPE type_; unsigned width_; }; /* * The UDP is a User Defined Primitive from the Verilog source. Do not * expand it out any further than this in the netlist, as this can be * used to represent target device primitives. * * The UDP can be combinational or sequential. The sequential UDP * includes the current output in the truth table, and supports edges, * whereas the combinational does not and is entirely level sensitive. * In any case, pin 0 is an output, and all the remaining pins are * inputs. * * Set_table takes as input a string with one letter per pin. The * parser translates the written sequences to one of these. The * valid characters are: * * 0, 1, x -- The levels * r -- (01) * R -- (x1) * f -- (10) * F -- (x0) * P -- (0x) * N -- (1x) * * It also takes one of the following glob letters to represent more * than one item. * * p -- 01, 0x or x1 // check this with the lexer * n -- 10, 1x or x0 // check this with the lexer * ? -- 0, 1, or x * * -- any edge * + -- 01 or x1 * _ -- 10 or x0 (Note that this is not the output '-'.) * % -- 0x or 1x * * SEQUENTIAL * These objects have a single bit of memory. The logic table includes * an entry for the current value, and allows edges on the inputs. In * canonical form, only the entries that generate 0, 1 or - (no change) * are listed. * * COMBINATIONAL * The logic table is a map between the input levels and the * output. Each input pin can have the value 0, 1 or x and the output * can have the values 0 or 1. If the input matches nothing, the * output is x. In canonical form, only the entries that generate 0 or * 1 are listed. * */ class NetUDP : public NetNode { public: explicit NetUDP(NetScope*s, perm_string n, unsigned pins, PUdp*u); virtual bool emit_node(struct target_t*) const; virtual void dump_node(ostream&, unsigned ind) const; /* Use these methods to scan the truth table of the device. "first" returns the first item in the table, and "next" returns the next item in the table. The method will return false when the scan is done. */ bool first(string&inp, char&out) const; bool next(string&inp, char&out) const; unsigned rows() const { return udp->tinput.count(); } unsigned nin() const { return pin_count()-1; } bool is_sequential() const { return udp->sequential; } perm_string udp_name() const { return udp->name_; } perm_string udp_file() const { return udp->get_file(); } unsigned udp_lineno() const { return udp->get_lineno(); } char get_initial() const; unsigned port_count() const; string port_name(unsigned idx) const; private: mutable unsigned table_idx; PUdp *udp; }; enum DelayType { NO_DELAY, ZERO_DELAY, POSSIBLE_DELAY, DEFINITE_DELAY }; /* ========= * A process is a behavioral-model description. A process is a * statement that may be compound. The various statement types may * refer to places in a netlist (by pointing to nodes) but is not * linked into the netlist. However, elaborating a process may cause * special nodes to be created to handle things like events. */ class NetProc : public virtual LineInfo { public: explicit NetProc(); virtual ~NetProc(); // Find the nexa that are input by the statement. This is used // for example by @* to find the inputs to the process for the // sensitivity list. virtual NexusSet* nex_input(bool rem_out = true); // Find the nexa that are set by the statement. Add the output // values to the set passed as a parameter. virtual void nex_output(NexusSet&); // This method is called to emit the statement to the // target. The target returns true if OK, false for errors. virtual bool emit_proc(struct target_t*) const; // This method is used by the NetFuncDef object to evaluate a // constant function at compile time. The loc is the location // of the function call, and is used for error messages. The // ctx is a map of name to expression. This is for mapping // identifiers to values. The function returns true if the // processing succeeds, or false otherwise. virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; // This method is called by functors that want to scan a // process in search of matchable patterns. virtual int match_proc(struct proc_match_t*); // Return true if this represents the root of a combinational // process. Most process types are not. virtual bool is_asynchronous(); // Return true if this represents the root of a synchronous // process. Most process types are not. virtual bool is_synchronous(); // Synthesize as asynchronous logic, and return true. The // nex_out is where this function attaches its output // results. The accumulated_nex_out is used by sequential // blocks to show outputs from the previous code. virtual bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); // Synthesize as synchronous logic, and return true. That // means binding the outputs to the data port of a FF, and the // event inputs to a FF clock. Only some key NetProc sub-types // that have specific meaning in synchronous statements. The // remainder reduce to a call to synth_async that connects the // output to the Data input of the FF. // // The events argument is filled in be the NetEvWait // implementation of this method with the probes that it does // not itself pick off as a clock. These events should be // picked off by e.g. condit statements as set/reset inputs to // the flipflop being generated. virtual bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clock, NetBus&ff_ce, NetBus&ff_aclr, NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&events); virtual void dump(ostream&, unsigned ind) const; // Recursively checks to see if there is delay in this element. virtual DelayType delay_type() const; protected: bool synth_async_block_substatement_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&accumulated_nex_out, NetProc*substmt); private: friend class NetBlock; NetProc*next_; private: // not implemented NetProc(const NetProc&); NetProc& operator= (const NetProc&); }; class NetAlloc : public NetProc { public: NetAlloc(NetScope*); ~NetAlloc(); const string name() const; const NetScope* scope() const; virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; private: NetScope*scope_; }; /* * Procedural assignment is broken into a suite of classes. These * classes represent the various aspects of the assignment statement * in behavioral code. (The continuous assignment is *not* * represented here.) * * The NetAssignBase carries the common aspects of an assignment, * including the r-value. This class has no cares of blocking vs * non-blocking, however it carries nearly all the other properties * of the assignment statement. It is abstract because it does not * differentiate the virtual behaviors. * * The NetAssign and NetAssignNB classes are the concrete classes that * give the assignment its final, precise meaning. These classes fill * in the NetProc behaviors. * * The l-value of the assignment is a collection of NetAssign_ * objects that are connected to the structural netlist where the * assignment has its effect. The NetAssign_ class is not to be * derived from. * * The collection is arranged from lsb up to msb, and represents the * concatenation of l-values. The elaborator may collapse some * concatenations into a single NetAssign_. The "more" member of the * NetAssign_ object points to the next most significant bits of l-value. * * NOTE: The elaborator will make an effort to match the width of the * r-value to the width of the l-value, but targets and functions * should know that this is not a guarantee. */ class NetAssign_ { public: explicit NetAssign_(NetAssign_*nest); explicit NetAssign_(NetNet*sig); ~NetAssign_(); // This is so NetAssign_ objects can be passed to ivl_assert // and other macros that call this method. string get_fileline() const; // If this expression exists, then it is used to select a word // from an array/memory. NetExpr*word(); const NetExpr*word() const; NetScope*scope()const; // Get the base index of the part select, or 0 if there is no // part select. const NetExpr* get_base() const; ivl_select_type_t select_type() const; void set_word(NetExpr*); // Set a part select expression for the l-value vector. Note // that the expression calculates a CANONICAL bit address. void set_part(NetExpr* loff, unsigned wid, ivl_select_type_t = IVL_SEL_OTHER); // Set the member or property name if the signal type is a // class. void set_property(const perm_string&name); inline perm_string get_property(void) const { return member_; } // Get the width of the r-value that this node expects. This // method accounts for the presence of the mux, so it is not // necessarily the same as the pin_count(). unsigned lwidth() const; ivl_variable_type_t expr_type() const; // Get the expression type of the l-value. This may be // different from the type of the contained signal if for // example a darray is indexed. const ivl_type_s* net_type() const; // Return the enumeration type of this l-value, or nil if it's // not an enumeration. const netenum_t*enumeration() const; // Get the name of the underlying object. perm_string name() const; NetNet* sig() const; inline const NetAssign_* nest() const { return nest_; } // Mark that the synthesizer has worked with this l-value, so // when it is released, the l-value signal should be turned // into a wire. void turn_sig_to_wire_on_release(); // It is possible that l-values can have *inputs*, as well as // being outputs. For example foo[idx] = ... is the l-value // (NetAssign_ object) with a foo l-value and the input // expression idx. NexusSet* nex_input(bool rem_out = true); // Figuring out nex_output to process ultimately comes down to // this method. void nex_output(NexusSet&); // This pointer is for keeping simple lists. NetAssign_* more; void dump_lval(ostream&o) const; private: // Nested l-value. If this is set, sig_ must NOT be set! NetAssign_*nest_; NetNet *sig_; // Memory word index NetExpr*word_; // member/property if signal is a class. perm_string member_; bool turn_sig_to_wire_on_release_; // indexed part select base NetExpr*base_; unsigned lwid_; ivl_select_type_t sel_type_; }; class NetAssignBase : public NetProc { public: NetAssignBase(NetAssign_*lv, NetExpr*rv); virtual ~NetAssignBase() =0; // This is the (procedural) value that is to be assigned when // the assignment is executed. NetExpr*rval(); const NetExpr*rval() const; void set_rval(NetExpr*); NetAssign_* l_val(unsigned); const NetAssign_* l_val(unsigned) const; unsigned l_val_count() const; void set_delay(NetExpr*); const NetExpr* get_delay() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&o); // This returns the total width of the accumulated l-value. It // accounts for any grouping of NetAssign_ objects that might happen. unsigned lwidth() const; bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); // This dumps all the lval structures. void dump_lval(ostream&) const; virtual void dump(ostream&, unsigned ind) const; private: NetAssign_*lval_; NetExpr *rval_; NetExpr *delay_; }; class NetAssign : public NetAssignBase { public: explicit NetAssign(NetAssign_*lv, NetExpr*rv); explicit NetAssign(NetAssign_*lv, char op, NetExpr*rv); ~NetAssign(); bool is_asynchronous(); inline char assign_operator(void) const { return op_; } virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: bool eval_func_lval_(const LineInfo&loc, map&ctx, const NetAssign_*lval, NetExpr*rval_result) const; char op_; }; class NetAssignNB : public NetAssignBase { public: explicit NetAssignNB(NetAssign_*lv, NetExpr*rv, NetEvWait*ev, NetExpr*cnt); ~NetAssignNB(); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; unsigned nevents() const; const NetEvent*event(unsigned) const; const NetExpr* get_count() const; private: NetEvWait*event_; NetExpr*count_; }; /* * A block is stuff like begin-end blocks, that contain an ordered * list of NetProc statements. * * NOTE: The emit method calls the target->proc_block function but * does not recurse. It is up to the target-supplied proc_block * function to call emit_recurse. */ class NetBlock : public NetProc { public: enum Type { SEQU, PARA, PARA_JOIN_ANY, PARA_JOIN_NONE }; NetBlock(Type t, NetScope*subscope); ~NetBlock(); Type type() const { return type_; } NetScope* subscope() const { return subscope_; } void append(NetProc*); const NetProc*proc_first() const; const NetProc*proc_next(const NetProc*cur) const; bool evaluate_function(const LineInfo&loc, map&ctx) const; // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&events); // This version of emit_recurse scans all the statements of // the begin-end block sequentially. It is typically of use // for sequential blocks. void emit_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; private: const Type type_; NetScope*subscope_; NetProc*last_; }; /* * A CASE statement in the Verilog source leads, eventually, to one of * these. This is different from a simple conditional because of the * way the comparisons are performed. Also, it is likely that the * target may be able to optimize differently. * * Case can be one of three types: * EQ -- All bits must exactly match * EQZ -- z bits are don't care * EQX -- x and z bits are don't care. */ class NetCase : public NetProc { public: enum TYPE { EQ, EQX, EQZ }; NetCase(TYPE c, NetExpr*ex, unsigned cnt); ~NetCase(); void set_case(unsigned idx, NetExpr*ex, NetProc*st); void prune(); TYPE type() const; const NetExpr*expr() const { return expr_; } inline unsigned nitems() const { return items_.size(); } inline const NetExpr*expr(unsigned idx) const { return items_[idx].guard;} inline const NetProc*stat(unsigned idx) const { return items_[idx].statement; } virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&out); bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: bool evaluate_function_vect_(const LineInfo&loc, map&ctx) const; bool evaluate_function_real_(const LineInfo&loc, map&ctx) const; bool synth_async_casez_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); TYPE type_; struct Item { inline Item() : guard(0), statement(0) { } NetExpr*guard; NetProc*statement; }; NetExpr* expr_; std::vectoritems_; }; /* * The cassign statement causes the r-val net to be forced onto the * l-val reg when it is executed. The code generator is expected to * know what that means. */ class NetCAssign : public NetAssignBase { public: explicit NetCAssign(NetAssign_*lv, NetExpr*rv); ~NetCAssign(); virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; private: // not implemented NetCAssign(const NetCAssign&); NetCAssign& operator= (const NetCAssign&); }; /* * A condit represents a conditional. It has an expression to test, * and a pair of statements to select from. If the original statement * has empty clauses, then the NetProc for it will be a null pointer. */ class NetCondit : public NetProc { public: explicit NetCondit(NetExpr*ex, NetProc*i, NetProc*e); ~NetCondit(); const NetExpr*expr() const; NetExpr*expr(); NetProc* if_clause(); NetProc* else_clause(); // Replace the condition expression. void set_expr(NetExpr*ex); bool emit_recurse_if(struct target_t*) const; bool emit_recurse_else(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&o); bool is_asynchronous(); bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&events); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: NetExpr* expr_; NetProc*if_; NetProc*else_; }; /* * This represents the analog contribution statement. The l-val is a * branch expression, and the r-value is an arbitrary expression that * may include branches and real values. */ class NetContribution : public NetProc { public: explicit NetContribution(NetEAccess*lval, NetExpr*rval); ~NetContribution(); const NetEAccess* lval() const; const NetExpr* rval() const; virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; private: NetEAccess*lval_; NetExpr*rval_; }; /* * The procedural deassign statement (the opposite of assign) releases * any assign expressions attached to the bits of the reg. The * lval is the expression of the "deassign ;" statement with the * expr elaborated to a net. */ class NetDeassign : public NetAssignBase { public: explicit NetDeassign(NetAssign_*l); ~NetDeassign(); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; private: // not implemented NetDeassign(const NetDeassign&); NetDeassign& operator= (const NetDeassign&); }; /* * This node represents the behavioral disable statement. The Verilog * source that produces it looks like: * * disable ; * * Where the scope is a named block or a task. It cannot be a module * instance scope because module instances cannot be disabled. */ class NetDisable : public NetProc { public: explicit NetDisable(NetScope*tgt); ~NetDisable(); const NetScope*target() const; virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: NetScope*target_; private: // not implemented NetDisable(const NetDisable&); NetDisable& operator= (const NetDisable&); }; /* * The do/while statement is a condition that is tested at the end of * each iteration, and a statement (a NetProc) that is executed once and * then again as long as the condition is true. */ class NetDoWhile : public NetProc { public: NetDoWhile(NetExpr*c, NetProc*p) : cond_(c), proc_(p) { } const NetExpr*expr() const { return cond_; } void emit_proc_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: NetExpr* cond_; NetProc*proc_; }; /* * A NetEvent is an object that represents an event object, that is * objects declared like so in Verilog: * * event foo; * * Once an object of this type exists, behavioral code can wait on the * event or trigger the event. Event waits refer to this object, as do * the event trigger statements. The NetEvent class may have a name and * a scope. The name is a simple name (no hierarchy) and the scope is * the NetScope that contains the object. The scope member is written * by the NetScope object when the NetEvent is stored. * * The NetEvWait class represents a thread wait for an event. When * this statement is executed, it starts waiting on the * event. Conceptually, it puts itself on the event list for the * referenced event. When the event is triggered, the wait ends its * block and starts the associated statement. * * The NetEvTrig class represents trigger statements. Executing this * statement causes the referenced event to be triggered, which in * turn awakens the waiting threads. Each NetEvTrig object references * exactly one event object. * * The NetEvProbe class is the structural equivalent of the NetEvTrig, * in that it is a node and watches bit values that it receives. It * checks for edges then if appropriate triggers the associated * NetEvent. Each NetEvProbe references exactly one event object, and * the NetEvent objects have a list of NetEvProbe objects that * reference it. */ class NetEvent : public LineInfo { friend class NetScope; friend class NetEvProbe; friend class NetEvTrig; friend class NetEvWait; friend class NetEEvent; public: // The name of the event is the basename, and should not // include the scope. Also, the name passed here should be // perm-allocated. explicit NetEvent (perm_string n); ~NetEvent(); perm_string name() const; // Get information about probes connected to me. unsigned nprobe() const; NetEvProbe* probe(unsigned); const NetEvProbe* probe(unsigned) const; // Return the number of NetEvWait nodes that reference me. unsigned nwait() const; unsigned ntrig() const; unsigned nexpr() const; NetScope* scope(); const NetScope* scope() const; void nex_output(NexusSet&); // Locate the first event that matches my behavior and // monitors the same signals. void find_similar_event(list&); // This method replaces pointers to me with pointers to // that. It is typically used to replace similar events // located by the find_similar_event method. void replace_event(NetEvent*that); private: // This returns a nexus set if it represents possibly // asynchronous inputs, otherwise 0. NexusSet*nex_async_(); private: perm_string name_; // The NetScope class uses these to list the events. NetScope*scope_; NetEvent*snext_; // Use these methods to list the probes attached to me. NetEvProbe*probes_; // Use these methods to list the triggers attached to me. NetEvTrig* trig_; // Use This member to count references by NetEvWait objects. unsigned waitref_; struct wcell_ { NetEvWait*obj; struct wcell_*next; }; struct wcell_ *wlist_; // expression references, ala. task/funcs unsigned exprref_; private: // not implemented NetEvent(const NetEvent&); NetEvent& operator= (const NetEvent&); }; class NetEvTrig : public NetProc { friend class NetEvent; public: explicit NetEvTrig(NetEvent*tgt); ~NetEvTrig(); const NetEvent*event() const; virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; private: NetEvent*event_; // This is used to place me in the NetEvents lists of triggers. NetEvTrig*enext_; }; class NetEvWait : public NetProc { public: explicit NetEvWait(NetProc*st); ~NetEvWait(); void add_event(NetEvent*tgt); void replace_event(NetEvent*orig, NetEvent*repl); inline unsigned nevents() const { return events_.size(); } inline const NetEvent*event(unsigned idx) const { return events_[idx]; } inline NetEvent*event(unsigned idx) { return events_[idx]; } NetProc*statement(); virtual bool emit_proc(struct target_t*) const; bool emit_recurse(struct target_t*) const; virtual int match_proc(struct proc_match_t*); // It is possible that this is the root of a combinational // process. This method checks. virtual bool is_asynchronous(); // It is possible that this is the root of a synchronous // process? This method checks. virtual bool is_synchronous(); virtual void nex_output(NexusSet&out); virtual bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); virtual bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&events); virtual void dump(ostream&, unsigned ind) const; // This will ignore any statement. virtual void dump_inline(ostream&) const; virtual DelayType delay_type() const; private: NetProc*statement_; // Events that I might wait for. std::vectorevents_; }; ostream& operator << (ostream&out, const NetEvWait&obj); class NetEvProbe : public NetNode { friend class NetEvent; public: enum edge_t { ANYEDGE, POSEDGE, NEGEDGE }; explicit NetEvProbe(NetScope*s, perm_string n, NetEvent*tgt, edge_t t, unsigned p); ~NetEvProbe(); edge_t edge() const; NetEvent* event(); const NetEvent* event() const; void find_similar_probes(list&); virtual bool emit_node(struct target_t*) const; virtual void dump_node(ostream&, unsigned ind) const; private: NetEvent*event_; edge_t edge_; // The NetEvent class uses this to list me. NetEvProbe*enext_; }; /* * The force statement causes the r-val net to be forced onto the * l-val net when it is executed. The code generator is expected to * know what that means. */ class NetForce : public NetAssignBase { public: explicit NetForce(NetAssign_*l, NetExpr*r); ~NetForce(); virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; }; /* * A forever statement is executed over and over again forever. Or * until its block is disabled. */ class NetForever : public NetProc { public: explicit NetForever(NetProc*s); ~NetForever(); void emit_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: NetProc*statement_; }; class NetForLoop : public NetProc { public: explicit NetForLoop(NetNet*index, NetExpr*initial_expr, NetExpr*cond, NetProc*sub, NetProc*step); ~NetForLoop(); void wrap_up(); void emit_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out); private: NetNet*index_; NetExpr*init_expr_; NetExpr*condition_; NetProc*statement_; NetProc*step_statement_; // The code generator needs to see this rewritten as a while // loop with synthetic statements. This is a hack that I // should probably take out later as the ivl_target learns // about for loops. NetBlock*as_block_; }; class NetFree : public NetProc { public: NetFree(NetScope*); ~NetFree(); const string name() const; const NetScope* scope() const; virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; private: NetScope*scope_; }; /* * A function definition is elaborated just like a task, though by now * it is certain that the first parameter (a phantom parameter) is the * output and all the remaining parameters are the inputs. This makes * for easy code generation in targets that support behavioral * descriptions. * * The NetNet array that is passed in as a parameter is the set of * signals that make up its parameter list. These are all internal to * the scope of the function. */ class NetFuncDef : public NetBaseDef { public: NetFuncDef(NetScope*, NetNet*result, const std::vector&po, const std::vector&pd); ~NetFuncDef(); const NetNet*return_sig() const; // When we want to evaluate the function during compile time, // use this method to pass in the argument and get out a // result. The result should be a constant. If the function // cannot evaluate to a constant, this returns nil. NetExpr* evaluate_function(const LineInfo&loc, const std::vector&args) const; void dump(ostream&, unsigned ind) const; private: NetNet*result_sig_; }; /* * This class represents delay statements of the form: * * # * * Where the statement may be null. The delay is evaluated at * elaboration time to make a constant unsigned long that is the delay * in simulation ticks. * * If the delay expression is non-constant, construct the NetPDelay * object with a NetExpr* instead of the d value, and use the expr() * method to get the expression. If expr() returns 0, use the delay() * method to get the constant delay. */ class NetPDelay : public NetProc { public: NetPDelay(uint64_t d, NetProc*st); NetPDelay(NetExpr* d, NetProc*st); ~NetPDelay(); uint64_t delay() const; const NetExpr*expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; bool emit_proc_recurse(struct target_t*) const; private: uint64_t delay_; NetExpr*expr_; NetProc*statement_; }; /* * A repeat statement is executed some fixed number of times. */ class NetRepeat : public NetProc { public: explicit NetRepeat(NetExpr*e, NetProc*s); ~NetRepeat(); const NetExpr*expr() const; void emit_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: NetExpr*expr_; NetProc*statement_; }; /* * The procedural release statement (the opposite of force) releases * any force expressions attached to the bits of the wire or reg. The * lval is the expression of the "release ;" statement with the * expr elaborated to a net. */ class NetRelease : public NetAssignBase { public: explicit NetRelease(NetAssign_*l); ~NetRelease(); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; private: }; /* * The NetSTask class is a call to a system task. These kinds of tasks * are generally handled very simply in the target. They certainly are * handled differently from user defined tasks because ivl knows all * about the user defined tasks. */ class NetSTask : public NetProc { public: NetSTask(const char*na, ivl_sfunc_as_task_t sfat, const std::vector&); ~NetSTask(); const char* name() const; ivl_sfunc_as_task_t sfunc_as_task() const; unsigned nparms() const; const NetExpr* parm(unsigned idx) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: const char* name_; ivl_sfunc_as_task_t sfunc_as_task_; std::vectorparms_; }; /* * This class represents an elaborated class definition. NetUTask * classes may refer to objects of this type to get the meaning of the * defined task. * * The task also introduces a scope, and the parameters are actually * reg objects in the new scope. The task is called by the calling * thread assigning (blocking assignment) to the in and inout * parameters, then invoking the thread, and finally assigning out the * output and inout variables. The variables accessible as ports are * also elaborated and accessible as ordinary reg objects. */ class NetTaskDef : public NetBaseDef { public: NetTaskDef(NetScope*n, const vector&po, const std::vector&pd); ~NetTaskDef(); void dump(ostream&, unsigned) const; DelayType delay_type() const; private: // not implemented NetTaskDef(const NetTaskDef&); NetTaskDef& operator= (const NetTaskDef&); }; /* * The NetELast expression node takes as an argument a net, that is * intended to be a queue or dynamic array object. The return value is * the index of the last item in the node. This is intended to * implement the '$' is the expression "foo[$]". */ class NetELast : public NetExpr { public: explicit NetELast(NetNet*sig); ~NetELast(); inline const NetNet*sig() const { return sig_; } virtual ivl_variable_type_t expr_type() const; virtual void dump(std::ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetELast*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); private: NetNet*sig_; }; /* * This node represents a function call in an expression. The object * contains a pointer to the function definition, which is used to * locate the value register and input expressions. */ class NetEUFunc : public NetExpr { public: NetEUFunc(NetScope*, NetScope*, NetESignal*, std::vector&, bool); ~NetEUFunc(); const NetESignal*result_sig() const; unsigned parm_count() const; const NetExpr* parm(unsigned idx) const; const NetScope* func() const; virtual ivl_variable_type_t expr_type() const; virtual void dump(ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual NetExpr* eval_tree(); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); private: NetScope*scope_; NetScope*func_; NetESignal*result_sig_; std::vector parms_; bool need_const_; private: // not implemented NetEUFunc(const NetEUFunc&); NetEUFunc& operator= (const NetEUFunc&); }; /* * A call to a nature access function for a branch. */ class NetEAccess : public NetExpr { public: explicit NetEAccess(NetBranch*br, ivl_nature_t nat); ~NetEAccess(); ivl_nature_t get_nature() const { return nature_; } NetBranch* get_branch() const { return branch_; } virtual ivl_variable_type_t expr_type() const; virtual void dump(ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEAccess*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); private: NetBranch*branch_; ivl_nature_t nature_; }; /* * A call to a user defined task is elaborated into this object. This * contains a pointer to the elaborated task definition, but is a * NetProc object so that it can be linked into statements. */ class NetUTask : public NetProc { public: NetUTask(NetScope*); ~NetUTask(); const string name() const; const NetScope* task() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; private: NetScope*task_; }; /* * The while statement is a condition that is tested in the front of * each iteration, and a statement (a NetProc) that is executed as * long as the condition is true. */ class NetWhile : public NetProc { public: NetWhile(NetExpr*c, NetProc*p) : cond_(c), proc_(p) { } const NetExpr*expr() const { return cond_; } void emit_proc_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: NetExpr* cond_; NetProc*proc_; }; /* * The is the top of any process. It carries the type (initial or * always) and a pointer to the statement, probably a block, that * makes up the process. */ class NetProcTop : public LineInfo, public Attrib { public: NetProcTop(NetScope*s, ivl_process_type_t t, class NetProc*st); ~NetProcTop(); ivl_process_type_t type() const { return type_; } NetProc*statement(); const NetProc*statement() const; NetScope*scope(); const NetScope*scope() const; /* Return true if this process represents combinational logic. */ bool is_asynchronous() const; /* Create asynchronous logic from this thread and return true, or return false if that cannot be done. */ bool synth_async(Design*des); /* Return true if this process represents synchronous logic. */ bool is_synchronous(); /* Create synchronous logic from this thread and return true, or return false if that cannot be done. */ bool synth_sync(Design*des); void dump(ostream&, unsigned ind) const; bool emit(struct target_t*tgt) const; private: const ivl_process_type_t type_; NetProc*const statement_; NetScope*scope_; friend class Design; NetProcTop*next_; }; class NetAnalogTop : public LineInfo, public Attrib { public: NetAnalogTop(NetScope*scope, ivl_process_type_t t, NetProc*st); ~NetAnalogTop(); ivl_process_type_t type() const { return type_; } NetProc*statement(); const NetProc*statement() const; NetScope*scope(); const NetScope*scope() const; void dump(ostream&, unsigned ind) const; bool emit(struct target_t*tgt) const; private: const ivl_process_type_t type_; NetProc* statement_; NetScope*scope_; friend class Design; NetAnalogTop*next_; }; /* * This class represents a binary operator, with the left and right * operands and a single character for the operator. The operator * values are: * * ^ -- Bit-wise exclusive OR * + -- Arithmetic add * - -- Arithmetic minus * * -- Arithmetic multiply * / -- Arithmetic divide * % -- Arithmetic modulus * p -- Arithmetic power (**) * & -- Bit-wise AND * | -- Bit-wise OR * < -- Less than * > -- Greater than * e -- Logical equality (==) * E -- Case equality (===) * L -- Less or equal * G -- Greater or equal * n -- Logical inequality (!=) * N -- Case inequality (!==) * a -- Logical AND (&&) * A -- Bitwise NAND (~&) * o -- Logical OR (||) * O -- Bit-wise NOR (~|) * l -- Left shift (<<) * r -- Right shift (>>) * R -- signed right shift (>>>) * X -- Bitwise exclusive NOR (~^) * m -- min(a,b) * M -- max(a,b) */ class NetEBinary : public NetExpr { public: NetEBinary(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBinary(); const NetExpr*left() const { return left_; } const NetExpr*right() const { return right_; } char op() const { return op_; } // A binary expression node only has a definite // self-determinable width if the operands both have definite // widths. virtual bool has_width() const; virtual NetEBinary* dup_expr() const; virtual NetExpr* eval_tree(); virtual NetExpr* evaluate_function(const LineInfo&loc, map&ctx) const; virtual NexusSet* nex_input(bool rem_out = true); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; protected: char op_; NetExpr* left_; NetExpr* right_; virtual NetExpr* eval_arguments_(const NetExpr*l, const NetExpr*r) const; }; /* * The addition operators have slightly more complex width * calculations because there is the optional carry bit that can be * used. The operators covered by this class are: * + -- Arithmetic add * - -- Arithmetic minus */ class NetEBAdd : public NetEBinary { public: NetEBAdd(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBAdd(); virtual ivl_variable_type_t expr_type() const; virtual NetEBAdd* dup_expr() const; virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetExpr * eval_arguments_(const NetExpr*l, const NetExpr*r) const; NetECReal* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* * This class represents the integer division operators. * / -- Divide * % -- Modulus */ class NetEBDiv : public NetEBinary { public: NetEBDiv(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBDiv(); virtual ivl_variable_type_t expr_type() const; virtual NetEBDiv* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetExpr* eval_arguments_(const NetExpr*l, const NetExpr*r) const; NetExpr* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* * The bitwise binary operators are represented by this class. This is * a specialization of the binary operator, so is derived from * NetEBinary. The particular constraints on these operators are that * operand and result widths match exactly, and each bit slice of the * operation can be represented by a simple gate. The operators * covered by this class are: * * ^ -- Bit-wise exclusive OR * & -- Bit-wise AND * | -- Bit-wise OR * O -- Bit-wise NOR * X -- Bit-wise XNOR (~^) */ class NetEBBits : public NetEBinary { public: NetEBBits(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBBits(); virtual NetEBBits* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetEConst* eval_arguments_(const NetExpr*l, const NetExpr*r) const; }; /* * The binary comparison operators are handled by this class. This * this case the bit width of the expression is 1 bit, and the * operands take their natural widths. The supported operators are: * * < -- Less than * > -- Greater than * e -- Logical equality (==) * E -- Case equality (===) * L -- Less or equal (<=) * G -- Greater or equal (>=) * n -- Logical inequality (!=) * N -- Case inequality (!==) */ class NetEBComp : public NetEBinary { public: NetEBComp(char op, NetExpr*l, NetExpr*r); ~NetEBComp(); /* A compare expression has a definite width. */ virtual bool has_width() const; virtual ivl_variable_type_t expr_type() const; virtual NetEBComp* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetEConst* must_be_leeq_(const NetExpr*le, const verinum&rv, bool eq_flag) const; NetEConst*eval_arguments_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_eqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; NetEConst*eval_eqeq_real_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; NetEConst*eval_less_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_leeq_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_leeq_real_(const NetExpr*le, const NetExpr*ri, bool eq_flag) const; NetEConst*eval_gt_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_gteq_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; }; /* * The binary logical operators are those that return boolean * results. The supported operators are: * * a -- Logical AND (&&) * o -- Logical OR (||) */ class NetEBLogic : public NetEBinary { public: NetEBLogic(char op, NetExpr*l, NetExpr*r); ~NetEBLogic(); virtual NetEBLogic* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetEConst* eval_arguments_(const NetExpr*l, const NetExpr*r) const; NetEConst* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* * Support the binary min(l,r) and max(l,r) operators. The opcodes * supported are: * * m -- min * M -- max */ class NetEBMinMax : public NetEBinary { public: NetEBMinMax(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMinMax(); virtual ivl_variable_type_t expr_type() const; private: NetExpr* eval_arguments_(const NetExpr*l, const NetExpr*r) const; NetExpr* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* * Support the binary multiplication (*) operator. */ class NetEBMult : public NetEBinary { public: NetEBMult(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMult(); virtual ivl_variable_type_t expr_type() const; virtual NetEBMult* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetExpr* eval_arguments_(const NetExpr*l, const NetExpr*r) const; NetExpr* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* * Support the binary power (**) operator. */ class NetEBPow : public NetEBinary { public: NetEBPow(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBPow(); virtual ivl_variable_type_t expr_type() const; virtual NetEBPow* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetExpr* eval_arguments_(const NetExpr*l, const NetExpr*r) const; NetExpr* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* * Support the binary shift operators. The supported operators are: * * l -- left shift (<<) * r -- right shift (>>) * R -- right shift arithmetic (>>>) */ class NetEBShift : public NetEBinary { public: NetEBShift(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBShift(); // A shift expression only needs the left expression to have a // definite width to give the expression a definite width. virtual bool has_width() const; virtual NetEBShift* dup_expr() const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetEConst* eval_arguments_(const NetExpr*l, const NetExpr*r) const; }; /* * This expression node supports the concat expression. This is an * operator that just glues the results of many expressions into a * single value. * * Note that the class stores the parameter expressions in source code * order. That is, the parm(0) is placed in the most significant * position of the result. */ class NetEConcat : public NetExpr { public: NetEConcat(unsigned cnt, unsigned repeat, ivl_variable_type_t vt); ~NetEConcat(); // Manipulate the parameters. void set(unsigned idx, NetExpr*e); unsigned repeat() const { return repeat_; } unsigned nparms() const { return parms_.size() ; } NetExpr* parm(unsigned idx) const { return parms_[idx]; } virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool has_width() const; virtual NetEConcat* dup_expr() const; virtual NetEConst* eval_tree(); virtual NetExpr* evaluate_function(const LineInfo&loc, map&ctx) const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; private: std::vectorparms_; unsigned repeat_; ivl_variable_type_t expr_type_; NetEConst* eval_arguments_(const vector&vals, unsigned gap) const; }; /* * This expression node supports bit/part selects from general * expressions. The sub-expression is self-sized, and has bits * selected from it. The base is the expression that identifies the * lsb of the expression, and the wid is the width of the part select, * or 1 for a bit select. No matter what the subexpression is, the * base is translated in canonical bits. It is up to the elaborator * to figure this out and adjust the expression if the subexpression * has a non-canonical base or direction. * * If the base expression is null, then this expression node can be * used to express width expansion, signed or unsigned depending on * the has_sign() flag. */ class NetESelect : public NetExpr { public: NetESelect(NetExpr*exp, NetExpr*base, unsigned wid, ivl_select_type_t sel_type = IVL_SEL_OTHER); ~NetESelect(); const NetExpr*sub_expr() const; const NetExpr*select() const; ivl_select_type_t select_type() const; // The type of a NetESelect is the base type of the // sub-expression. virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEConst* eval_tree(); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; virtual NetESelect* dup_expr() const; virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); virtual void dump(ostream&) const; private: NetExpr*expr_; NetExpr*base_; ivl_select_type_t sel_type_; }; /* * This node is for representation of named events. */ class NetEEvent : public NetExpr { public: NetEEvent(NetEvent*); ~NetEEvent(); const NetEvent* event() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEEvent* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; private: NetEvent*event_; }; /* * This class is a special (and magical) expression node type that * represents enumeration types. These can only be found as parameters * to NetSTask objects. */ class NetENetenum : public NetExpr { public: NetENetenum(const netenum_t*); ~NetENetenum(); const netenum_t* netenum() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetENetenum* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; private: const netenum_t*netenum_; }; class NetENew : public NetExpr { public: // Make class object explicit NetENew(ivl_type_t); // dynamic array of objects. explicit NetENew(ivl_type_t, NetExpr*size, NetExpr* init_val=0); ~NetENew(); inline ivl_type_t get_type() const { return obj_type_; } inline const NetExpr*size_expr() const { return size_; } inline const NetExpr*init_expr() const { return init_val_; } virtual ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetENew* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; private: ivl_type_t obj_type_; NetExpr*size_; NetExpr*init_val_; }; /* * The NetENull node represents the SystemVerilog (null) * expression. This is always a null class handle. */ class NetENull : public NetExpr { public: NetENull(); ~NetENull(); virtual void expr_scan(struct expr_scan_t*) const; virtual NetENull* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; }; /* * The NetEProperty represents a SystemVerilog property select of a * class object. In SV, the expression would look like "a.b", where * the "a" is the signal (the NetNet) and "b" is the property name. * * The canon_index is an optional expression to address an element for * parameters that are arrays. */ class NetEProperty : public NetExpr { public: NetEProperty(NetNet*n, perm_string pname, NetExpr*canon_index =0); ~NetEProperty(); inline const NetNet* get_sig() const { return net_; } inline size_t property_idx() const { return pidx_; } inline const NetExpr*get_index() const { return index_; } public: // Overridden methods ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEProperty* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; private: NetNet*net_; size_t pidx_; NetExpr*index_; }; /* * This class is a special (and magical) expression node type that * represents scope names. These can only be found as parameters to * NetSTask objects. */ class NetEScope : public NetExpr { public: NetEScope(NetScope*); ~NetEScope(); const NetScope* scope() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEScope* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; private: NetScope*scope_; }; /* * This node represents a system function call in an expression. The * object contains the name of the system function, which the backend * uses to do VPI matching. */ class NetESFunc : public NetExpr { public: NetESFunc(const char*name, ivl_variable_type_t t, unsigned width, unsigned nprms); NetESFunc(const char*name, ivl_type_t rtype, unsigned nprms); NetESFunc(const char*name, const netenum_t*enum_type, unsigned nprms); ~NetESFunc(); const char* name() const; unsigned nparms() const; void parm(unsigned idx, NetExpr*expr); NetExpr* parm(unsigned idx); const NetExpr* parm(unsigned idx) const; virtual NetExpr* eval_tree(); virtual NetExpr* evaluate_function(const LineInfo&loc, map&ctx) const; virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual const netenum_t* enumeration() const; virtual void dump(ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetESFunc*dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); private: /* Use the 32 bit ID as follows: * The lower sixteen bits are used to identify the individual * functions. * * The top sixteen bits are used to indicate the number of * arguments the function can take by bit position. If more * than one bit is set the argument can take a different number * of arguments. This varies from 0 to 14 with the MSB indicating * fifteen or more (an unbounded value). For example all bit set * except for the LSB indicate 1 or more arguments are allowed. */ enum ID { NOT_BUILT_IN = 0x0, /* Available in all version of Verilog/SystemVerilog. */ ITOR = 0x00020001, /* $itor takes one argument. */ RTOI = 0x00020002, /* $rtoi takes one argument. */ /* Available in Verilog 2005 and later. */ ACOS = 0x00020003, /* $acos takes one argument. */ ACOSH = 0x00020004, /* $acosh takes one argument. */ ASIN = 0x00020005, /* $asin takes one argument. */ ASINH = 0x00020006, /* $asinh takes one argument. */ ATAN = 0x00020007, /* $atan takes one argument. */ ATANH = 0x00020008, /* $atanh takes one argument. */ ATAN2 = 0x00040009, /* $atan2 takes two argument. */ CEIL = 0x0002000a, /* $ceil takes one argument. */ CLOG2 = 0x0002000b, /* $clog2 takes one argument. */ COS = 0x0002000c, /* $cos takes one argument. */ COSH = 0x0002000d, /* $cosh takes one argument. */ EXP = 0x0002000e, /* $exp takes one argument. */ FLOOR = 0x0002000f, /* $floor takes one argument. */ HYPOT = 0x00040010, /* $hypot takes two argument. */ LN = 0x00020011, /* $ln takes one argument. */ LOG10 = 0x00020012, /* $log10 takes one argument. */ POW = 0x00040013, /* $pow takes two argument. */ SIN = 0x00020014, /* $sin takes one argument. */ SINH = 0x00020015, /* $sinh takes one argument. */ SQRT = 0x00020016, /* $sqrt takes one argument. */ TAN = 0x00020017, /* $tan takes one argument. */ TANH = 0x00020018, /* $tanh takes one argument. */ /* Added in SystemVerilog 2005 and later. */ DIMS = 0x00020019, /* $dimensions takes one argument. */ HIGH = 0x0006001a, /* $high takes one or two arguments. */ INCR = 0x0006001b, /* $increment takes one or two arguments. */ LEFT = 0x0006001c, /* $left takes one or two arguments. */ LOW = 0x0006001d, /* $low takes one or two arguments. */ RIGHT = 0x0006001e, /* $right takes one or two arguments. */ SIZE = 0x0006001f, /* $size takes one or two arguments. */ UPDIMS = 0x00020020, /* $unpacked_dimensions takes one argument. */ ISUNKN = 0x00020021, /* $isunknown takes one argument. */ ONEHT = 0x00020022, /* $onehot takes one argument. */ ONEHT0 = 0x00020023, /* $onehot0 takes one argument. */ /* Added in SystemVerilog 2009 and later. */ CTONES = 0x00020024, /* $countones takes one argument. */ /* Added in SystemVerilog 2012 and later. */ CTBITS = 0xfffe0025, /* $countbits takes one or more arguments. */ /* Added as Icarus extensions to Verilog-A. */ ABS = 0x00020026, /* $abs takes one argument. */ MAX = 0x00040027, /* $max takes two argument. */ MIN = 0x00040028, /* $min takes two argument. */ /* A dummy value to properly close the enum. */ DUMMY = 0xffffffff }; bool takes_nargs_(ID func, unsigned nargs) { if (nargs > 15) nargs = 15; return func & (1 << (nargs + 16)); } const char* name_; ivl_variable_type_t type_; const netenum_t*enum_type_; std::vectorparms_; ID built_in_id_() const; NetExpr* evaluate_one_arg_(ID id, const NetExpr*arg) const; NetExpr* evaluate_two_arg_(ID id, const NetExpr*arg0, const NetExpr*arg1) const; NetEConst* evaluate_rtoi_(const NetExpr*arg) const; NetECReal* evaluate_itor_(const NetExpr*arg) const; NetEConst* evaluate_clog2_(const NetExpr*arg) const; NetECReal* evaluate_math_one_arg_(ID id, const NetExpr*arg) const; NetECReal* evaluate_math_two_arg_(ID id, const NetExpr*arg0, const NetExpr*arg1) const; NetExpr* evaluate_abs_(const NetExpr*arg) const; NetExpr* evaluate_min_max_(ID id, const NetExpr*arg0, const NetExpr*arg1) const; /* Constant SystemVerilog functions. */ NetEConst* evaluate_countones_(const NetExpr*arg) const; NetEConst* evaluate_dimensions_(const NetExpr*arg) const; NetEConst* evaluate_isunknown_(const NetExpr*arg) const; NetEConst* evaluate_onehot_(const NetExpr*arg) const; NetEConst* evaluate_onehot0_(const NetExpr*arg) const; NetEConst* evaluate_unpacked_dimensions_(const NetExpr*arg) const; /* This value is used as a default when the array functions are * called with a single argument. */ static const NetEConst*const_one_; NetEConst* evaluate_array_funcs_(ID id, const NetExpr*arg0, const NetExpr*arg1) const; NetEConst* evaluate_countbits_(const NetExpr*arg0, const NetExpr*arg1) const; public: bool is_built_in() const { return built_in_id_() != NOT_BUILT_IN; }; private: // not implemented NetESFunc(const NetESFunc&); NetESFunc& operator= (const NetESFunc&); }; class NetEShallowCopy : public NetExpr { public: // Make a shallow copy from arg2 into arg1. explicit NetEShallowCopy(NetExpr*arg1, NetExpr*arg2); ~NetEShallowCopy(); virtual ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEShallowCopy* dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&os) const; void expr_scan_oper1(struct expr_scan_t*) const; void expr_scan_oper2(struct expr_scan_t*) const; private: NetExpr*arg1_; NetExpr*arg2_; }; /* * This class represents the ternary (?:) operator. It has 3 * expressions, one of which is a condition used to select which of * the other two expressions is the result. */ class NetETernary : public NetExpr { public: NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag); ~NetETernary(); const NetExpr*cond_expr() const; const NetExpr*true_expr() const; const NetExpr*false_expr() const; virtual NetETernary* dup_expr() const; virtual NetExpr* eval_tree(); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); public: static bool test_operand_compat(ivl_variable_type_t tru, ivl_variable_type_t fal); private: NetExpr* blended_arguments_(const NetExpr*t, const NetExpr*f) const; NetExpr*cond_; NetExpr*true_val_; NetExpr*false_val_; }; /* * This class represents a unary operator, with the single operand * and a single character for the operator. The operator values are: * * ~ -- Bit-wise negation * ! -- Logical negation * & -- Reduction AND * | -- Reduction OR * ^ -- Reduction XOR * + -- * - -- * A -- Reduction NAND (~&) * N -- Reduction NOR (~|) * X -- Reduction NXOR (~^ or ^~) * m -- abs(x) (i.e. "magnitude") * v -- Cast from real to integer (vector) * 2 -- Cast from real or logic (vector) to bool (vector) * r -- Cast from integer (vector) to real * i -- post-increment * I -- pre-increment * d -- post-decrement * D -- pre-decrement */ class NetEUnary : public NetExpr { public: NetEUnary(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUnary(); char op() const { return op_; } const NetExpr* expr() const { return expr_; } virtual NetEUnary* dup_expr() const; virtual NetExpr* eval_tree(); virtual NetExpr* evaluate_function(const LineInfo&loc, map&ctx) const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; protected: char op_; NetExpr* expr_; private: virtual NetExpr* eval_arguments_(const NetExpr*ex) const; virtual NetExpr* eval_tree_real_(const NetExpr*ex) const; }; class NetEUBits : public NetEUnary { public: NetEUBits(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUBits(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUBits* dup_expr() const; virtual ivl_variable_type_t expr_type() const; }; class NetEUReduce : public NetEUnary { public: NetEUReduce(char op, NetExpr*ex); ~NetEUReduce(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUReduce* dup_expr() const; virtual ivl_variable_type_t expr_type() const; private: virtual NetEConst* eval_arguments_(const NetExpr*ex) const; virtual NetEConst* eval_tree_real_(const NetExpr*ex) const; }; class NetECast : public NetEUnary { public: NetECast(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetECast(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetECast* dup_expr() const; virtual ivl_variable_type_t expr_type() const; virtual void dump(ostream&) const; private: virtual NetExpr* eval_arguments_(const NetExpr*ex) const; }; /* * When a signal shows up in an expression, this type represents * it. From this the expression can get any kind of access to the * structural signal, including arrays. * * The NetESignal may refer to an array, if the word_index is * included. This expression calculates the index of the word in the * array. It may only be nil if the expression refers to the whole * array, and that is legal only in limited situation. */ class NetESignal : public NetExpr { public: NetESignal(NetNet*n); NetESignal(NetNet*n, NetExpr*word_index); ~NetESignal(); perm_string name() const; virtual NetESignal* dup_expr() const; NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); NexusSet* nex_input(bool rem_out = true); const netenum_t*enumeration() const; virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; // This is the expression for selecting an array word, if this // signal refers to an array. const NetExpr* word_index() const; // This is the width of the vector that this signal refers to. unsigned vector_width() const; // Point back to the signal that this expression node references. const NetNet* sig() const; NetNet* sig(); // Declared vector dimensions for the signal. long msi() const; long lsi() const; virtual ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; private: NetNet*net_; const netenum_t*enum_type_; // Expression to select a word from the net. NetExpr*word_; }; /* * The Design object keeps a list of work items for processing * elaboration. This is the type of those work items. */ struct elaborator_work_item_t { explicit elaborator_work_item_t(Design*d) : des(d) { } virtual ~elaborator_work_item_t() { } virtual void elaborate_runrun() =0; protected: Design*des; }; /* * This class contains an entire design. It includes processes and a * netlist, and can be passed around from function to function. */ class Design : public Definitions { public: Design(); ~Design(); /* We need to pass the tool delay selection for $sdf_annotate. */ enum delay_sel_t { MIN, TYP, MAX }; void set_delay_sel(delay_sel_t sel); const char* get_delay_sel() const; /* The flags are a generic way of accepting command line parameters/flags and passing them to the processing steps that deal with the design. The compilation driver sets the entire flags map after elaboration is done. Subsequent steps can then use the get_flag() function to get the value of an interesting key. */ void set_flags(const map&f) { flags_ = f; } const char* get_flag(const string&key) const; NetScope* make_root_scope(perm_string name, bool program_block, bool is_interface); NetScope* find_root_scope(); std::list find_root_scopes() const; NetScope* make_package_scope(perm_string name); std::list find_package_scopes() const; /* Attempt to set the precision to the specified value. If the precision is already more precise, the keep the precise setting. This is intended to hold the simulation precision for use throughout the entire design. */ void set_precision(int val); int get_precision() const; /* This function takes a delay value and a scope, and returns the delay value scaled to the precision of the design. */ uint64_t scale_to_precision(uint64_t, const NetScope*)const; /* Look up a scope. If no starting scope is passed, then the path is taken as an absolute scope name. Otherwise, the scope is located starting at the passed scope and working up if needed. */ NetScope* find_scope(const hname_t&path) const; NetScope* find_scope(NetScope*, const hname_t&name, NetScope::TYPE type = NetScope::MODULE) const; NetScope* find_package(perm_string name) const; // Note: Try to remove these versions of find_scope. Avoid // using these in new code, use the above forms (or // symbol_search) instead. NetScope* find_scope(const std::list&path) const; NetScope* find_scope(NetScope*, const std::list&path, NetScope::TYPE type = NetScope::MODULE) const; /* These members help manage elaboration of scopes. When we get to a point in scope elaboration where we want to put off a scope elaboration, an object of scope_elaboration_t is pushed onto the scope_elaborations list. The scope elaborator will go through this list elaborating scopes until the list is empty. */ listelaboration_work_list; void run_elaboration_work(void); set defparams_later; // PARAMETERS void run_defparams(); void evaluate_parameters(); // Look for defparams that never matched, and print warnings. void residual_defparams(); // Do elaborate_sig for objects in $root scope. void root_elaborate_sig(void); void root_elaborate(void); /* This method locates a signal, starting at a given scope. The name parameter may be partially hierarchical, so this method, unlike the NetScope::find_signal method, handles global name binding. */ NetNet*find_signal(NetScope*scope, pform_name_t path); // Functions NetFuncDef* find_function(NetScope*scope, const pform_name_t&key); // Tasks NetScope* find_task(NetScope*scope, const pform_name_t&name); void add_root_task(NetScope*tscope, PTaskFunc*tf); std::list find_roottask_scopes(void) const; // Find a class in the $root scope. void add_class(netclass_t*cl, PClass*pclass); netclass_t* find_class(perm_string name) const; // NODES void add_node(NetNode*); void del_node(NetNode*); // BRANCHES void add_branch(NetBranch*); // PROCESSES void add_process(NetProcTop*); void add_process(NetAnalogTop*); void delete_process(NetProcTop*); bool check_proc_delay() const; NetNet* find_discipline_reference(ivl_discipline_t dis, NetScope*scope); // Iterate over the design... void dump(ostream&) const; void functor(struct functor_t*); void join_islands(void); int emit(struct target_t*) const; // This is incremented by elaboration when an error is // detected. It prevents code being emitted. unsigned errors; private: // Keep a tree of scopes. The NetScope class handles the wide // tree and per-hop searches for me. listroot_scopes_; // Keep a map of all the elaborated packages. Note that // packages do not nest. std::mappackages_; // Tasks in the $root scope std::maproot_tasks_; // Need this for elaboration of $root scope pclass objects. std::map class_to_pclass_; // List the nodes in the design. NetNode*nodes_; // These are in support of the node functor iterator. NetNode*nodes_functor_cur_; NetNode*nodes_functor_nxt_; // List the branches in the design. NetBranch*branches_; // List the processes in the design. NetProcTop*procs_; NetProcTop*procs_idx_; // List the ANALOG processes in the design. NetAnalogTop*aprocs_; // Map of discipline take to NetNet for the reference node. mapdiscipline_references_; // Map the design arguments to values. map flags_; int des_precision_; delay_sel_t des_delay_sel_; private: // not implemented Design(const Design&); Design& operator= (const Design&); }; /* ======= */ inline bool operator == (const Link&l, const Link&r) { return l.is_equal(r); } inline bool operator != (const Link&l, const Link&r) { return ! l.is_equal(r); } /* Connect the pins of two nodes together. Either may already be connected to other things, connect is transitive. */ extern void connect(Link&, Link&); /* Return true if l and r are connected. */ inline bool connected(const Link&l, const Link&r) { return l.is_linked(r); } /* Return the number of signals in the nexus. */ extern unsigned count_signals(const Link&pin); /* Find the next link that is an output into the nexus. */ extern Link* find_next_output(Link*lnk); /* Find the signal connected to the given node pin. There should always be exactly one signal. The bidx parameter gets filled with the signal index of the Net, in case it is a vector. */ const NetNet* find_link_signal(const NetObj*net, unsigned pin, unsigned&bidx); inline ostream& operator << (ostream&o, const NetExpr&exp) { exp.dump(o); return o; } extern ostream& operator << (ostream&, NetNet::Type); /* * Manipulator to dump a scope complete path to the output. The * manipulator is "scope_path" and works like this: * * out << .... << scope_path(sc) << ... ; */ struct __ScopePathManip { const NetScope*scope; }; inline __ScopePathManip scope_path(const NetScope*scope) { __ScopePathManip tmp; tmp.scope = scope; return tmp; } extern ostream& operator << (ostream&o, __ScopePathManip); struct __ObjectPathManip { const NetObj*obj; }; inline __ObjectPathManip scope_path(const NetObj*obj) { __ObjectPathManip tmp; tmp.obj = obj; return tmp; } extern ostream& operator << (ostream&o, __ObjectPathManip); /* * If this link has a nexus_ pointer, then it is the last Link in the * list. next_nlink() returns 0 for the last Link. */ inline Link* Link::next_nlink() { if (nexus_) return 0; else return next_; } inline const Link* Link::next_nlink() const { if (nexus_) return 0; else return next_; } inline NetPins*Link::get_obj() { if (pin_zero_) return node_; Link*tmp = this - pin_; assert(tmp->pin_zero_); return tmp->node_; } inline const NetPins*Link::get_obj() const { if (pin_zero_) return node_; const Link*tmp = this - pin_; assert(tmp->pin_zero_); return tmp->node_; } inline unsigned Link::get_pin() const { if (pin_zero_) return 0; else return pin_; } #undef ENUM_UNSIGNED_INT #endif /* IVL_netlist_H */ iverilog-10_1/netlist.txt000066400000000000000000000325261265551621300155750ustar00rootroot00000000000000/* * Copyright (c) 1998-1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ Note that the netlist.h header contains detailed descriptions of how things work. This is just an overview. NETLIST FORMAT The output from the parse and elaboration steps is a "netlist" rooted in a Design object. Parsing translates the design described in the initial source file into a temporary symbolic "pform". Elaboration then expands the design, resolving references and expanding hierarchies, to produce a flattened netlist. This is the form that optimizers and code generators use. The design optimization processes all manipulate the netlist, translating it to a (hopefully) better netlist after each step. The complete netlist is then passed to the code generator, the emit function, where the final code (in the target format) is produced. STRUCTURAL ITEMS: NetNode and NetNet Components and wires, memories and registers all at their base are either NetNode objects or NetNet objects. Even these classes are derived from the NetObj class. All NetNode and NetNet objects have a name and some number of pins. The name usually comes from the Verilog source that represents that object, although objects that are artifacts of elaboration will have a generated (and probably unreadable) name. The pins are the ports into the device. NetNode objects have a pin for each pin of the component it represents, and NetNet objects have a pin for each signal in the vector. Node and net pins can be connected together via the connect function. Connections are transitive (A==B and B==c means A==C) so connections accumulate on a link as items are connected to it. The destructors for nets and nodes automatically arrange for pins to be disconnected when the item is deleted, so that the netlist can be changed during processing. STRUCTURAL LINKS The NetNode and NetNet classes contain arrays of Link objects, one object per pin. Each pin is a single bit. The Link objects link to all the NetNode and NetNet objects' links that are connected together in the design, and to a Nexus object. This way, code that examines a node of the design can discover what is connected to each pin. The connected set of links also has common properties that are stored or access from the Nexus object. All the Links that are connected together are also connected to a single Nexus object. This object is useful for accessing the properties and values that come from the connected set of links. The Nexus object is also handy for iterating over the connected set of Links. See the Link class definition in netlist.h for a description of the link methods, and the Nexus class for nexus global methods. Currently, a link has 3 possible direction properties: PASSIVE -- These pins are sampled by the object that holds the pin based on some external event. These are used, for example, by NetESignal objects that read a point for a procedural expression. INPUT -- These pins potentially react to the setting of its input. OUTPUT -- These pins potentially drive the node. (They may be three-state.) BEHAVIORAL ITEMS: NetProcTop, NetProc and derived classes Behavioral items are not in general linked to the netlist. Instead, they represent elaborated behavioral statements. The type of the object implies what the behavior of the statement does. For example, a NetCondit object represents an ``if'' statement, and carries a condition expression and up to two alternative sub-statements. At the root of a process is a NetProcTop object. This class carries a type flag (initial or always) and a single NetProc object. The contained statement may, depending on the derived class, refer to other statements, compound statements, so on. But at the root of the tree is the NetProcTop object. The Design class keeps a list of the elaborated NetProcTop objects. That list represents the list of processes in the design. INTERACTION OF BEHAVIORAL AND STRUCTURAL: NetAssign_ The behavioral statements in a Verilog design effect the structural aspects through assignments to registers. Registers are structural items represented by the NetNet class, linked to the assignment statement through pins. This implies that the l-value of an assignment is structural. It also implies that the statement itself is structural, and indeed it is derived from NetNode. The NetAssign_ class is also derived from the NetProc class because what it does is brought on by executing the process. By multiple inheritance we have therefore that the assignment is both a NetNode and a NetProc. The NetAssign_ node has pins that represent the l-value of the statement, and carries behavioral expressions that represent the r-value of the assignment. MEMORIES The netlist form includes the NetMemory type to hold the content of a memory. Instances of this type represent the declaration of a memory, and occur once for each memory. References to the memory are managed by the NetEMemory and NetAssignMem_ classes. An instance of the NetEMemory class is created whenever a procedural expression references a memory element. The operand is the index to use to address (and read) the memory. An instance of the NetAssignMem_ class is created when there is a procedural assignment to the memory. The NetAssignMem_ object represents the l-value reference (a write) to the memory. As with the NetEMemory class, this is a procedural reference only. When a memory reference appears in structural context (i.e. continuous assignments) elaboration creates a NetRamDq. This is a LPM_RAM_DQ device. Elaboration leaves the write control and data input pins unconnected for now, because memories cannot appear is l-values of continuous assignments. However, the synthesis functor may connect signals to the write control lines to get a fully operational RAM. By the time elaboration completes, there may be many NetAssignMem_, NetEMemory and NetRamDq objects referencing the same NetMemory object. Each represents a port into the memory. It is up to the synthesis steps (and the target code) to figure out what to do with these ports. EXPRESSIONS Expressions are represented as a tree of NetExpr nodes. The NetExpr base class contains the core methods that represent an expression node, including virtual methods to help with dealing with nested complexities of expressions. Expressions (as expressed in the source and p-form) may also be elaborated structurally, where it makes sense. For example, assignment l-value expressions are represented as connections to pins. Also, continuous assignment module items are elaborated as gates instead of as a procedural expression. Event expressions are also elaborated structurally as events are like devices that trigger behavioral statements. However, typical expressions the behavioral description are represented as a tree of NetExpr nodes. The derived class of the node encodes what kind of operator the node represents. EXPRESSION BIT WIDTH The expression (represented by the NetExpr class) has a bit width that it either explicitly specified, or implied by context or contents. When each node of the expression is first constructed during elaboration, it is given, by type and parameters, an idea what its width should be. It certain cases, this is definitive, for example with signals. In others, it is ambiguous, as with unsized constants. As the expression is built up by elaboration, operators that combine expressions impose bit widths of the environment or expose the bit widths of the sub expressions. For example, the bitwise AND (&) operator has a bit size implied by its operands, whereas the comparison (==) operator has a bit size of 1. The building up of the elaborated expression checks and adjusts the bit widths as the expression is built up, until finally the context of the expression takes the final bit width and makes any final adjustments. The NetExpr::expr_width() method returns the calculated (or guessed) expression width. This method will return 0 until the width is set by calculation or context. If this method returns false, then it is up to the context that wants the width to set one. The elaboration phase will call the NetExpr::set_width method on an expression as soon as it gets to a point where it believes that it knows what the width should be. The NetExpr::set_width(unsigned) virtual method is used by the context of an expression node to note to the expression that the width is determined and please adapt. If the expression cannot reasonably adapt, it will return false. Otherwise, it will adjust bit widths and return true. XXXX I do not yet properly deal with cases where elaboration knows for XXXX certain that the bit width does not matter. In this case, I XXXX really should tell the expression node about it so that it can XXXX pick a practical (and optimal) width. INTERACTION OF EXPRESSIONS AND STRUCTURE: NetESignal The NetAssign_ class described above is the means for processes to manipulate the net, but values are read from the net by NetESignal objects. These objects are class NetExpr because they can appear in expressions (and have width). They are not NetNode object, but hold pointers to a NetNet object, which is used to retrieve values with the expression is evaluated. HIERARCHY IN NETLISTS The obvious hierarchical structure of Verilog is the module. The Verilog program may contain any number of instantiations of modules in order to form an hierarchical design. However, the elaboration of the design into a netlist erases module boundaries. Modules are expanded each place they are used, with the hierarchical instance name used to name the components of the module instance. However, the fact that a wire or register is a module port is lost. The advantage of this behavior is first the simplification of the netlist structure itself. Backends that process netlists only need to cope with a list of nets, a list of nodes and a list of processes. This eases the task of the backend code generators. Another advantage of this flattening of the netlist is that optimizers can operate globally, with optimizations freely crossing module boundaries. This makes coding of netlist transform functions such as constant propagation more effective and easier to write. SCOPE REPRESENTATION IN NETLISTS In spite of the literal flattening of the design, scope information is preserved in the netlist, with the NetScope class. The Design class keeps a single pointer to the root scope of the design. This is the scope of the root module. Scopes that are then created within that (or any nested) module are placed as children of the root scope, and those children can have further children, and so on. Each scope in the tree carries its own name, and its relationship to its parent and children. This makes it possible to walk the tree of scopes. In practice, the walking of the scopes is handled by recursive methods. Each scope also carries the parameters that are applicable to the scope itself. The parameter expression (possibly evaluated) can be located by name, given the scope object itself. The scan of the pform to generate scopes also places the parameters that are declared in the scope. Overrides are managed during the scan, and once the scan is complete, defparam overrides are applied. TASKS IN NETLISTS The flattening of the design does not include tasks and named begin-end blocks. Tasks are behavioral hierarchy (whereas modules are structural) so do not easily succumb to the flattening process. In particular, it is logically impossible to flatten tasks that recurse. (The elaboration process does reserve the right to flatten some task calls. C++ programmers recognize this as inlining a task.) TIME SCALE IN NETLISTS The Design class and the NetScope classes carry time scale and resolution information of the elaborated design. There is a global resolution, and there are scope specific units and resolutions. Units and resolutions are specified as signed integers, and interpreted as the power of 10 of the value. For example, a resolution "-9" means that "1" is 1ns (1e-9). The notation supports units from -128 to +127. It is up to the back-ends to interpret "-4" as "100us". Delays are expressed in the netlist by integers. The units of these delays are always given in the units of the design precision. This allows everything to work with integers, and generally places the burden of scaling delays into elaboration. This is, after all, a common task. The Design::get_precision() method gets the global design precision. Each NetScope also carries its local time_units and time_precision values. These are filled in during scope elaboration and are used in subsequent elaboration phases to arrange for scaling of delays. This information can also be used by the code generator to scale times back to the units of the scope, if that is desired. iverilog-10_1/netmisc.cc000066400000000000000000001417011265551621300153170ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include "netlist.h" # include "netparray.h" # include "netvector.h" # include "netmisc.h" # include "PExpr.h" # include "pform_types.h" # include "compiler.h" # include "ivl_assert.h" NetNet* sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig) { netvector_t*zero_vec = new netvector_t(sig->data_type(), sig->vector_width()-1, 0); NetNet*zero_net = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, zero_vec); zero_net->set_line(*sig); zero_net->local_flag(true); if (sig->data_type() == IVL_VT_REAL) { verireal zero (val); NetLiteral*zero_obj = new NetLiteral(scope, scope->local_symbol(), zero); zero_obj->set_line(*sig); des->add_node(zero_obj); connect(zero_net->pin(0), zero_obj->pin(0)); } else { verinum zero ((int64_t)val); zero = cast_to_width(zero, sig->vector_width()); zero.has_sign(sig->get_signed()); NetConst*zero_obj = new NetConst(scope, scope->local_symbol(), zero); zero_obj->set_line(*sig); des->add_node(zero_obj); connect(zero_net->pin(0), zero_obj->pin(0)); } NetAddSub*adder = new NetAddSub(scope, scope->local_symbol(), sig->vector_width()); adder->set_line(*sig); des->add_node(adder); adder->attribute(perm_string::literal("LPM_Direction"), verinum("SUB")); connect(zero_net->pin(0), adder->pin_DataA()); connect(adder->pin_DataB(), sig->pin(0)); netvector_t*tmp_vec = new netvector_t(sig->data_type(), sig->vector_width()-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*sig); tmp->local_flag(true); connect(adder->pin_Result(), tmp->pin(0)); return tmp; } NetNet* cast_to_int2(Design*des, NetScope*scope, NetNet*src, unsigned wid) { if (src->data_type() == IVL_VT_BOOL) return src; netvector_t*tmp_vec = new netvector_t(IVL_VT_BOOL, wid-1, 0, src->get_signed()); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*src); tmp->local_flag(true); NetCastInt2*cast = new NetCastInt2(scope, scope->local_symbol(), wid); cast->set_line(*src); des->add_node(cast); connect(cast->pin(0), tmp->pin(0)); connect(cast->pin(1), src->pin(0)); return tmp; } NetNet* cast_to_int4(Design*des, NetScope*scope, NetNet*src, unsigned wid) { if (src->data_type() != IVL_VT_REAL) return src; netvector_t*tmp_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*src); tmp->local_flag(true); NetCastInt4*cast = new NetCastInt4(scope, scope->local_symbol(), wid); cast->set_line(*src); des->add_node(cast); connect(cast->pin(0), tmp->pin(0)); connect(cast->pin(1), src->pin(0)); return tmp; } NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) { if (src->data_type() == IVL_VT_REAL) return src; netvector_t*tmp_vec = new netvector_t(IVL_VT_REAL); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*src); tmp->local_flag(true); NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed()); cast->set_line(*src); des->add_node(cast); connect(cast->pin(0), tmp->pin(0)); connect(cast->pin(1), src->pin(0)); return tmp; } NetExpr* cast_to_int2(NetExpr*expr, unsigned width) { // Special case: The expression is already BOOL if (expr->expr_type() == IVL_VT_BOOL) return expr; if (debug_elaborate) cerr << expr->get_fileline() << ": debug: " << "Cast expression to int2, width=" << width << "." << endl; NetECast*cast = new NetECast('2', expr, width, expr->has_sign()); cast->set_line(*expr); return cast; } NetExpr* cast_to_int4(NetExpr*expr, unsigned width) { // Special case: The expression is already LOGIC or BOOL if (expr->expr_type() != IVL_VT_REAL) return expr; if (debug_elaborate) cerr << expr->get_fileline() << ": debug: " << "Cast expression to int4, width=" << width << "." << endl; NetECast*cast = new NetECast('v', expr, width, expr->has_sign()); cast->set_line(*expr); return cast; } NetExpr* cast_to_real(NetExpr*expr) { if (expr->expr_type() == IVL_VT_REAL) return expr; if (debug_elaborate) cerr << expr->get_fileline() << ": debug: " << "Cast expression to real." << endl; NetECast*cast = new NetECast('r', expr, 1, true); cast->set_line(*expr); return cast; } /* * Add a signed constant to an existing expression. Generate a new * NetEBAdd node that has the input expression and an expression made * from the constant value. */ static NetExpr* make_add_expr(NetExpr*expr, long val) { if (val == 0) return expr; // If the value to be added is <0, then instead generate a // SUBTRACT node and turn the value positive. char add_op = '+'; if (val < 0) { add_op = '-'; val = -val; } verinum val_v (val, expr->expr_width()); val_v.has_sign(true); NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); NetEBAdd*res = new NetEBAdd(add_op, expr, val_c, expr->expr_width(), expr->has_sign()); res->set_line(*expr); return res; } static NetExpr* make_add_expr(const LineInfo*loc, NetExpr*expr1, NetExpr*expr2) { bool use_signed = expr1->has_sign() && expr2->has_sign(); unsigned use_wid = expr1->expr_width(); if (expr2->expr_width() > use_wid) use_wid = expr2->expr_width(); expr1 = pad_to_width(expr1, use_wid, *loc); expr2 = pad_to_width(expr2, use_wid, *loc); NetEBAdd*tmp = new NetEBAdd('+', expr1, expr2, use_wid, use_signed); return tmp; } /* * Subtract an existing expression from a signed constant. */ static NetExpr* make_sub_expr(long val, NetExpr*expr) { verinum val_v (val, expr->expr_width()); val_v.has_sign(true); NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); NetEBAdd*res = new NetEBAdd('-', val_c, expr, expr->expr_width(), expr->has_sign()); res->set_line(*expr); return res; } /* * Multiple an existing expression by a signed positive number. * This does a lossless multiply, so the arguments will need to be * sized to match the output size. */ static NetExpr* make_mult_expr(NetExpr*expr, unsigned long val) { const unsigned val_wid = ceil(log2((double)val)) ; unsigned use_wid = expr->expr_width() + val_wid; verinum val_v (val, use_wid); val_v.has_sign(true); NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); // We know by definitions that the expr argument needs to be // padded to be the right argument width for this lossless multiply. expr = pad_to_width(expr, use_wid, *expr); NetEBMult*res = new NetEBMult('*', expr, val_c, use_wid, expr->has_sign()); res->set_line(*expr); return res; } /* * This routine is used to calculate the number of bits needed to * contain the given number. */ static unsigned num_bits(long arg) { unsigned res = 0; /* For a negative value we have room for one extra value, but * we have a signed result so we need an extra bit for this. */ if (arg < 0) { arg = -arg - 1; res += 1; } /* Calculate the number of bits needed here. */ while (arg) { res += 1; arg >>= 1; } return res; } /* * This routine generates the normalization expression needed for a variable * bit select or a variable base expression for an indexed part * select. This function doesn't actually look at the variable * dimensions, it just does the final calculation using msb/lsb of the * last slice, and the off of the slice in the variable. */ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb, unsigned long wid, bool is_up, long soff) { long offset = lsb; if (msb < lsb) { /* Correct the offset if needed. */ if (is_up) offset -= wid - 1; /* Calculate the space needed for the offset. */ unsigned min_wid = num_bits(offset); if (num_bits(soff) > min_wid) min_wid = num_bits(soff); /* We need enough space for the larger of the offset or the * base expression. */ if (min_wid < base->expr_width()) min_wid = base->expr_width(); /* Now that we have the minimum needed width increase it by * one to make room for the normalization calculation. */ min_wid += 2; /* Pad the base expression to the correct width. */ base = pad_to_width(base, min_wid, *base); /* If the base expression is unsigned and either the lsb * is negative or it does not fill the width of the base * expression then we could generate negative normalized * values so cast the expression to signed to get the * math correct. */ if ((lsb < 0 || num_bits(lsb+1) <= base->expr_width()) && ! base->has_sign()) { /* We need this extra select to hide the signed * property from the padding above. It will be * removed automatically during code generation. */ NetESelect *tmp = new NetESelect(base, 0 , min_wid); tmp->set_line(*base); tmp->cast_signed(true); base = tmp; } /* Normalize the expression. */ base = make_sub_expr(offset+soff, base); } else { /* Correct the offset if needed. */ if (!is_up) offset += wid - 1; /* If the offset is zero then just return the base (index) * expression. */ if ((soff-offset) == 0) return base; /* Calculate the space needed for the offset. */ unsigned min_wid = num_bits(-offset); if (num_bits(soff) > min_wid) min_wid = num_bits(soff); /* We need enough space for the larger of the offset or the * base expression. */ if (min_wid < base->expr_width()) min_wid = base->expr_width(); /* Now that we have the minimum needed width increase it by * one to make room for the normalization calculation. */ min_wid += 2; /* Pad the base expression to the correct width. */ base = pad_to_width(base, min_wid, *base); /* If the offset is greater than zero then we need to do * signed math to get the location value correct. */ if (offset > 0 && ! base->has_sign()) { /* We need this extra select to hide the signed * property from the padding above. It will be * removed automatically during code generation. */ NetESelect *tmp = new NetESelect(base, 0 , min_wid); tmp->set_line(*base); tmp->cast_signed(true); base = tmp; } /* Normalize the expression. */ base = make_add_expr(base, soff-offset); } return base; } /* * This method is how indices should work except that the base should * be a vector of expressions that matches the size of the dims list, * so that we can generate an expression based on the entire packed * vector. For now, we assert that there is only one set of dimensions. */ NetExpr *normalize_variable_base(NetExpr *base, const list&dims, unsigned long wid, bool is_up) { ivl_assert(*base, dims.size() == 1); const netrange_t&rng = dims.back(); return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), wid, is_up); } NetExpr *normalize_variable_bit_base(const list&indices, NetExpr*base, const NetNet*reg) { const vector&packed_dims = reg->packed_dims(); ivl_assert(*base, indices.size()+1 == packed_dims.size()); // Get the canonical offset of the slice within which we are // addressing. We need that address as a slice offset to // calculate the proper complete address const netrange_t&rng = packed_dims.back(); long slice_off = reg->sb_to_idx(indices, rng.get_lsb()); return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), 1, true, slice_off); } NetExpr *normalize_variable_part_base(const list&indices, NetExpr*base, const NetNet*reg, unsigned long wid, bool is_up) { const vector&packed_dims = reg->packed_dims(); ivl_assert(*base, indices.size()+1 == packed_dims.size()); // Get the canonical offset of the slice within which we are // addressing. We need that address as a slice offset to // calculate the proper complete address const netrange_t&rng = packed_dims.back(); long slice_off = reg->sb_to_idx(indices, rng.get_lsb()); return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), wid, is_up, slice_off); } NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, const NetNet*reg, unsigned long&lwid) { const vector&packed_dims = reg->packed_dims(); ivl_assert(*base, indices.size() < packed_dims.size()); vector::const_iterator pcur = packed_dims.end(); for (size_t idx = indices.size() ; idx < packed_dims.size(); idx += 1) { -- pcur; } long sb; if (pcur->get_msb() >= pcur->get_lsb()) sb = pcur->get_lsb(); else sb = pcur->get_msb(); long loff; reg->sb_to_slice(indices, sb, loff, lwid); base = make_mult_expr(base, lwid); base = make_add_expr(base, loff); return base; } ostream& operator << (ostream&o, __IndicesManip val) { for (list::const_iterator cur = val.val.begin() ; cur != val.val.end() ; ++cur) { o << "[" << *cur << "]"; } return o; } ostream& operator << (ostream&o, __IndicesManip val) { for (list::const_iterator cur = val.val.begin() ; cur != val.val.end() ; ++cur) { o << "[" << *(*cur) << "]"; } return o; } /* * The src is the input index expression list from the expression, and * the count is the number that are to be elaborated into the indices * list. At the same time, create a indices_const list that contains * the evaluated values for the expression, if they can be evaluated. */ void indices_to_expressions(Design*des, NetScope*scope, // loc is for error messages. const LineInfo*loc, // src is the index list, and count is // the number of items in the list to use. const list&src, unsigned count, // True if the expression MUST be constant. bool need_const, // These are the outputs. indices_flags&flags, list&indices, list&indices_const) { ivl_assert(*loc, count <= src.size()); flags.invalid = false; flags.variable = false; flags.undefined = false; for (list::const_iterator cur = src.begin() ; count > 0 ; ++cur, --count) { ivl_assert(*loc, cur->sel != index_component_t::SEL_NONE); if (cur->sel != index_component_t::SEL_BIT) { cerr << loc->get_fileline() << ": error: " << "Array cannot be indexed by a range." << endl; des->errors += 1; } ivl_assert(*loc, cur->msb); NetExpr*word_index = elab_and_eval_lossless(des, scope, cur->msb, -2, need_const); if (word_index == 0) flags.invalid = true; // Track if we detect any non-constant expressions // here. This may allow for a special case. NetEConst*word_const = dynamic_cast (word_index); if (word_const == 0) flags.variable = true; else if (!word_const->value().is_defined()) flags.undefined = true; else if (!flags.variable && !flags.undefined) indices_const.push_back(word_const->value().as_long()); indices.push_back(word_index); } } static void make_strides(const vector&dims, vector&stride) { stride[dims.size()-1] = 1; for (size_t idx = stride.size()-1 ; idx > 0 ; --idx) { long tmp = dims[idx].width(); if (idx < stride.size()) tmp *= stride[idx]; stride[idx-1] = tmp; } } /* * Take in a vector of constant indices and convert them to a single * number that is the canonical address (zero based, 1-d) of the * word. If any of the indices are out of bounds, return nil instead * of an expression. */ static NetExpr* normalize_variable_unpacked(const vector&dims, list&indices) { // Make strides for each index. The stride is the distance (in // words) to the next element in the canonical array. vector stride (dims.size()); make_strides(dims, stride); int64_t canonical_addr = 0; int idx = 0; for (list::const_iterator cur = indices.begin() ; cur != indices.end() ; ++cur, ++idx) { long tmp = *cur; if (dims[idx].get_lsb() <= dims[idx].get_msb()) tmp -= dims[idx].get_lsb(); else tmp -= dims[idx].get_msb(); // Notice of this index is out of range. if (tmp < 0 || tmp >= (long)dims[idx].width()) { return 0; } canonical_addr += tmp * stride[idx]; } NetEConst*canonical_expr = new NetEConst(verinum(canonical_addr)); return canonical_expr; } NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) { const vector&dims = net->unpacked_dims(); return normalize_variable_unpacked(dims, indices); } NetExpr* normalize_variable_unpacked(const netsarray_t*stype, list&indices) { const vector&dims = stype->static_dimensions(); return normalize_variable_unpacked(dims, indices); } NetExpr* normalize_variable_unpacked(const LineInfo&loc, const vector&dims, list&indices) { // Make strides for each index. The stride is the distance (in // words) to the next element in the canonical array. vector stride (dims.size()); make_strides(dims, stride); NetExpr*canonical_expr = 0; int idx = 0; for (list::const_iterator cur = indices.begin() ; cur != indices.end() ; ++cur, ++idx) { NetExpr*tmp = *cur; // If the expression elaboration generated errors, then // give up. Presumably, the error during expression // elaboration already generated the error message. if (tmp == 0) return 0; int64_t use_base; if (! dims[idx].defined()) use_base = 0; else if (dims[idx].get_lsb() <= dims[idx].get_msb()) use_base = dims[idx].get_lsb(); else use_base = dims[idx].get_msb(); int64_t use_stride = stride[idx]; // Account for that we are doing arithmetic and should // have a proper width to make sure there are no // losses. So calculate a min_wid width. unsigned tmp_wid; unsigned min_wid = tmp->expr_width(); if (use_base != 0 && ((tmp_wid = num_bits(use_base)) >= min_wid)) min_wid = tmp_wid + 1; if ((tmp_wid = num_bits(dims[idx].width()+1)) >= min_wid) min_wid = tmp_wid + 1; if (use_stride != 1) min_wid += num_bits(use_stride); tmp = pad_to_width(tmp, min_wid, loc); // Now generate the math to calculate the canonical address. NetExpr*tmp_scaled = 0; if (NetEConst*tmp_const = dynamic_cast (tmp)) { // Special case: the index is constant, so this // iteration can be replaced with a constant // expression. int64_t val = tmp_const->value().as_long(); val -= use_base; val *= use_stride; // Very special case: the index is zero, so we can // skip this iteration if (val == 0) continue; tmp_scaled = new NetEConst(verinum(val)); } else { tmp_scaled = tmp; if (use_base != 0) tmp_scaled = make_add_expr(tmp_scaled, -use_base); if (use_stride != 1) tmp_scaled = make_mult_expr(tmp_scaled, use_stride); } if (canonical_expr == 0) { canonical_expr = tmp_scaled; } else { bool expr_has_sign = canonical_expr->has_sign() && tmp_scaled->has_sign(); canonical_expr = new NetEBAdd('+', canonical_expr, tmp_scaled, canonical_expr->expr_width()+1, expr_has_sign); } } // If we don't have an expression at this point, all the indices were // constant zero. But this variant of normalize_variable_unpacked() // is only used when at least one index is not a constant. ivl_assert(loc, canonical_expr); return canonical_expr; } NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) { const vector&dims = net->unpacked_dims(); return normalize_variable_unpacked(*net, dims, indices); } NetExpr* normalize_variable_unpacked(const LineInfo&loc, const netsarray_t*stype, list&indices) { const vector&dims = stype->static_dimensions(); return normalize_variable_unpacked(loc, dims, indices); } NetExpr* make_canonical_index(Design*des, NetScope*scope, const LineInfo*loc, const std::list&src, const netsarray_t*stype, bool need_const) { NetExpr*canon_index = 0; list indices_const; list indices_expr; indices_flags flags; indices_to_expressions(des, scope, loc, src, src.size(), need_const, flags, indices_expr, indices_const); if (flags.undefined) { cerr << loc->get_fileline() << ": warning: " << "ignoring undefined value array access." << endl; } else if (flags.variable) { canon_index = normalize_variable_unpacked(*loc, stype, indices_expr); } else { canon_index = normalize_variable_unpacked(stype, indices_const); } return canon_index; } NetEConst* make_const_x(unsigned long wid) { verinum xxx (verinum::Vx, wid); NetEConst*resx = new NetEConst(xxx); return resx; } NetEConst* make_const_0(unsigned long wid) { verinum xxx (verinum::V0, wid); NetEConst*resx = new NetEConst(xxx); return resx; } NetEConst* make_const_val(unsigned long value) { verinum tmp (value, integer_width); NetEConst*res = new NetEConst(tmp); return res; } NetEConst* make_const_val_s(long value) { verinum tmp (value, integer_width); tmp.has_sign(true); NetEConst*res = new NetEConst(tmp); return res; } NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid) { verinum xxx (verinum::Vx, wid); NetConst*res = new NetConst(scope, scope->local_symbol(), xxx); des->add_node(res); netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0); NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec); sig->local_flag(true); connect(sig->pin(0), res->pin(0)); return sig; } NetNet* make_const_z(Design*des, NetScope*scope, unsigned long wid) { verinum xxx (verinum::Vz, wid); NetConst*res = new NetConst(scope, scope->local_symbol(), xxx); des->add_node(res); netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0); NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec); sig->local_flag(true); connect(sig->pin(0), res->pin(0)); return sig; } NetExpr* condition_reduce(NetExpr*expr) { if (expr->expr_type() == IVL_VT_REAL) { if (NetECReal *tmp = dynamic_cast(expr)) { verinum::V res; if (tmp->value().as_double() == 0.0) res = verinum::V0; else res = verinum::V1; verinum vres (res, 1, true); NetExpr *rtn = new NetEConst(vres); rtn->set_line(*expr); delete expr; return rtn; } NetExpr *rtn = new NetEBComp('n', expr, new NetECReal(verireal(0.0))); rtn->set_line(*expr); return rtn; } if (expr->expr_width() == 1) return expr; verinum zero (verinum::V0, expr->expr_width()); zero.has_sign(expr->has_sign()); NetEConst*ezero = new NetEConst(zero); ezero->set_line(*expr); NetEBComp*cmp = new NetEBComp('n', expr, ezero); cmp->set_line(*expr); cmp->cast_signed(false); return cmp; } static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const, bool annotatable, bool force_expand, ivl_variable_type_t cast_type) { PExpr::width_mode_t mode = PExpr::SIZED; if ((context_width == -2) && !gn_strict_expr_width_flag) mode = PExpr::EXPAND; if (force_expand) mode = PExpr::EXPAND; pe->test_width(des, scope, mode); // Get the final expression width. If the expression is unsized, // this may be different from the value returned by test_width(). unsigned expr_width = pe->expr_width(); // If context_width is positive, this is the RHS of an assignment, // so the LHS width must also be included in the width calculation. unsigned pos_context_width = context_width > 0 ? context_width : 0; if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width)) expr_width = pos_context_width; if (debug_elaborate) { cerr << pe->get_fileline() << ": elab_and_eval: test_width of " << *pe << endl; cerr << pe->get_fileline() << ": : " << "returns type=" << pe->expr_type() << ", context_width=" << context_width << ", signed=" << pe->has_sign() << ", force_expand=" << force_expand << ", expr_width=" << expr_width << ", mode=" << PExpr::width_mode_name(mode) << endl; cerr << pe->get_fileline() << ": : " << "cast_type=" << cast_type << endl; } // If we can get the same result using a smaller expression // width, do so. unsigned min_width = pe->min_width(); if ((min_width != UINT_MAX) && (pe->expr_type() != IVL_VT_REAL) && (pos_context_width > 0) && (expr_width > pos_context_width)) { expr_width = max(min_width, pos_context_width); if (debug_elaborate) { cerr << pe->get_fileline() << ": : " << "pruned to width=" << expr_width << endl; } } if ((mode >= PExpr::LOSSLESS) && (expr_width > width_cap) && (expr_width > pos_context_width)) { cerr << pe->get_fileline() << ": warning: excessive unsized " << "expression width detected." << endl; cerr << pe->get_fileline() << ": : The expression width " << "is capped at " << width_cap << " bits." << endl; expr_width = width_cap; } unsigned flags = PExpr::NO_FLAGS; if (need_const) flags |= PExpr::NEED_CONST; if (annotatable) flags |= PExpr::ANNOTATABLE; if (debug_elaborate) { cerr << pe->get_fileline() << ": elab_and_eval: " << "Calculated width is " << expr_width << "." << endl; } NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, flags); if (tmp == 0) return 0; if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) { switch (cast_type) { case IVL_VT_REAL: tmp = cast_to_real(tmp); break; case IVL_VT_BOOL: tmp = cast_to_int2(tmp, pos_context_width); break; case IVL_VT_LOGIC: tmp = cast_to_int4(tmp, pos_context_width); break; default: break; } } // If the context_width sent is is actually the minimum width, // then raise the context_width to be big enough for the // lossless expression. if (force_expand && context_width > 0) { context_width = max(context_width, (int)expr_width); } eval_expr(tmp, context_width); if (NetEConst*ce = dynamic_cast(tmp)) { if ((mode >= PExpr::LOSSLESS) && (context_width < 0)) ce->trim(); } return tmp; } NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const, bool annotatable, ivl_variable_type_t cast_type) { return do_elab_and_eval(des, scope, pe, context_width, need_const, annotatable, false, cast_type); } /* * This variant of elab_and_eval does the expression losslessly, no * matter what the generation of verilog. This is in support of * certain special contexts, notably index expressions. */ NetExpr* elab_and_eval_lossless(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const, bool annotatable, ivl_variable_type_t cast_type) { return do_elab_and_eval(des, scope, pe, context_width, need_const, annotatable, true, cast_type); } NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, ivl_type_t lv_net_type, bool need_const) { if (debug_elaborate) { cerr << pe->get_fileline() << ": elab_and_eval: " << "pe=" << *pe << ", lv_net_type=" << *lv_net_type << endl; } // Elaborate the expression using the more general // elaborate_expr method. unsigned flags = PExpr::NO_FLAGS; if (need_const) flags |= PExpr::NEED_CONST; NetExpr*tmp = pe->elaborate_expr(des, scope, lv_net_type, flags); return tmp; } NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, unsigned arg_idx, PExpr*pe, bool need_const) { PExpr::width_mode_t mode = PExpr::SIZED; pe->test_width(des, scope, mode); if (debug_elaborate) { cerr << pe->get_fileline() << ": debug: test_width of " << name << " argument " << (arg_idx+1) << " " << *pe << endl; cerr << pe->get_fileline() << ": " << "returns type=" << pe->expr_type() << ", width=" << pe->expr_width() << ", signed=" << pe->has_sign() << ", mode=" << PExpr::width_mode_name(mode) << endl; } unsigned flags = PExpr::SYS_TASK_ARG; if (need_const) flags |= PExpr::NEED_CONST; NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), flags); if (tmp == 0) return 0; eval_expr(tmp, -1); if (NetEConst*ce = dynamic_cast(tmp)) { // For lossless/unsized constant expressions, we can now // determine the exact width required to hold the result. // But leave literal numbers exactly as the user supplied // them. if ((mode >= PExpr::LOSSLESS) && !dynamic_cast(pe) && tmp->expr_width()>32) ce->trim(); } return tmp; } bool evaluate_ranges(Design*des, NetScope*scope, vector&llist, const list&rlist) { bool bad_msb = false, bad_lsb = false; for (list::const_iterator cur = rlist.begin() ; cur != rlist.end() ; ++cur) { long use_msb, use_lsb; NetExpr*texpr = elab_and_eval(des, scope, cur->first, -1, true); if (! eval_as_long(use_msb, texpr)) { cerr << cur->first->get_fileline() << ": error: " "Range expressions must be constant." << endl; cerr << cur->first->get_fileline() << " : " "This MSB expression violates the rule: " << *cur->first << endl; des->errors += 1; bad_msb = true; } delete texpr; texpr = elab_and_eval(des, scope, cur->second, -1, true); if (! eval_as_long(use_lsb, texpr)) { cerr << cur->second->get_fileline() << ": error: " "Range expressions must be constant." << endl; cerr << cur->second->get_fileline() << " : " "This LSB expression violates the rule: " << *cur->second << endl; des->errors += 1; bad_lsb = true; } delete texpr; /* Error recovery */ if (bad_lsb) use_lsb = 0; if (bad_msb) use_msb = use_lsb; llist.push_back(netrange_t(use_msb, use_lsb)); } return bad_msb | bad_lsb; } void eval_expr(NetExpr*&expr, int context_width) { assert(expr); if (dynamic_cast(expr)) return; NetExpr*tmp = expr->eval_tree(); if (tmp != 0) { tmp->set_line(*expr); delete expr; expr = tmp; } if (context_width <= 0) return; NetEConst *ce = dynamic_cast(expr); if (ce == 0) return; // The expression is a constant, so resize it if needed. if (ce->expr_width() < (unsigned)context_width) { expr = pad_to_width(expr, context_width, *expr); } else if (ce->expr_width() > (unsigned)context_width) { verinum value(ce->value(), context_width); ce = new NetEConst(value); ce->set_line(*expr); delete expr; expr = ce; } } bool eval_as_long(long&value, const NetExpr*expr) { if (const NetEConst*tmp = dynamic_cast(expr) ) { value = tmp->value().as_long(); return true; } if (const NetECReal*rtmp = dynamic_cast(expr)) { value = rtmp->value().as_long(); return true; } return false; } bool eval_as_double(double&value, NetExpr*expr) { if (NetEConst*tmp = dynamic_cast(expr) ) { value = tmp->value().as_double(); return true; } if (NetECReal*rtmp = dynamic_cast(expr)) { value = rtmp->value().as_double(); return true; } return false; } /* * At the parser level, a name component is a name with a collection * of expressions. For example foo[N] is the name "foo" and the index * expression "N". This function takes as input the name component and * returns the path component name. It will evaluate the index * expression if it is present. */ hname_t eval_path_component(Design*des, NetScope*scope, const name_component_t&comp, bool&error_flag) { // No index expression, so the path component is an undecorated // name, for example "foo". if (comp.index.empty()) return hname_t(comp.name); vector index_values; for (list::const_iterator cur = comp.index.begin() ; cur != comp.index.end() ; ++cur) { const index_component_t&index = *cur; if (index.sel != index_component_t::SEL_BIT) { cerr << index.msb->get_fileline() << ": error: " << "Part select is not valid for this kind of object." << endl; des->errors += 1; return hname_t(comp.name, 0); } // The parser will assure that path components will have only // bit select index expressions. For example, "foo[n]" is OK, // but "foo[n:m]" is not. assert(index.sel == index_component_t::SEL_BIT); // Evaluate the bit select to get a number. NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1); ivl_assert(*index.msb, tmp); if (NetEConst*ctmp = dynamic_cast(tmp)) { index_values.push_back(ctmp->value().as_long()); delete ctmp; continue; } #if 1 // Darn, the expression doesn't evaluate to a constant. That's // an error to be reported. And make up a fake index value to // return to the caller. cerr << index.msb->get_fileline() << ": error: " << "Scope index expression is not constant: " << *index.msb << endl; des->errors += 1; #endif error_flag = true; delete tmp; } return hname_t(comp.name, index_values); } std::list eval_scope_path(Design*des, NetScope*scope, const pform_name_t&path) { bool path_error_flag = false; list res; typedef pform_name_t::const_iterator pform_path_it; for (pform_path_it cur = path.begin() ; cur != path.end(); ++ cur ) { const name_component_t&comp = *cur; res.push_back( eval_path_component(des,scope,comp,path_error_flag) ); } #if 0 if (path_error_flag) { cerr << "XXXXX: Errors evaluating path " << path << endl; } #endif return res; } /* * Human readable version of op. Used in elaboration error messages. */ const char *human_readable_op(const char op, bool unary) { const char *type; switch (op) { case '~': type = "~"; break; // Negation case '+': type = "+"; break; case '-': type = "-"; break; case '*': type = "*"; break; case '/': type = "/"; break; case '%': type = "%"; break; case '<': type = "<"; break; case '>': type = ">"; break; case 'L': type = "<="; break; case 'G': type = ">="; break; case '^': type = "^"; break; // XOR case 'X': type = "~^"; break; // XNOR case '&': type = "&"; break; // Bitwise AND case 'A': type = "~&"; break; // NAND (~&) case '|': type = "|"; break; // Bitwise OR case 'O': type = "~|"; break; // NOR case '!': type = "!"; break; // Logical NOT case 'a': type = "&&"; break; // Logical AND case 'o': type = "||"; break; // Logical OR case 'e': type = "=="; break; case 'n': type = "!="; break; case 'E': type = "==="; break; // Case equality case 'N': if (unary) type = "~|"; // NOR else type = "!=="; // Case inequality break; case 'l': type = "<<(<)"; break; // Left shifts case 'r': type = ">>"; break; // Logical right shift case 'R': type = ">>>"; break; // Arithmetic right shift case 'p': type = "**"; break; // Power case 'i': case 'I': type = "++"; break; /* increment */ case 'd': case 'D': type = "--"; break; /* decrement */ default: type = "???"; assert(0); } return type; } const_bool const_logical(const NetExpr*expr) { switch (expr->expr_type()) { case IVL_VT_REAL: { const NetECReal*val = dynamic_cast (expr); if (val == 0) return C_NON; if (val->value().as_double() == 0.0) return C_0; else return C_1; } case IVL_VT_BOOL: case IVL_VT_LOGIC: { const NetEConst*val = dynamic_cast (expr); if (val == 0) return C_NON; verinum cval = val->value(); const_bool res = C_0; for (unsigned idx = 0; idx < cval.len(); idx += 1) { switch (cval.get(idx)) { case verinum::V1: return C_1; break; case verinum::V0: break; default: if (res == C_0) res = C_X; break; } } return res; } default: break; } return C_NON; } uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val) { verireal fn = val->value(); int shift = scope->time_unit() - scope->time_precision(); assert(shift >= 0); int64_t delay = fn.as_long64(shift); shift = scope->time_precision() - des->get_precision(); assert(shift >= 0); for (int lp = 0; lp < shift; lp += 1) delay *= 10; return delay; } /* * This function looks at the NetNet signal to see if there are any * NetPartSelect::PV nodes driving this signal. If so, See if they can * be collapsed into a single concatenation. */ void collapse_partselect_pv_to_concat(Design*des, NetNet*sig) { NetScope*scope = sig->scope(); vector ps_map (sig->vector_width()); Nexus*nex = sig->pin(0).nexus(); for (Link*cur = nex->first_nlink(); cur ; cur = cur->next_nlink()) { NetPins*obj; unsigned obj_pin; cur->cur_link(obj, obj_pin); // Look for NetPartSelect devices, where this signal is // connected to pin 1 of a NetPartSelect::PV. NetPartSelect*ps_obj = dynamic_cast (obj); if (ps_obj == 0) continue; if (ps_obj->dir() != NetPartSelect::PV) continue; if (obj_pin != 1) continue; // Don't support overrun selects here. if (ps_obj->base()+ps_obj->width() > ps_map.size()) continue; ivl_assert(*ps_obj, ps_obj->base() < ps_map.size()); ps_map[ps_obj->base()] = ps_obj; } // Check the collected NetPartSelect::PV objects to see if // they cover the vector. unsigned idx = 0; unsigned device_count = 0; while (idx < ps_map.size()) { NetPartSelect*ps_obj = ps_map[idx]; if (ps_obj == 0) return; idx += ps_obj->width(); device_count += 1; } ivl_assert(*sig, idx == ps_map.size()); /* The vlog95 and possibly other code generators do not want * to have a group of part selects turned into a transparent * concatenation. */ if (disable_concatz_generation) { // HERE: If the part selects have matching strengths then we can use // a normal concat with a buf-Z after if the strengths are not // both strong. We would ideally delete any buf-Z driving the // concat, but that is not required for the vlog95 generator. return; } // Ah HAH! The NetPartSelect::PV objects exactly cover the // target signal. We can replace all of them with a single // concatenation. if (debug_elaborate) { cerr << sig->get_fileline() << ": debug: " << "Collapse " << device_count << " NetPartSelect::PV devices into a concatenation." << endl; } NetConcat*cat = new NetConcat(scope, scope->local_symbol(), ps_map.size(), device_count, true); des->add_node(cat); cat->set_line(*sig); connect(cat->pin(0), sig->pin(0)); idx = 0; unsigned concat_position = 1; while (idx < ps_map.size()) { assert(ps_map[idx]); NetPartSelect*ps_obj = ps_map[idx]; connect(cat->pin(concat_position), ps_obj->pin(0)); concat_position += 1; idx += ps_obj->width(); delete ps_obj; } } /* * Evaluate the prefix indices. All but the final index in a * chain of indices must be a single value and must evaluate * to constants at compile time. For example: * [x] - OK * [1][2][x] - OK * [1][x:y] - OK * [2:0][x] - BAD * [y][x] - BAD * Leave the last index for special handling. */ bool evaluate_index_prefix(Design*des, NetScope*scope, list&prefix_indices, const list&indices) { list::const_iterator icur = indices.begin(); for (size_t idx = 0 ; (idx+1) < indices.size() ; idx += 1, ++icur) { assert(icur != indices.end()); assert(icur->sel == index_component_t::SEL_BIT); NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, true); long tmp; if (texpr == 0 || !eval_as_long(tmp, texpr)) { cerr << icur->msb->get_fileline() << ": error: " "Array index expressions must be constant here." << endl; des->errors += 1; return false; } prefix_indices .push_back(tmp); delete texpr; } return true; } /* * Evaluate the indices. The chain of indices are applied to the * packed indices of a NetNet to generate a canonical expression to * replace the exprs. */ NetExpr*collapse_array_exprs(Design*des, NetScope*scope, const LineInfo*loc, NetNet*net, const list&indices) { // First elaborate all the expressions as far as possible. list exprs; list exprs_const; indices_flags flags; indices_to_expressions(des, scope, loc, indices, net->packed_dimensions(), false, flags, exprs, exprs_const); ivl_assert(*loc, exprs.size() == net->packed_dimensions()); // Special Case: there is only 1 packed dimension, so the // single expression should already be naturally canonical. if (net->slice_width(1) == 1) { return *exprs.begin(); } const std::vector&pdims = net->packed_dims(); std::vector::const_iterator pcur = pdims.begin(); list::iterator ecur = exprs.begin(); NetExpr* base = 0; for (size_t idx = 0 ; idx < net->packed_dimensions() ; idx += 1, ++pcur, ++ecur) { unsigned cur_slice_width = net->slice_width(idx+1); long lsb = pcur->get_lsb(); long msb = pcur->get_msb(); // This normalizes the expression of this index based on // the msb/lsb values. NetExpr*tmp = normalize_variable_base(*ecur, msb, lsb, cur_slice_width, msb > lsb); // If this slice has width, then scale it. if (net->slice_width(idx+1) != 1) { unsigned min_wid = tmp->expr_width(); if (num_bits(cur_slice_width) >= min_wid) { min_wid = num_bits(cur_slice_width)+1; tmp = pad_to_width(tmp, min_wid, *loc); } tmp = make_mult_expr(tmp, cur_slice_width); } // Now add it to the position we've accumulated so far. if (base) { base = make_add_expr(loc, base, tmp); } else { base = tmp; } } return base; } /* * Given a list of indices, treat them as packed indices and convert * them to an expression that normalizes the list to a single index * expression over a canonical equivalent 1-dimensional array. */ NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net, const list&indices) { listprefix_indices; bool rc = evaluate_index_prefix(des, scope, prefix_indices, indices); assert(rc); const index_component_t&back_index = indices.back(); assert(back_index.sel == index_component_t::SEL_BIT); assert(back_index.msb && !back_index.lsb); NetExpr*base = elab_and_eval(des, scope, back_index.msb, -1, true); NetExpr*res = normalize_variable_bit_base(prefix_indices, base, net); eval_expr(res, -1); return res; } void assign_unpacked_with_bufz(Design*des, NetScope*scope, const LineInfo*loc, NetNet*lval, NetNet*rval) { ivl_assert(*loc, lval->pin_count()==rval->pin_count()); for (unsigned idx = 0 ; idx < lval->pin_count() ; idx += 1) { NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(), lval->vector_width(), false); driver->set_line(*loc); des->add_node(driver); connect(lval->pin(idx), driver->pin(0)); connect(driver->pin(1), rval->pin(idx)); } } /* * synthesis sometimes needs to unpack assignment to a part * select. That looks like this: * * foo[N] <= ; * * The NetAssignBase::synth_async() method will turn that into a * netlist like this: * * NetAssignBase(PV) --> base()== * (0) (1) * | | * v v * foo * * This search will return a pointer to the NetAssignBase(PV) object, * but only if it matches this pattern. */ NetPartSelect* detect_partselect_lval(Link&pin) { NetPartSelect*found_ps = 0; Nexus*nex = pin.nexus(); for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { NetPins*obj; unsigned obj_pin; cur->cur_link(obj, obj_pin); // Skip NexusSet objects. if (obj == 0) continue; // NetNet pins have no effect on this search. if (dynamic_cast (obj)) continue; if (NetPartSelect*ps = dynamic_cast (obj)) { // If this is the input side of a NetPartSelect, skip. if (ps->pin(obj_pin).get_dir()==Link::INPUT) continue; // Oops, driven by the wrong size of a // NetPartSelect, so this is not going to work out. if (ps->dir()==NetPartSelect::VP) return 0; // So now we know this is a NetPartSelect::PV. It // is a candidate for our part-select assign. If // we already have a candidate, then give up. if (found_ps) return 0; // This is our candidate. Carry on. found_ps = ps; continue; } // If this is a driver to the Nexus that is not a // NetPartSelect device. This cannot happen to // part selected lval nets, so quit now. if (obj->pin(obj_pin).get_dir() == Link::OUTPUT) return 0; } return found_ps; } const netclass_t* find_class_containing_scope(const LineInfo&loc, const NetScope*scope) { while (scope && scope->type() != NetScope::CLASS) scope = scope->parent(); if (scope == 0) return 0; const netclass_t*found_in = scope->class_def(); ivl_assert(loc, found_in); return found_in; } /* * Find the scope that contains this scope, that is the method for a * class scope. Look for the scope whose PARENT is the scope for a * class. This is going to be a method. */ NetScope* find_method_containing_scope(const LineInfo&, NetScope*scope) { NetScope*up = scope->parent(); while (up && up->type() != NetScope::CLASS) { scope = up; up = up->parent(); } if (up == 0) return 0; // Should I check if this scope is a TASK or FUNC? return scope; } iverilog-10_1/netmisc.h000066400000000000000000000351361265551621300151650ustar00rootroot00000000000000#ifndef IVL_netmisc_H #define IVL_netmisc_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netlist.h" class netsarray_t; /* * Search for a symbol using the "start" scope as the starting * point. If the path includes a scope part, then locate the * scope first. * * The return value is the scope where the symbol was found. * If the symbol was not found, return 0. The output arguments * get 0 except for the pointer to the object that represents * the located symbol. * * The ex1 and ex2 output arguments are extended results. If the * symbol is a parameter (par!=0) then ex1 is the msb expression and * ex2 is the lsb expression for the range. If there is no range, then * these values are set to 0. */ extern NetScope* symbol_search(const LineInfo*li, Design*des, NetScope*start, pform_name_t path, NetNet*&net, /* net/reg */ const NetExpr*&par,/* parameter/expr */ NetEvent*&eve, /* named event */ const NetExpr*&ex1, const NetExpr*&ex2); inline NetScope* symbol_search(const LineInfo*li, Design*des, NetScope*start, const pform_name_t&path, NetNet*&net, /* net/reg */ const NetExpr*&par,/* parameter/expr */ NetEvent*&eve /* named event */) { const NetExpr*ex1, *ex2; return symbol_search(li, des, start, path, net, par, eve, ex1, ex2); } /* * This function transforms an expression by padding the high bits * with V0 until the expression has the desired width. This may mean * not transforming the expression at all, if it is already wide * enough. */ extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info); extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w, const LineInfo&info); extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w, const LineInfo&info); /* * Generate the nodes necessary to cast an expression (a net) to a * real value. */ extern NetNet*cast_to_int4(Design*des, NetScope*scope, NetNet*src, unsigned wid); extern NetNet*cast_to_int2(Design*des, NetScope*scope, NetNet*src, unsigned wid); extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src); extern NetExpr*cast_to_int4(NetExpr*expr, unsigned width); extern NetExpr*cast_to_int2(NetExpr*expr, unsigned width); extern NetExpr*cast_to_real(NetExpr*expr); /* * Take the input expression and return a variation that assures that * the expression is 1-bit wide and logical. This reflects the needs * of conditions i.e. for "if" statements or logical operators. */ extern NetExpr*condition_reduce(NetExpr*expr); /* * This function transforms an expression by cropping the high bits * off with a part select. The result has the width w passed in. This * function does not pad, use pad_to_width if padding is desired. */ extern NetNet*crop_to_width(Design*des, NetNet*n, unsigned w); extern bool calculate_part(const LineInfo*li, Design*des, NetScope*scope, const index_component_t&index, long&off, unsigned long&wid); /* * These functions generate an equation to normalize an expression using * the provided vector/array information. */ extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb, unsigned long wid, bool is_up, long slice_off =0); extern NetExpr*normalize_variable_base(NetExpr *base, const list&dims, unsigned long wid, bool is_up); /* * Calculate a canonicalizing expression for a bit select, when the * base expression is the last index of an otherwise complete bit * select. For example: * reg [3:0][7:0] foo; * ... foo[1][x] ... * base is (x) and the generated expression will be (x+8). */ extern NetExpr*normalize_variable_bit_base(const list&indices, NetExpr *base, const NetNet*reg); /* * This is similar to normalize_variable_bit_base, but the tail index * it a base and width, instead of a bit. This is used for handling * indexed part selects: * reg [3:0][7:0] foo; * ... foo[1][x +: 2] * base is (x), wid input is (2), and is_up is (true). The output * expression is (x+8). */ extern NetExpr *normalize_variable_part_base(const list&indices, NetExpr*base, const NetNet*reg, unsigned long wid, bool is_up); /* * Calculate a canonicalizing expression for a slice select. The * indices array is less than needed to fully address a bit, so the * result is a slice of the packed array. The return value is an * expression that gets to the base of the slice, and (lwid) becomes * the width of the slice, in bits. For example: * reg [4:1][7:0] foo * ...foo[x]... * base is (x) and the generated expression will be (x*8 - 8), with * lwid set to (8). */ extern NetExpr*normalize_variable_slice_base(const list&indices, NetExpr *base, const NetNet*reg, unsigned long&lwid); /* * The as_indices() manipulator is a convenient way to emit a list of * index values in the form [<>][<>].... */ template struct __IndicesManip { inline __IndicesManip(const std::list&v) : val(v) { } const std::list&val; }; template inline __IndicesManip as_indices(const std::list&indices) { return __IndicesManip(indices); } extern ostream& operator << (ostream&o, __IndicesManip); extern ostream& operator << (ostream&o, __IndicesManip); /* * Given a list of index expressions, generate elaborated expressions * and constant values, if possible. */ struct indices_flags { bool invalid; // at least one index failed elaboration bool variable; // at least one index is a dynamic value bool undefined; // at least one index is an undefined value }; extern void indices_to_expressions(Design*des, NetScope*scope, // loc is for error messages. const LineInfo*loc, // src is the index list, and count is // the number of items in the list to use. const list&src, unsigned count, // True if the expression MUST be constant. bool need_const, // These are the outputs. indices_flags&flags, list&indices,list&indices_const); extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); extern NetExpr*normalize_variable_unpacked(const netsarray_t*net, list&indices); extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); extern NetExpr*normalize_variable_unpacked(const LineInfo&loc, const netsarray_t*net, list&indices); extern NetExpr*make_canonical_index(Design*des, NetScope*scope, // loc for error messages const LineInfo*loc, // src is the index list const std::list&src, // This is the reference type const netsarray_t*stype, // True if the expression MUST be constant. bool need_const); /* * This function takes as input a NetNet signal and adds a constant * value to it. If the val is 0, then simply return sig. Otherwise, * return a new NetNet value that is the output of an addition. * * Not currently used. */ #if 0 extern NetNet*add_to_net(Design*des, NetNet*sig, long val); #endif extern NetNet*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig); /* * Make a NetEConst object that contains only X bits. */ extern NetEConst*make_const_x(unsigned long wid); extern NetEConst*make_const_0(unsigned long wid); extern NetEConst*make_const_val(unsigned long val); extern NetEConst*make_const_val_s(long val); /* * Make A const net */ extern NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid); extern NetNet* make_const_z(Design*des, NetScope*scope, unsigned long wid); /* * In some cases the lval is accessible as a pointer to the head of * a list of NetAssign_ objects. This function returns the width of * the l-value represented by this list. */ extern unsigned count_lval_width(const class NetAssign_*first); /* * This function elaborates an expression, and tries to evaluate it * right away. If the expression can be evaluated, this returns a * constant expression. If it cannot be evaluated, it returns whatever * it can. If the expression cannot be elaborated, return 0. * * The context_width is the width of the context where the expression is * being elaborated, or -1 if the expression is self-determined, or -2 * if the expression is lossless self-determined (this last option is * treated as standard self-determined if the gn_strict_expr_width flag * is set). * * cast_type allows the expression to be cast to a different type * (before it is evaluated). If cast to a vector type, the vector * width will be set to the context_width. The default value of * IVL_VT_NO_TYPE causes the expression to retain its self-determined * type. */ class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const =false, bool annotatable =false, ivl_variable_type_t cast_type =IVL_VT_NO_TYPE); extern NetExpr* elab_and_eval_lossless(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const =false, bool annotatable =false, ivl_variable_type_t cast_type =IVL_VT_NO_TYPE); /* * This form of elab_and_eval uses the ivl_type_t to carry type * information instead of the piecemeal form. We should transition to * this form as we reasonably can. */ extern NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*expr, ivl_type_t lv_net_type, bool need_const); /* * This function is a variant of elab_and_eval that elaborates and * evaluates the arguments of a system task. */ extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, unsigned arg_idx, PExpr*pe, bool need_const =false); /* * This function elaborates an expression as if it is for the r-value * of an assignment, The lv_type and lv_width are the type and width * of the l-value, and the expr is the expression to elaborate. The * result is the NetExpr elaborated and evaluated. (See elab_expr.cc) * * I would rather that all calls to elaborate_rval_expr use the * lv_net_type argument to express the l-value type, but, for now, * that it not possible. Those cases will be indicated by the * lv_net_type being set to nil. */ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, unsigned lv_width, PExpr*expr, bool need_const =false); extern bool evaluate_ranges(Design*des, NetScope*scope, std::vector&llist, const std::list&rlist); /* * This procedure evaluates an expression and if the evaluation is * successful the original expression is replaced with the new one. */ void eval_expr(NetExpr*&expr, int context_width =-1); /* * Get the long integer value for the passed in expression, if * possible. If it is not possible (the expression is not evaluated * down to a constant) then return false and leave value unchanged. */ bool eval_as_long(long&value, const NetExpr*expr); bool eval_as_double(double&value, NetExpr*expr); /* * Evaluate an entire scope path in the context of the given scope. */ extern std::list eval_scope_path(Design*des, NetScope*scope, const pform_name_t&path); extern hname_t eval_path_component(Design*des, NetScope*scope, const name_component_t&comp, bool&error_flag); /* * If this scope is contained within a class scope (i.e. a method of a * class) then return the class definition that contains it. */ extern const netclass_t*find_class_containing_scope(const LineInfo&loc,const NetScope*scope); extern NetScope* find_method_containing_scope(const LineInfo&log, NetScope*scope); /* * Return true if the data type is a type that is normally available * in vector for. IVL_VT_BOOL and IVL_VT_LOGIC are vectorable, * IVL_VT_REAL is not. */ extern bool type_is_vectorable(ivl_variable_type_t type); /* * Return a human readable version of the operator. */ const char *human_readable_op(const char op, bool unary = false); /* * Is the expression a constant value and if so what is its logical * value. * * C_NON - the expression is not a constant value. * C_0 - the expression is constant and it has a false value. * C_1 - the expression is constant and it has a true value. * C_X - the expression is constant and it has an 'bX value. */ enum const_bool { C_NON, C_0, C_1, C_X }; const_bool const_logical(const NetExpr*expr); extern bool dly_used_no_timescale; extern bool dly_used_timescale; extern bool display_ts_dly_warning; /* * When scaling a real value to a time we need to do some standard * processing. */ extern uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val); extern void collapse_partselect_pv_to_concat(Design*des, NetNet*sig); extern bool evaluate_index_prefix(Design*des, NetScope*scope, list&prefix_indices, const list&indices); extern NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net, const std::list&indices); extern NetExpr*collapse_array_exprs(Design*des, NetScope*scope, const LineInfo*loc, NetNet*net, const list&indices); extern void assign_unpacked_with_bufz(Design*des, NetScope*scope, const LineInfo*loc, NetNet*lval, NetNet*rval); extern NetPartSelect* detect_partselect_lval(Link&pin); #endif /* IVL_netmisc_H */ iverilog-10_1/netparray.cc000066400000000000000000000041051265551621300156560ustar00rootroot00000000000000/* * Copyright (c) 2012 Picture Elements, Inc. * Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netparray.h" using namespace std; netsarray_t::~netsarray_t() { } netparray_t::~netparray_t() { } /* * The packed width of a packed array is the packed width of the * element times the dimension width of the array itself. */ bool netparray_t::packed(void) const { return true; } long netparray_t::packed_width(void) const { long cur_width = element_type()->packed_width(); for (vector::const_iterator cur = static_dimensions().begin() ; cur != static_dimensions().end() ; ++cur) { cur_width *= cur->width(); } return cur_width; } vector netparray_t::slice_dimensions() const { const vector&packed_dims = static_dimensions(); vector elem_dims = element_type()->slice_dimensions(); vector res (packed_dims.size() + elem_dims.size()); for (size_t idx = 0 ; idx < packed_dims.size() ; idx += 1) res[idx] = packed_dims[idx]; for (size_t idx = 0 ; idx < elem_dims.size() ; idx += 1) res[idx+packed_dims.size()] = elem_dims[idx]; return res; } netuarray_t::~netuarray_t() { } vector netuarray_t::slice_dimensions() const { return static_dimensions(); } iverilog-10_1/netparray.h000066400000000000000000000050611265551621300155220ustar00rootroot00000000000000#ifndef IVL_netarray_H #define IVL_netarray_H /* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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 */ # include "nettypes.h" # include /* * Arrays with static dimensions (packed and unpacked) share this * common base type. */ class netsarray_t : public netarray_t { public: explicit netsarray_t(const std::vector&packed, ivl_type_t etype); ~netsarray_t(); public: // Virtual methods from the ivl_type_s type... public: inline const std::vector& static_dimensions() const { return dims_; } private: std::vector dims_; }; inline netsarray_t::netsarray_t(const std::vector&pd, ivl_type_t etype) : netarray_t(etype), dims_(pd) { } /* * Packed arrays. */ class netparray_t : public netsarray_t { public: explicit netparray_t(const std::vector&packed, ivl_type_t etype); ~netparray_t(); public: // Virtual methods from the ivl_type_s type... bool packed(void) const; long packed_width(void) const; std::vector slice_dimensions() const; }; inline netparray_t::netparray_t(const std::vector&pd, ivl_type_t etype) : netsarray_t(pd, etype) { } /* * Unpacked arrays are very similar, but lack packed slices. */ class netuarray_t : public netsarray_t { public: explicit netuarray_t(const std::vector&packed, ivl_type_t etype); ~netuarray_t(); public: // Virtual methods from the ivl_type_s type... std::vector slice_dimensions() const; }; inline netuarray_t::netuarray_t(const std::vector&pd, ivl_type_t etype) : netsarray_t(pd, etype) { } #endif /* IVL_netarray_H */ iverilog-10_1/netqueue.cc000066400000000000000000000024761265551621300155150ustar00rootroot00000000000000/* * Copyright (c) 2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netqueue.h" # include using namespace std; netqueue_t::netqueue_t(ivl_type_t vec) : netdarray_t(vec) { } netqueue_t::~netqueue_t() { } ivl_variable_type_t netqueue_t::base_type() const { return IVL_VT_QUEUE; } bool netqueue_t::test_compatibility(ivl_type_t that) const { const netqueue_t*that_q = dynamic_cast(that); if (that_q == 0) return false; return element_type()->type_compatible(that_q->element_type()); } iverilog-10_1/netqueue.h000066400000000000000000000032741265551621300153540ustar00rootroot00000000000000#ifndef IVL__netqueue_H #define IVL__netqueue_H /* * Copyright (c) 2014-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netdarray.h" # include "ivl_target.h" /* * A queue type is actually a dynamic array with a few extra * methods. This will probably result in a different implementation at * run-time, but for the most part this applies during elaboration. */ class netqueue_t : public netdarray_t { public: explicit netqueue_t(ivl_type_t vec); ~netqueue_t(); // This is the "base_type()" virtual method of the // nettype_base_t. The ivl_target api expects this to return // IVL_VT_QUEUE for queues. ivl_variable_type_t base_type() const; // A queue may have a type that is signed. inline bool get_signed() const { return element_type()->get_signed(); } std::ostream& debug_dump(std::ostream&) const; private: bool test_compatibility(ivl_type_t that) const; }; #endif iverilog-10_1/netscalar.cc000066400000000000000000000023171265551621300156300ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netscalar.h" using namespace std; netreal_t netreal_t::type_real; netreal_t netreal_t::type_shortreal; netstring_t netstring_t::type_string; netreal_t::~netreal_t() { } ivl_variable_type_t netreal_t::base_type() const { return IVL_VT_REAL; } netstring_t::~netstring_t() { } ivl_variable_type_t netstring_t::base_type() const { return IVL_VT_STRING; } iverilog-10_1/netscalar.h000066400000000000000000000026121265551621300154700ustar00rootroot00000000000000#ifndef IVL_netscalar_H #define IVL_netscalar_H /* * Copyright (c) 2013-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "nettypes.h" class netreal_t : public ivl_type_s { public: inline explicit netreal_t() { } ~netreal_t(); ivl_variable_type_t base_type() const; public: static netreal_t type_real; static netreal_t type_shortreal; }; class netstring_t : public ivl_type_s { public: inline explicit netstring_t() { } ~netstring_t(); ivl_variable_type_t base_type() const; public: static netstring_t type_string; }; #endif /* IVL_netscalar_H */ iverilog-10_1/netstruct.cc000066400000000000000000000074611265551621300157140ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netlist.h" # include "netstruct.h" # include "netvector.h" # include # include "ivl_assert.h" using namespace std; netstruct_t::netstruct_t() : union_(false), packed_(false) { } netstruct_t::~netstruct_t() { } void netstruct_t::union_flag(bool flag) { // This MUST be called before any members are pushed into the // definition. This is because the append relies on this flag // being accurate. ivl_assert(*this, members_.empty()); union_ = flag; } void netstruct_t::packed(bool flag) { ivl_assert(*this, members_.empty()); packed_ = flag; } void netstruct_t::append_member(Design*des, const netstruct_t::member_t&val) { ivl_assert(*this, val.net_type); members_.push_back(val); if (packed_) { if (! members_.back().net_type->packed()) { cerr << get_fileline() << ": error: " << "Member " << members_.back().name << " of packed struct/union" << " must be packed." << endl; des->errors += 1; } } if (union_ && packed_ && members_.size() > 1) { unsigned long expect_wid = members_.front().net_type->packed_width(); unsigned long got_wid = members_.back().net_type->packed_width(); if (expect_wid != got_wid) { cerr << get_fileline() << ": error: " << "Member " << val.name << " of packed union" << " is " << got_wid << " bits, expecting " << expect_wid << " bits." << endl; des->errors += 1; } } } const netstruct_t::member_t* netstruct_t::packed_member(perm_string name, unsigned long&off) const { unsigned long count_off = 0; for (size_t idx = members_.size() ; idx > 0 ; idx -= 1) { if (members_[idx-1].name == name) { off = count_off; return &members_[idx-1]; } // If this is not a union, then the members are lined up // from LSB to MSB. If this is a union, then all // members are at offset 0. if (!union_) count_off += members_[idx-1].net_type->packed_width(); } return 0; } long netstruct_t::packed_width(void) const { if (! packed_) return -1; // If this is a packed union, then all the members are the // same width, so it is sufficient to return the width of any // single member. if (union_) return members_.front().net_type->packed_width(); // The width of a packed struct is the sum of member widths. long res = 0; for (size_t idx = 0 ; idx < members_.size() ; idx += 1) res += members_[idx].net_type->packed_width(); return res; } vector netstruct_t::slice_dimensions() const { vector tmp; tmp .push_back(netrange_t(packed_width()-1, 0)); return tmp; } ivl_variable_type_t netstruct_t::base_type() const { if (! packed_) return IVL_VT_NO_TYPE; for (size_t idx = 0 ; idx < members_.size() ; idx += 1) { if (members_[idx].data_type() != IVL_VT_BOOL) return members_[idx].data_type(); } return IVL_VT_BOOL; } iverilog-10_1/netstruct.h000066400000000000000000000054021265551621300155470ustar00rootroot00000000000000#ifndef IVL_netstruct_H #define IVL_netstruct_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include # include "ivl_target.h" # include "nettypes.h" class Design; class netstruct_t : public LineInfo, public ivl_type_s { public: struct member_t { perm_string name; ivl_type_t net_type; inline ivl_variable_type_t data_type() const { return net_type->base_type(); }; // We need to keep the individual element sign information. bool get_signed() const { return false; }; }; public: netstruct_t(); ~netstruct_t(); // If this is a union (instead of struct) then this flag is // set. We handle union and struct together because they are // so similar. void union_flag(bool); bool union_flag(void) const; void packed(bool flag); bool packed(void) const; // Append a new member to the struct/union. This must be done // after the union_flag and packed settings are set. This // function does error checking, and the "des" argument is // only present so that it can set error flags. void append_member(Design*des, const member_t&); // Given the name of a member, return a pointer to the member // description, and set the off value to be the offset into // the packed value where the member begins. const struct member_t* packed_member(perm_string name, unsigned long&off) const; // Return the width (in bits) of the packed record, or -1 if // the record is not packed. long packed_width() const; std::vector slice_dimensions() const; // Return the base type of the packed record, or // IVL_VT_NO_TYPE if the record is not packed. ivl_variable_type_t base_type() const; private: bool union_; bool packed_; std::vectormembers_; }; inline bool netstruct_t::union_flag(void) const { return union_; } inline bool netstruct_t::packed(void) const { return packed_; } #endif /* IVL_netstruct_H */ iverilog-10_1/nettypes.cc000066400000000000000000000105371265551621300155320ustar00rootroot00000000000000/* * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "nettypes.h" # include # include using namespace std; ivl_type_s::~ivl_type_s() { } /* * The derived class may override this to provide a more accurate * response. */ bool ivl_type_s::packed(void) const { return false; } long ivl_type_s::packed_width(void) const { return 1; } vector ivl_type_s::slice_dimensions() const { return vector(); } ivl_variable_type_t ivl_type_s::base_type() const { return IVL_VT_NO_TYPE; } bool ivl_type_s::get_signed() const { return false; } bool ivl_type_s::type_compatible(ivl_type_t that) const { if (this == that) return true; return test_compatibility(that); } bool ivl_type_s::test_compatibility(const ivl_type_s* /*that*/) const { return false; } netarray_t::~netarray_t() { } ivl_variable_type_t netarray_t::base_type() const { return element_type_->base_type(); } unsigned long netrange_width(const vector&packed) { unsigned wid = 1; for (vector::const_iterator cur = packed.begin() ; cur != packed.end() ; ++cur) { unsigned use_wid = cur->width(); wid *= use_wid; } return wid; } /* * Given a netrange_t list (which represent packed dimensions) and a * prefix of calculated index values, calculate the canonical offset * and width of the resulting slice. In this case, the "sb" argument * is an extra index of the prefix. */ bool prefix_to_slice(const std::vector&dims, const std::list&prefix, long sb, long&loff, unsigned long&lwid) { assert(prefix.size() < dims.size()); // Figure out the width of the slice, given the number of // prefix numbers there are. We don't need to look at the // actual values yet, but we do need to know how many there // are compared to the actual dimensions of the target. So do // this by multiplying the widths of the dims that are NOT // accounted for by the prefix or sb indices. size_t acc_wid = 1; vector::const_iterator pcur = dims.end(); for (size_t idx = prefix.size()+1 ; idx < dims.size() ; idx += 1) { -- pcur; acc_wid *= pcur->width(); } lwid = acc_wid; // lwid is now the final slice width. // pcur is pointing to the dimension AFTER the dimension that // we have an index for, so step back one, then this will be // used with the sb index. Start accumulating in the acc_off // the offset into the n-dimensional vector. -- pcur; if (sb < pcur->get_msb() && sb < pcur->get_lsb()) return false; if (sb > pcur->get_msb() && sb > pcur->get_lsb()) return false; long acc_off = 0; if (pcur->get_msb() >= pcur->get_lsb()) acc_off += (sb - pcur->get_lsb()) * acc_wid; else acc_off += (pcur->get_lsb() - sb) * acc_wid; // If there are no more prefix items, we are done. if (prefix.empty()) { loff = acc_off; return true; } // Now similarly go through the prefix numbers, working // through the dimensions until we run out. Accumulate a // growing slice width (acc_wid) that is used to calculate the // growing offset (acc_off). list::const_iterator icur = prefix.end(); do { -- icur; acc_wid *= pcur->width(); if (pcur->get_msb() >= pcur->get_lsb()) acc_off += (*icur - pcur->get_lsb()) * acc_wid; else acc_off += (pcur->get_lsb() - *icur) * acc_wid; -- pcur; } while (icur != prefix.begin()); // Got our final offset. loff = acc_off; return true; } iverilog-10_1/nettypes.h000066400000000000000000000111171265551621300153670ustar00rootroot00000000000000#ifndef IVL_nettypes_H #define IVL_nettypes_H /* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "ivl_target.h" # include # include # include # include # include class netrange_t; /* * This is a fully abstract type that is a type that can be attached * to a NetNet object. */ class ivl_type_s { public: virtual ~ivl_type_s() =0; virtual bool packed(void) const; virtual long packed_width(void) const; virtual std::vector slice_dimensions() const; // Some types have a base variable type. This is the bit type // for packed data types, or IVL_VT_DARRAY or IVL_VT_CLASS for // those specific types. virtual ivl_variable_type_t base_type() const; virtual bool get_signed() const; // Return true if "that" type is compatible with this // type. Compatible means the types are essentially the same. bool type_compatible(ivl_type_t that) const; virtual std::ostream& debug_dump(std::ostream&) const; private: // The "type_compatible" method uses this virtual method to // invoke type-specific tests of compatibility. This should // only be called by the type_compatible method above. virtual bool test_compatibility(ivl_type_t that) const; }; /* * There are a couple types of array types. This class represents the * common bits of array types. */ class netarray_t : public ivl_type_s { public: inline explicit netarray_t(ivl_type_t etype) : element_type_(etype) { } ~netarray_t(); public: // Some virtual methods have a common implementation for arrays. // The base_type() for arrays is the base_Typeof the element. ivl_variable_type_t base_type() const; public: inline ivl_type_t element_type() const { return element_type_; } private: ivl_type_t element_type_; }; inline static std::ostream& operator << (std::ostream&out, const ivl_type_s&obj) { return obj.debug_dump(out); } class netrange_t { public: // Create an undefined range. An undefined range is a range // used to declare dynamic arrays, etc. inline netrange_t() : msb_(LONG_MAX), lsb_(LONG_MAX) { } // Create a properly defined netrange inline netrange_t(long m, long l) : msb_(m), lsb_(l) { } // Copy constructor. inline netrange_t(const netrange_t&that) : msb_(that.msb_), lsb_(that.lsb_) { } inline netrange_t& operator = (const netrange_t&that) { msb_ = that.msb_; lsb_ = that.lsb_; return *this; } inline bool defined() const { return msb_!=LONG_MAX || lsb_!= LONG_MAX; } inline unsigned long width()const { if (!defined()) return 0; else if (msb_ >= lsb_) return msb_-lsb_+1; else return lsb_-msb_+1; } inline long get_msb() const { assert(defined()); return msb_; } inline long get_lsb() const { assert(defined()); return lsb_; } inline bool operator == (const netrange_t&that) const { if (msb_ != that.msb_) return false; if (lsb_ != that.lsb_) return false; return true; } inline bool operator != (const netrange_t&that) const { if (msb_ != that.msb_) return true; if (lsb_ != that.lsb_) return true; return false; } private: long msb_; long lsb_; }; extern std::ostream&operator << (std::ostream&out, const std::list&rlist); extern std::ostream&operator << (std::ostream&out, const std::vector&rlist); extern unsigned long netrange_width(const std::vector&dims); /* * Take as input a list of packed dimensions and a list of prefix * indices, and calculate the offset/width of the resulting slice into * the packed array. */ extern bool prefix_to_slice(const std::vector&dims, const std::list&prefix, long sb, long&loff, unsigned long&lwid); #endif /* IVL_nettypes_H */ iverilog-10_1/netvector.cc000066400000000000000000000052611265551621300156660ustar00rootroot00000000000000/* * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netvector.h" # include using namespace std; netvector_t netvector_t::atom2s64 (IVL_VT_BOOL, 63, 0, true); netvector_t netvector_t::atom2u64 (IVL_VT_BOOL, 63, 0, false); netvector_t netvector_t::atom2s32 (IVL_VT_BOOL, 31, 0, true); netvector_t netvector_t::atom2u32 (IVL_VT_BOOL, 31, 0, false); netvector_t netvector_t::atom2s16 (IVL_VT_BOOL, 15, 0, true); netvector_t netvector_t::atom2u16 (IVL_VT_BOOL, 15, 0, false); netvector_t netvector_t::atom2s8 (IVL_VT_BOOL, 7, 0, true); netvector_t netvector_t::atom2u8 (IVL_VT_BOOL, 7, 0, false); //netvector_t netvector_t::scalar_bool (IVL_VT_BOOL); netvector_t netvector_t::scalar_logic (IVL_VT_LOGIC); netvector_t::netvector_t(ivl_variable_type_t type, long msb, long lsb, bool flag) : type_(type), signed_(flag), isint_(false), is_scalar_(false) { packed_dims_.push_back(netrange_t(msb,lsb)); } netvector_t::netvector_t(ivl_variable_type_t type) : type_(type), signed_(false), isint_(false), is_scalar_(false) { } netvector_t::~netvector_t() { } ivl_variable_type_t netvector_t::base_type() const { return type_; } /* * vectors are by definition packed. */ bool netvector_t::packed(void) const { return true; } long netvector_t::packed_width() const { return netrange_width(packed_dims_); } vector netvector_t::slice_dimensions() const { return packed_dims_; } bool netvector_t::test_compatibility(ivl_type_t that) const { const netvector_t*that_st = dynamic_cast(that); if (that_st == 0) return false; if (type_ != that_st->type_) return false; if (packed_dims_.size() != that_st->packed_dims_.size()) return false; for (size_t idx = 0 ; idx < packed_dims_.size() ; idx += 1) { if (packed_dims_[idx] != that_st->packed_dims_[idx]) return false; } return true; } iverilog-10_1/netvector.h000066400000000000000000000070741265551621300155340ustar00rootroot00000000000000#ifndef IVL_netvector_H #define IVL_netvector_H /* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "nettypes.h" # include "ivl_target.h" # include class netvector_t : public ivl_type_s { public: explicit netvector_t(const std::vector&packed, ivl_variable_type_t type); // This is a variant of the vector form. Some code processes // the list of packed ranges as a list, but we will store them // as a vector in this constructor. explicit netvector_t(const std::list&packed, ivl_variable_type_t type); // special case: there is a single packed dimension and we // know it in the form [:]. This step saves me // creating a netrange_t for this single item. explicit netvector_t(ivl_variable_type_t type, long msb, long lsb, bool signed_flag =false); // Special case: scalar object--no packed dimensions at all. explicit netvector_t(ivl_variable_type_t type); ~netvector_t(); // Vectors can be interpreted as signed or unsigned when // handled as vectors. inline void set_signed(bool flag) { signed_ = flag; } inline bool get_signed(void) const { return signed_; } inline void set_isint(bool flag) { isint_ = flag; } inline bool get_isint(void) const { return isint_; } inline void set_scalar(bool flag) { is_scalar_ = flag; } inline bool get_scalar(void) const { return is_scalar_; } ivl_variable_type_t base_type() const; const std::vector&packed_dims() const; bool packed(void) const; long packed_width() const; std::vector slice_dimensions() const; std::ostream& debug_dump(std::ostream&) const; public: // Some commonly used predefined types static netvector_t atom2s64; static netvector_t atom2u64; static netvector_t atom2s32; static netvector_t atom2u32; static netvector_t atom2s16; static netvector_t atom2u16; static netvector_t atom2s8; static netvector_t atom2u8; static netvector_t scalar_bool; static netvector_t scalar_logic; private: bool test_compatibility(ivl_type_t that) const; private: std::vector packed_dims_; ivl_variable_type_t type_; bool signed_ : 1; bool isint_ : 1; // original type of integer bool is_scalar_ : 1; }; inline netvector_t::netvector_t(const std::vector&pd, ivl_variable_type_t type) : packed_dims_(pd), type_(type), signed_(false), isint_(false), is_scalar_(false) { } inline const std::vector& netvector_t::packed_dims() const { return packed_dims_; } inline static std::ostream& operator << (std::ostream&out, const netvector_t&obj) { return obj.debug_dump(out); } #endif /* IVL_netvector_H */ iverilog-10_1/nodangle.cc000066400000000000000000000167021265551621300154460ustar00rootroot00000000000000/* * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * This functor scans the design looking for dangling objects and * excess local signals. These deletions are not necessarily required * for proper functioning of anything, but they can clean up the * appearance of design files that are generated. */ # include "functor.h" # include "netlist.h" # include "compiler.h" class nodangle_f : public functor_t { public: void event(Design*des, NetEvent*ev); void signal(Design*des, NetNet*sig); unsigned iteration; unsigned stotal, etotal; bool scontinue, econtinue; bool scomplete, ecomplete; }; void nodangle_f::event(Design*, NetEvent*ev) { if (ecomplete) return; /* If there are no references to this event, then go right ahead and delete it. There is no use looking further at it. */ if ((ev->nwait() + ev->ntrig() + ev->nexpr()) == 0) { delete ev; etotal += 1; return; } if (iteration == 0) { /* Try to remove duplicate probes from the event. This is done as a separate initial pass to ensure similar events are detected as soon as possible in subsequent iterations. */ for (unsigned idx = 0 ; idx < ev->nprobe() ; idx += 1) { unsigned jdx = idx + 1; while (jdx < ev->nprobe()) { NetEvProbe*ip = ev->probe(idx); NetEvProbe*jp = ev->probe(jdx); if (ip->edge() != jp->edge()) { jdx += 1; continue; } bool fully_connected = true; for (unsigned jpin = 0; jpin < jp->pin_count(); jpin += 1) { unsigned ipin = 0; bool connected_flag = false; for (ipin = 0 ; ipin < ip->pin_count(); ipin += 1) if (connected(ip->pin(ipin), jp->pin(jpin))) { connected_flag = true; break; } if (!connected_flag) { fully_connected = false; break; } } if (fully_connected) { delete jp; } else { jdx += 1; } } } econtinue = true; } else { /* Postpone examining events in an automatic scope until the third (optional) pass. This will mean similar events are biased towards being stored in static scopes. */ if (ev->scope()->is_auto()) { if (iteration == 1) { econtinue = true; return; } } else { if (iteration == 2) { return; } } /* Try to find all the events that are similar to me, and replace their references with references to me. */ list match; ev->find_similar_event(match); for (list::iterator idx = match.begin() ; idx != match.end() ; ++ idx ) { NetEvent*tmp = *idx; assert(tmp != ev); tmp ->replace_event(ev); } } } void nodangle_f::signal(Design*, NetNet*sig) { if (scomplete) return; /* Cannot delete signals referenced in an expression or an l-value. */ if (sig->get_refs() > 0) return; /* Cannot delete the ports of tasks, functions or modules. There are too many places where they are referenced. */ if ((sig->port_type() != NetNet::NOT_A_PORT) && ((sig->scope()->type() == NetScope::TASK) || (sig->scope()->type() == NetScope::FUNC) || (sig->scope()->type() == NetScope::MODULE))) return; /* Can't delete ports of cells. */ if ((sig->port_type() != NetNet::NOT_A_PORT) && (sig->scope()->attribute(perm_string::literal("ivl_synthesis_cell")) != verinum())) return; /* Don't delete signals that are marked with the ivl_do_not_elide property. */ if (!sig->local_flag() && (sig->attribute(perm_string::literal("ivl_do_not_elide")) != verinum())) return; /* Check to see if the signal is completely unconnected. If all the bits are unlinked, then delete it. */ if (! sig->is_linked()) { delete sig; stotal += 1; return; } /* The remaining things can only be done to synthesized signals, not ones that appear in the original Verilog. */ if (! sig->local_flag()) return; /* Check to see if there is some significant signal connected to every pin of this signal. */ unsigned significant_flags = 0; for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) { Nexus*nex = sig->pin(idx).nexus(); for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { if (cur == &sig->pin(idx)) continue; NetNet*cursig = dynamic_cast(cur->get_obj()); if (cursig == 0) continue; if (cursig->local_flag()) continue; significant_flags += 1; break; } if (significant_flags <= idx) break; } /* If every pin is connected to another significant signal, then I can delete this one. */ if (significant_flags == sig->pin_count()) { delete sig; stotal += 1; } } void nodangle(Design*des) { nodangle_f fun; fun.iteration = 0; fun.stotal = 0; fun.etotal = 0; fun.scomplete = false; fun.ecomplete = false; do { if (verbose_flag) { cout << " ... scan for dangling signal and event nodes. " << "(scomplete=" << (fun.scomplete? "T" : "F") << ", ecomplete=" << (fun.ecomplete? "T" : "F") << ")" << endl << flush; } fun.scontinue = false; fun.econtinue = false; des->functor(&fun); fun.iteration += 1; fun.scomplete = !fun.scontinue; fun.ecomplete = !fun.econtinue; if (verbose_flag) { cout << " ... " << fun.iteration << " iterations" << " deleted " << fun.stotal << " dangling signals" << " and " << fun.etotal << " events." << endl << flush; } } while (fun.scontinue || fun.econtinue); if (verbose_flag) { cout << " ... done" << endl << flush; } } iverilog-10_1/pad_to_width.cc000066400000000000000000000110011265551621300163070ustar00rootroot00000000000000/* * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "netlist.h" # include "netvector.h" # include "netmisc.h" /* * This function transforms an expression by padding the high bits * with V0 until the expression has the desired width. This may mean * not transforming the expression at all, if it is already wide * enough. */ NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info) { if (wid <= expr->expr_width()) return expr; /* If the expression is a const, then replace it with a wider const. This is a more efficient result. */ if (NetEConst*tmp = dynamic_cast(expr)) { verinum oval = pad_to_width(tmp->value(), wid); tmp = new NetEConst(oval); tmp->set_line(info); delete expr; return tmp; } NetESelect*tmp = new NetESelect(expr, 0, wid); tmp->set_line(info); tmp->cast_signed(expr->has_sign()); return tmp; } /* * Pad a NetNet to the desired vector width by concatenating a * NetConst of constant zeros. Use a NetConcat node to do the * concatenation. */ NetNet*pad_to_width(Design*des, NetNet*net, unsigned wid, const LineInfo&info) { NetScope*scope = net->scope(); if (net->vector_width() >= wid) return net; // Make the NetConcat and connect the input net to the lsb input. NetConcat*cc = new NetConcat(scope, scope->local_symbol(), wid, 2); cc->set_line(info); des->add_node(cc); connect(cc->pin(1), net->pin(0)); // Make a NetConst of the desired width and connect in to the // lsb input of the NetConcat. verinum pad(verinum::V0, wid - net->vector_width()); NetConst*con = new NetConst(scope, scope->local_symbol(), pad); con->set_line(info); des->add_node(con); connect(cc->pin(2), con->pin(0)); // Make a NetNet for the NetConst to NetConcat link. netvector_t*tmp_vec = new netvector_t(net->data_type(), wid - net->vector_width() - 1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(info); tmp->local_flag(true); connect(cc->pin(2), tmp->pin(0)); // Create a NetNet of the output width and connect it to the // NetConcat node output pin. tmp_vec = new netvector_t(net->data_type(), wid-1, 0); tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(info); tmp->local_flag(true); connect(cc->pin(0), tmp->pin(0)); return tmp; } NetNet*pad_to_width_signed(Design*des, NetNet*net, unsigned wid, const LineInfo&info) { NetScope*scope = net->scope(); if (net->vector_width() >= wid) return net; NetSignExtend*se = new NetSignExtend(scope, scope->local_symbol(), wid); se->set_line(info); des->add_node(se); netvector_t*tmp_vec = new netvector_t(net->data_type(), wid-1, 0); tmp_vec->set_signed(true); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(info); tmp->local_flag(true); connect(tmp->pin(0), se->pin(0)); connect(se->pin(1), net->pin(0)); return tmp; } NetNet*crop_to_width(Design*des, NetNet*net, unsigned wid) { NetScope*scope = net->scope(); if (net->vector_width() <= wid) return net; NetPartSelect*ps = new NetPartSelect(net, 0, wid, NetPartSelect::VP); ps->set_line(*net); des->add_node(ps); netvector_t*tmp_vec = new netvector_t(net->data_type(), wid-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec); tmp->set_line(*net); tmp->local_flag(true); connect(ps->pin(0), tmp->pin(0)); return tmp; } iverilog-10_1/parse.y000066400000000000000000005622661265551621300146670ustar00rootroot00000000000000 %{ /* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "parse_misc.h" # include "compiler.h" # include "pform.h" # include "Statement.h" # include "PSpec.h" # include # include # include class PSpecPath; extern void lex_end_table(); bool have_timeunit_decl = false; bool have_timeprec_decl = false; static list* param_active_range = 0; static bool param_active_signed = false; static ivl_variable_type_t param_active_type = IVL_VT_LOGIC; /* Port declaration lists use this structure for context. */ static struct { NetNet::Type port_net_type; NetNet::PortType port_type; data_type_t* data_type; } port_declaration_context = {NetNet::NONE, NetNet::NOT_A_PORT, 0}; /* Modport port declaration lists use this structure for context. */ enum modport_port_type_t { MP_NONE, MP_SIMPLE, MP_TF, MP_CLOCKING }; static struct { modport_port_type_t type; union { NetNet::PortType direction; bool is_import; }; } last_modport_port = { MP_NONE, {NetNet::NOT_A_PORT}}; /* The task and function rules need to briefly hold the pointer to the task/function that is currently in progress. */ static PTask* current_task = 0; static PFunction* current_function = 0; static stack current_block_stack; static pform_name_t* pform_create_this(void) { name_component_t name (perm_string::literal("@")); pform_name_t*res = new pform_name_t; res->push_back(name); return res; } static pform_name_t* pform_create_super(void) { name_component_t name (perm_string::literal("#")); pform_name_t*res = new pform_name_t; res->push_back(name); return res; } /* This is used to keep track of the extra arguments after the notifier * in the $setuphold and $recrem timing checks. This allows us to print * a warning message that the delayed signals will not be created. We * need to do this since not driving these signals creates real * simulation issues. */ static unsigned args_after_notifier; /* The rules sometimes push attributes into a global context where sub-rules may grab them. This makes parser rules a little easier to write in some cases. */ static list*attributes_in_context = 0; /* Later version of bison (including 1.35) will not compile in stack extension if the output is compiled with C++ and either the YYSTYPE or YYLTYPE are provided by the source code. However, I can get the old behavior back by defining these symbols. */ # define YYSTYPE_IS_TRIVIAL 1 # define YYLTYPE_IS_TRIVIAL 1 /* Recent version of bison expect that the user supply a YYLLOC_DEFAULT macro that makes up a yylloc value from existing values. I need to supply an explicit version to account for the text field, that otherwise won't be copied. The YYLLOC_DEFAULT blends the file range for the tokens of Rhs rule, which has N tokens. */ # define YYLLOC_DEFAULT(Current, Rhs, N) do { \ if (N) { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ (Current).text = YYRHSLOC (Rhs, 1).text; \ } else { \ (Current).first_line = YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = YYRHSLOC (Rhs, 0).last_column; \ (Current).last_line = YYRHSLOC (Rhs, 0).last_line; \ (Current).last_column = YYRHSLOC (Rhs, 0).last_column; \ (Current).text = YYRHSLOC (Rhs, 0).text; \ } \ } while (0) /* * These are some common strength pairs that are used as defaults when * the user is not otherwise specific. */ static const struct str_pair_t pull_strength = { IVL_DR_PULL, IVL_DR_PULL }; static const struct str_pair_t str_strength = { IVL_DR_STRONG, IVL_DR_STRONG }; static list >* make_port_list(char*id, PExpr*expr) { list >*tmp = new list >; tmp->push_back(make_pair(lex_strings.make(id), expr)); delete[]id; return tmp; } static list >* make_port_list(list >*tmp, char*id, PExpr*expr) { tmp->push_back(make_pair(lex_strings.make(id), expr)); delete[]id; return tmp; } list* make_range_from_width(uint64_t wid) { pform_range_t range; range.first = new PENumber(new verinum(wid-1, integer_width)); range.second = new PENumber(new verinum((uint64_t)0, integer_width)); list*rlist = new list; rlist->push_back(range); return rlist; } static list* list_from_identifier(char*id) { list*tmp = new list; tmp->push_back(lex_strings.make(id)); delete[]id; return tmp; } static list* list_from_identifier(list*tmp, char*id) { tmp->push_back(lex_strings.make(id)); delete[]id; return tmp; } list* copy_range(list* orig) { list*copy = 0; if (orig) copy = new list (*orig); return copy; } template void append(vector&out, const vector&in) { for (size_t idx = 0 ; idx < in.size() ; idx += 1) out.push_back(in[idx]); } /* * Look at the list and pull null pointers off the end. */ static void strip_tail_items(list*lst) { while (! lst->empty()) { if (lst->back() != 0) return; lst->pop_back(); } } /* * This is a shorthand for making a PECallFunction that takes a single * arg. This is used by some of the code that detects built-ins. */ static PECallFunction*make_call_function(perm_string tn, PExpr*arg) { vector parms(1); parms[0] = arg; PECallFunction*tmp = new PECallFunction(tn, parms); return tmp; } static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) { vector parms(2); parms[0] = arg1; parms[1] = arg2; PECallFunction*tmp = new PECallFunction(tn, parms); return tmp; } static list* make_named_numbers(perm_string name, long first, long last, PExpr*val =0) { list*lst = new list; named_pexpr_t tmp; // We are counting up. if (first <= last) { for (long idx = first ; idx <= last ; idx += 1) { ostringstream buf; buf << name.str() << idx << ends; tmp.name = lex_strings.make(buf.str()); tmp.parm = val; val = 0; lst->push_back(tmp); } // We are counting down. } else { for (long idx = first ; idx >= last ; idx -= 1) { ostringstream buf; buf << name.str() << idx << ends; tmp.name = lex_strings.make(buf.str()); tmp.parm = val; val = 0; lst->push_back(tmp); } } return lst; } static list* make_named_number(perm_string name, PExpr*val =0) { list*lst = new list; named_pexpr_t tmp; tmp.name = name; tmp.parm = val; lst->push_back(tmp); return lst; } static long check_enum_seq_value(const YYLTYPE&loc, verinum *arg, bool zero_ok) { long value = 1; // We can never have an undefined value in an enumeration name // declaration sequence. if (! arg->is_defined()) { yyerror(loc, "error: undefined value used in enum name sequence."); // We can never have a negative value in an enumeration name // declaration sequence. } else if (arg->is_negative()) { yyerror(loc, "error: negative value used in enum name sequence."); } else { value = arg->as_ulong(); // We cannot have a zero enumeration name declaration count. if (! zero_ok && (value == 0)) { yyerror(loc, "error: zero count used in enum name sequence."); value = 1; } } return value; } static void current_task_set_statement(const YYLTYPE&loc, vector*s) { if (s == 0) { /* if the statement list is null, then the parser detected the case that there are no statements in the task. If this is SystemVerilog, handle it as an an empty block. */ if (!gn_system_verilog()) { yyerror(loc, "error: Support for empty tasks requires SystemVerilog."); } PBlock*tmp = new PBlock(PBlock::BL_SEQ); FILE_NAME(tmp, loc); current_task->set_statement(tmp); return; } assert(s); /* An empty vector represents one or more null statements. Handle this as a simple null statement. */ if (s->empty()) return; /* A vector of 1 is handled as a simple statement. */ if (s->size() == 1) { current_task->set_statement((*s)[0]); return; } if (!gn_system_verilog()) { yyerror(loc, "error: Task body with multiple statements requires SystemVerilog."); } PBlock*tmp = new PBlock(PBlock::BL_SEQ); FILE_NAME(tmp, loc); tmp->set_statement(*s); current_task->set_statement(tmp); } static void current_function_set_statement(const YYLTYPE&loc, vector*s) { if (s == 0) { /* if the statement list is null, then the parser detected the case that there are no statements in the task. If this is SystemVerilog, handle it as an an empty block. */ if (!gn_system_verilog()) { yyerror(loc, "error: Support for empty functions requires SystemVerilog."); } PBlock*tmp = new PBlock(PBlock::BL_SEQ); FILE_NAME(tmp, loc); current_function->set_statement(tmp); return; } assert(s); /* An empty vector represents one or more null statements. Handle this as a simple null statement. */ if (s->empty()) return; /* A vector of 1 is handled as a simple statement. */ if (s->size() == 1) { current_function->set_statement((*s)[0]); return; } if (!gn_system_verilog()) { yyerror(loc, "error: Function body with multiple statements requires SystemVerilog."); } PBlock*tmp = new PBlock(PBlock::BL_SEQ); FILE_NAME(tmp, loc); tmp->set_statement(*s); current_function->set_statement(tmp); } %} %union { bool flag; char letter; int int_val; /* text items are C strings allocated by the lexor using strdup. They can be put into lists with the texts type. */ char*text; list*perm_strings; list >*port_list; vector* tf_ports; pform_name_t*pform_name; ivl_discipline_t discipline; hname_t*hier; list*strings; struct str_pair_t drive; PCase::Item*citem; svector*citems; lgate*gate; svector*gates; Module::port_t *mport; LexicalScope::range_t* value_range; vector*mports; named_number_t* named_number; list* named_numbers; named_pexpr_t*named_pexpr; list*named_pexprs; struct parmvalue_t*parmvalue; list*ranges; PExpr*expr; list*exprs; svector*event_expr; NetNet::Type nettype; PGBuiltin::Type gatetype; NetNet::PortType porttype; ivl_variable_type_t vartype; PBlock::BL_TYPE join_keyword; PWire*wire; vector*wires; PEventStatement*event_statement; Statement*statement; vector*statement_list; net_decl_assign_t*net_decl_assign; enum_type_t*enum_type; decl_assignment_t*decl_assignment; list*decl_assignments; struct_member_t*struct_member; list*struct_members; struct_type_t*struct_type; data_type_t*data_type; class_type_t*class_type; real_type_t::type_t real_type; property_qualifier_t property_qualifier; PPackage*package; struct { char*text; data_type_t*type; } type_identifier; struct { data_type_t*type; list*exprs; } class_declaration_extends; verinum* number; verireal* realtime; PSpecPath* specpath; list *dimensions; }; %token IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL %token TYPE_IDENTIFIER %token PACKAGE_IDENTIFIER %token DISCIPLINE_IDENTIFIER %token PATHPULSE_IDENTIFIER %token BASED_NUMBER DEC_NUMBER UNBASED_NUMBER %token REALTIME %token K_PLUS_EQ K_MINUS_EQ K_INCR K_DECR %token K_LE K_GE K_EG K_EQ K_NE K_CEQ K_CNE K_LP K_LS K_RS K_RSS K_SG /* K_CONTRIBUTE is <+, the contribution assign. */ %token K_CONTRIBUTE %token K_PO_POS K_PO_NEG K_POW %token K_PSTAR K_STARP K_DOTSTAR %token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER %token K_SCOPE_RES %token K_edge_descriptor /* The base tokens from 1364-1995. */ %token K_always K_and K_assign K_begin K_buf K_bufif0 K_bufif1 K_case %token K_casex K_casez K_cmos K_deassign K_default K_defparam K_disable %token K_edge K_else K_end K_endcase K_endfunction K_endmodule %token K_endprimitive K_endspecify K_endtable K_endtask K_event K_for %token K_force K_forever K_fork K_function K_highz0 K_highz1 K_if %token K_ifnone K_initial K_inout K_input K_integer K_join K_large %token K_macromodule K_medium K_module K_nand K_negedge K_nmos K_nor %token K_not K_notif0 K_notif1 K_or K_output K_parameter K_pmos K_posedge %token K_primitive K_pull0 K_pull1 K_pulldown K_pullup K_rcmos K_real %token K_realtime K_reg K_release K_repeat K_rnmos K_rpmos K_rtran %token K_rtranif0 K_rtranif1 K_scalared K_small K_specify K_specparam %token K_strong0 K_strong1 K_supply0 K_supply1 K_table K_task K_time %token K_tran K_tranif0 K_tranif1 K_tri K_tri0 K_tri1 K_triand K_trior %token K_trireg K_vectored K_wait K_wand K_weak0 K_weak1 K_while K_wire %token K_wor K_xnor K_xor %token K_Shold K_Snochange K_Speriod K_Srecovery K_Ssetup K_Ssetuphold %token K_Sskew K_Swidth /* Icarus specific tokens. */ %token KK_attribute K_bool K_logic /* The new tokens from 1364-2001. */ %token K_automatic K_endgenerate K_generate K_genvar K_localparam %token K_noshowcancelled K_pulsestyle_onevent K_pulsestyle_ondetect %token K_showcancelled K_signed K_unsigned %token K_Sfullskew K_Srecrem K_Sremoval K_Stimeskew /* The 1364-2001 configuration tokens. */ %token K_cell K_config K_design K_endconfig K_incdir K_include K_instance %token K_liblist K_library K_use /* The new tokens from 1364-2005. */ %token K_wone K_uwire /* The new tokens from 1800-2005. */ %token K_alias K_always_comb K_always_ff K_always_latch K_assert %token K_assume K_before K_bind K_bins K_binsof K_bit K_break K_byte %token K_chandle K_class K_clocking K_const K_constraint K_context %token K_continue K_cover K_covergroup K_coverpoint K_cross K_dist K_do %token K_endclass K_endclocking K_endgroup K_endinterface K_endpackage %token K_endprogram K_endproperty K_endsequence K_enum K_expect K_export %token K_extends K_extern K_final K_first_match K_foreach K_forkjoin %token K_iff K_ignore_bins K_illegal_bins K_import K_inside K_int /* Icarus already has defined "logic" above! */ %token K_interface K_intersect K_join_any K_join_none K_local %token K_longint K_matches K_modport K_new K_null K_package K_packed %token K_priority K_program K_property K_protected K_pure K_rand K_randc %token K_randcase K_randsequence K_ref K_return K_sequence K_shortint %token K_shortreal K_solve K_static K_string K_struct K_super %token K_tagged K_this K_throughout K_timeprecision K_timeunit K_type %token K_typedef K_union K_unique K_var K_virtual K_void K_wait_order %token K_wildcard K_with K_within /* Fake tokens that are passed once we have an initial token. */ %token K_timeprecision_check K_timeunit_check /* The new tokens from 1800-2009. */ %token K_accept_on K_checker K_endchecker K_eventually K_global K_implies %token K_let K_nexttime K_reject_on K_restrict K_s_always K_s_eventually %token K_s_nexttime K_s_until K_s_until_with K_strong K_sync_accept_on %token K_sync_reject_on K_unique0 K_until K_until_with K_untyped K_weak /* The new tokens from 1800-2012. */ %token K_implements K_interconnect K_nettype K_soft /* The new tokens for Verilog-AMS 2.3. */ %token K_above K_abs K_absdelay K_abstol K_access K_acos K_acosh /* 1800-2005 has defined "assert" above! */ %token K_ac_stim K_aliasparam K_analog K_analysis K_asin K_asinh %token K_atan K_atan2 K_atanh K_branch K_ceil K_connect K_connectmodule %token K_connectrules K_continuous K_cos K_cosh K_ddt K_ddt_nature K_ddx %token K_discipline K_discrete K_domain K_driver_update K_endconnectrules %token K_enddiscipline K_endnature K_endparamset K_exclude K_exp %token K_final_step K_flicker_noise K_floor K_flow K_from K_ground %token K_hypot K_idt K_idtmod K_idt_nature K_inf K_initial_step %token K_laplace_nd K_laplace_np K_laplace_zd K_laplace_zp %token K_last_crossing K_limexp K_ln K_log K_max K_merged K_min K_nature %token K_net_resolution K_noise_table K_paramset K_potential K_pow /* 1800-2005 has defined "string" above! */ %token K_resolveto K_sin K_sinh K_slew K_split K_sqrt K_tan K_tanh %token K_timer K_transition K_units K_white_noise K_wreal %token K_zi_nd K_zi_np K_zi_zd K_zi_zp %type from_exclude block_item_decls_opt %type number pos_neg_number %type signing unsigned_signed_opt signed_unsigned_opt %type import_export %type K_automatic_opt K_packed_opt K_reg_opt K_static_opt K_virtual_opt %type udp_reg_opt edge_operator %type drive_strength drive_strength_opt dr_strength0 dr_strength1 %type udp_input_sym udp_output_sym %type udp_input_list udp_sequ_entry udp_comb_entry %type udp_input_declaration_list %type udp_entry_list udp_comb_entry_list udp_sequ_entry_list %type udp_body %type udp_port_list %type udp_port_decl udp_port_decls %type udp_initial udp_init_opt %type udp_initial_expr_opt %type register_variable net_variable event_variable endlabel_opt class_declaration_endlabel_opt %type register_variable_list net_variable_list event_variable_list %type list_of_identifiers loop_variables %type list_of_port_identifiers %type net_decl_assign net_decl_assigns %type port port_opt port_reference port_reference_list %type port_declaration %type list_of_ports module_port_list_opt list_of_port_declarations module_attribute_foreign %type parameter_value_range parameter_value_ranges %type parameter_value_ranges_opt %type tf_port_item_expr_opt value_range_expression %type enum_name_list enum_name %type enum_data_type %type function_item function_item_list function_item_list_opt %type task_item task_item_list task_item_list_opt %type tf_port_declaration tf_port_item tf_port_list tf_port_list_opt %type modport_simple_port port_name parameter_value_byname %type port_name_list parameter_value_byname_list %type attribute %type attribute_list attribute_instance_list attribute_list_opt %type case_item %type case_items %type gate_instance %type gate_instance_list %type hierarchy_identifier implicit_class_handle %type assignment_pattern expression expr_primary expr_mintypmax %type class_new dynamic_array_new %type inc_or_dec_expression inside_expression lpvalue %type branch_probe_expression streaming_concatenation %type delay_value delay_value_simple %type delay1 delay3 delay3_opt delay_value_list %type expression_list_with_nuls expression_list_proper %type cont_assign cont_assign_list %type variable_decl_assignment %type list_of_variable_decl_assignments %type data_type data_type_or_implicit data_type_or_implicit_or_void %type class_identifier %type struct_union_member %type struct_union_member_list %type struct_data_type %type class_declaration_extends_opt %type class_item_qualifier property_qualifier %type class_item_qualifier_list property_qualifier_list %type class_item_qualifier_opt property_qualifier_opt %type random_qualifier %type variable_dimension %type dimensions_opt dimensions %type net_type var_type net_type_opt %type gatetype switchtype %type port_direction port_direction_opt %type bit_logic bit_logic_opt %type integer_vector_type %type parameter_value_opt %type event_expression_list %type event_expression %type event_control %type statement statement_item statement_or_null %type compressed_statement %type loop_statement for_step jump_statement %type statement_or_null_list statement_or_null_list_opt %type analog_statement %type join_keyword %type spec_polarity %type specify_path_identifiers %type specify_simple_path specify_simple_path_decl %type specify_edge_path specify_edge_path_decl %type non_integer_type %type atom2_type %type module_start module_end %token K_TAND %right K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ %right K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ %right '?' ':' K_inside %left K_LOR %left K_LAND %left '|' %left '^' K_NXOR K_NOR %left '&' K_NAND %left K_EQ K_NE K_CEQ K_CNE %left K_GE K_LE '<' '>' %left K_LS K_RS K_RSS %left '+' '-' %left '*' '/' '%' %left K_POW %left UNARY_PREC /* to resolve dangling else ambiguity. */ %nonassoc less_than_K_else %nonassoc K_else /* to resolve exclude (... ambiguity */ %nonassoc '(' %nonassoc K_exclude %% /* IEEE1800-2005: A.1.2 */ /* source_text ::= [ timeunits_declaration ] { description } */ source_text : description_list | ; assertion_item /* IEEE1800-2012: A.6.10 */ : concurrent_assertion_item ; assignment_pattern /* IEEE1800-2005: A.6.7.1 */ : K_LP expression_list_proper '}' { PEAssignPattern*tmp = new PEAssignPattern(*$2); FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | K_LP '}' { PEAssignPattern*tmp = new PEAssignPattern; FILE_NAME(tmp, @1); $$ = tmp; } ; /* Some rules have a ... [ block_identifier ':' ] ... part. This implements it in a LALR way. */ block_identifier_opt /* */ : IDENTIFIER ':' | ; class_declaration /* IEEE1800-2005: A.1.2 */ : K_virtual_opt K_class class_identifier class_declaration_extends_opt ';' { pform_start_class_declaration(@2, $3, $4.type, $4.exprs); } class_items_opt K_endclass { // Process a class. pform_end_class_declaration(@8); } class_declaration_endlabel_opt { // Wrap up the class. if ($10 && $3 && $3->name != $10) { yyerror(@10, "error: Class end label doesn't match class name."); delete[]$10; } } ; class_constraint /* IEEE1800-2005: A.1.8 */ : constraint_prototype | constraint_declaration ; class_identifier : IDENTIFIER { // Create a synthetic typedef for the class name so that the // lexor detects the name as a type. perm_string name = lex_strings.make($1); class_type_t*tmp = new class_type_t(name); FILE_NAME(tmp, @1); pform_set_typedef(name, tmp, NULL); delete[]$1; $$ = tmp; } | TYPE_IDENTIFIER { class_type_t*tmp = dynamic_cast($1.type); if (tmp == 0) { yyerror(@1, "Type name \"%s\"is not a predeclared class name.", $1.text); } delete[]$1.text; $$ = tmp; } ; /* The endlabel after a class declaration is a little tricky because the class name is detected by the lexor as a TYPE_IDENTIFIER if it does indeed match a name. */ class_declaration_endlabel_opt : ':' TYPE_IDENTIFIER { class_type_t*tmp = dynamic_cast ($2.type); if (tmp == 0) { yyerror(@2, "error: class declaration endlabel \"%s\" is not a class name\n", $2.text); $$ = 0; } else { $$ = strdupnew(tmp->name.str()); } delete[]$2.text; } | ':' IDENTIFIER { $$ = $2; } | { $$ = 0; } ; /* This rule implements [ extends class_type ] in the class_declaration. It is not a rule of its own in the LRM. Note that for this to be correct, the identifier after the extends keyword must be a class name. Therefore, match TYPE_IDENTIFIER instead of IDENTIFIER, and this rule will return a data_type. */ class_declaration_extends_opt /* IEEE1800-2005: A.1.2 */ : K_extends TYPE_IDENTIFIER { $$.type = $2.type; $$.exprs= 0; delete[]$2.text; } | K_extends TYPE_IDENTIFIER '(' expression_list_with_nuls ')' { $$.type = $2.type; $$.exprs = $4; delete[]$2.text; } | { $$.type = 0; $$.exprs = 0; } ; /* The class_items_opt and class_items rules together implement the rule snippet { class_item } (zero or more class_item) of the class_declaration. */ class_items_opt /* IEEE1800-2005: A.1.2 */ : class_items | ; class_items /* IEEE1800-2005: A.1.2 */ : class_items class_item | class_item ; class_item /* IEEE1800-2005: A.1.8 */ /* IEEE1800 A.1.8: class_constructor_declaration */ : method_qualifier_opt K_function K_new { assert(current_function==0); current_function = pform_push_constructor_scope(@3); } '(' tf_port_list_opt ')' ';' function_item_list_opt statement_or_null_list_opt K_endfunction endnew_opt { current_function->set_ports($6); pform_set_constructor_return(current_function); pform_set_this_class(@3, current_function); current_function_set_statement(@3, $10); pform_pop_scope(); current_function = 0; } /* Class properties... */ | property_qualifier_opt data_type list_of_variable_decl_assignments ';' { pform_class_property(@2, $1, $2, $3); } | K_const class_item_qualifier_opt data_type list_of_variable_decl_assignments ';' { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3, $4); } /* Class methods... */ | method_qualifier_opt task_declaration { /* The task_declaration rule puts this into the class */ } | method_qualifier_opt function_declaration { /* The function_declaration rule puts this into the class */ } /* External class method definitions... */ | K_extern method_qualifier_opt K_function K_new ';' { yyerror(@1, "sorry: External constructors are not yet supported."); } | K_extern method_qualifier_opt K_function K_new '(' tf_port_list_opt ')' ';' { yyerror(@1, "sorry: External constructors are not yet supported."); } | K_extern method_qualifier_opt K_function data_type_or_implicit_or_void IDENTIFIER ';' { yyerror(@1, "sorry: External methods are not yet supported."); delete[] $5; } | K_extern method_qualifier_opt K_function data_type_or_implicit_or_void IDENTIFIER '(' tf_port_list_opt ')' ';' { yyerror(@1, "sorry: External methods are not yet supported."); delete[] $5; } | K_extern method_qualifier_opt K_task IDENTIFIER ';' { yyerror(@1, "sorry: External methods are not yet supported."); delete[] $4; } | K_extern method_qualifier_opt K_task IDENTIFIER '(' tf_port_list_opt ')' ';' { yyerror(@1, "sorry: External methods are not yet supported."); delete[] $4; } /* Class constraints... */ | class_constraint /* Here are some error matching rules to help recover from various syntax errors within a class declaration. */ | property_qualifier_opt data_type error ';' { yyerror(@3, "error: Errors in variable names after data type."); yyerrok; } | property_qualifier_opt IDENTIFIER error ';' { yyerror(@3, "error: %s doesn't name a type.", $2); yyerrok; } | method_qualifier_opt K_function K_new error K_endfunction endnew_opt { yyerror(@1, "error: I give up on this class constructor declaration."); yyerrok; } | error ';' { yyerror(@2, "error: invalid class item."); yyerrok; } ; class_item_qualifier /* IEEE1800-2005 A.1.8 */ : K_static { $$ = property_qualifier_t::make_static(); } | K_protected { $$ = property_qualifier_t::make_protected(); } | K_local { $$ = property_qualifier_t::make_local(); } ; class_item_qualifier_list : class_item_qualifier_list class_item_qualifier { $$ = $1 | $2; } | class_item_qualifier { $$ = $1; } ; class_item_qualifier_opt : class_item_qualifier_list { $$ = $1; } | { $$ = property_qualifier_t::make_none(); } ; class_new /* IEEE1800-2005 A.2.4 */ : K_new '(' expression_list_with_nuls ')' { list*expr_list = $3; strip_tail_items(expr_list); PENewClass*tmp = new PENewClass(*expr_list); FILE_NAME(tmp, @1); delete $3; $$ = tmp; } | K_new hierarchy_identifier { PEIdent*tmpi = new PEIdent(*$2); FILE_NAME(tmpi, @2); PENewCopy*tmp = new PENewCopy(tmpi); FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | K_new { PENewClass*tmp = new PENewClass; FILE_NAME(tmp, @1); $$ = tmp; } ; /* The concurrent_assertion_item pulls together the concurrent_assertion_statement and checker_instantiation rules. */ concurrent_assertion_item /* IEEE1800-2012 A.2.10 */ : block_identifier_opt K_assert K_property '(' property_spec ')' statement_or_null { /* */ if (gn_assertions_flag) { yyerror(@2, "sorry: concurrent_assertion_item not supported." " Try -gno-assertion to turn this message off."); } } | block_identifier_opt K_assert K_property '(' error ')' statement_or_null { yyerrok; yyerror(@2, "error: Error in property_spec of concurrent assertion item."); } ; constraint_block_item /* IEEE1800-2005 A.1.9 */ : constraint_expression ; constraint_block_item_list : constraint_block_item_list constraint_block_item | constraint_block_item ; constraint_block_item_list_opt : | constraint_block_item_list ; constraint_declaration /* IEEE1800-2005: A.1.9 */ : K_static_opt K_constraint IDENTIFIER '{' constraint_block_item_list_opt '}' { yyerror(@2, "sorry: Constraint declarations not supported."); } /* Error handling rules... */ | K_static_opt K_constraint IDENTIFIER '{' error '}' { yyerror(@4, "error: Errors in the constraint block item list."); } ; constraint_expression /* IEEE1800-2005 A.1.9 */ : expression ';' | expression K_dist '{' '}' ';' | expression K_TRIGGER constraint_set | K_if '(' expression ')' constraint_set %prec less_than_K_else | K_if '(' expression ')' constraint_set K_else constraint_set | K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' constraint_set ; constraint_expression_list /* */ : constraint_expression_list constraint_expression | constraint_expression ; constraint_prototype /* IEEE1800-2005: A.1.9 */ : K_static_opt K_constraint IDENTIFIER ';' { yyerror(@2, "sorry: Constraint prototypes not supported."); } ; constraint_set /* IEEE1800-2005 A.1.9 */ : constraint_expression | '{' constraint_expression_list '}' ; data_declaration /* IEEE1800-2005: A.2.1.3 */ : attribute_list_opt data_type_or_implicit list_of_variable_decl_assignments ';' { data_type_t*data_type = $2; if (data_type == 0) { data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); FILE_NAME(data_type, @2); } pform_makewire(@2, 0, str_strength, $3, NetNet::IMPLICIT_REG, data_type); } ; data_type /* IEEE1800-2005: A.2.2.1 */ : integer_vector_type unsigned_signed_opt dimensions_opt { ivl_variable_type_t use_vtype = $1; bool reg_flag = false; if (use_vtype == IVL_VT_NO_TYPE) { use_vtype = IVL_VT_LOGIC; reg_flag = true; } vector_type_t*tmp = new vector_type_t(use_vtype, $2, $3); tmp->reg_flag = reg_flag; FILE_NAME(tmp, @1); $$ = tmp; } | non_integer_type { real_type_t*tmp = new real_type_t($1); FILE_NAME(tmp, @1); $$ = tmp; } | struct_data_type { if (!$1->packed_flag) { yyerror(@1, "sorry: Unpacked structs not supported."); } $$ = $1; } | enum_data_type { $$ = $1; } | atom2_type signed_unsigned_opt { atom2_type_t*tmp = new atom2_type_t($1, $2); FILE_NAME(tmp, @1); $$ = tmp; } | K_integer signed_unsigned_opt { list*pd = make_range_from_width(integer_width); vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $2, pd); tmp->reg_flag = true; tmp->integer_flag = true; $$ = tmp; } | K_time { list*pd = make_range_from_width(64); vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, false, pd); tmp->reg_flag = !gn_system_verilog(); $$ = tmp; } | TYPE_IDENTIFIER dimensions_opt { if ($2) { parray_type_t*tmp = new parray_type_t($1.type, $2); FILE_NAME(tmp, @1); $$ = tmp; } else $$ = $1.type; delete[]$1.text; } | PACKAGE_IDENTIFIER K_SCOPE_RES { lex_in_package_scope($1); } TYPE_IDENTIFIER { lex_in_package_scope(0); $$ = $4.type; delete[]$4.text; } | K_string { string_type_t*tmp = new string_type_t; FILE_NAME(tmp, @1); $$ = tmp; } ; /* The data_type_or_implicit rule is a little more complex then the rule documented in the IEEE format syntax in order to allow for signaling the special case that the data_type is completely absent. The context may need that information to decide to resort to left context. */ data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */ : data_type { $$ = $1; } | signing dimensions_opt { vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $1, $2); tmp->implicit_flag = true; FILE_NAME(tmp, @1); $$ = tmp; } | dimensions { vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, false, $1); tmp->implicit_flag = true; FILE_NAME(tmp, @1); $$ = tmp; } | { $$ = 0; } ; data_type_or_implicit_or_void : data_type_or_implicit { $$ = $1; } | K_void { void_type_t*tmp = new void_type_t; FILE_NAME(tmp, @1); $$ = tmp; } ; /* NOTE 1: We pull the "timeunits_declaration" into the description here in order to be a little more flexible with where timeunits statements may go. This may be a bad idea, but it is legacy now. */ /* NOTE 2: The "module" rule of the description combines the module_declaration, program_declaration, and interface_declaration rules from the standard description. */ description /* IEEE1800-2005: A.1.2 */ : module | udp_primitive | config_declaration | nature_declaration | package_declaration | discipline_declaration | package_item | KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' { perm_string tmp3 = lex_strings.make($3); pform_set_type_attrib(tmp3, $5, $7); delete[] $3; delete[] $5; } ; description_list : description | description_list description ; /* This implements the [ : IDENTIFIER ] part of the constructor rule documented in IEEE1800-2005: A.1.8 */ endnew_opt : ':' K_new | ; /* The dynamic_array_new rule is kinda like an expression, but it is treated differently by rules that use this "expression". Watch out! */ dynamic_array_new /* IEEE1800-2005: A.2.4 */ : K_new '[' expression ']' { $$ = new PENewArray($3, 0); FILE_NAME($$, @1); } | K_new '[' expression ']' '(' expression ')' { $$ = new PENewArray($3, $6); FILE_NAME($$, @1); } ; for_step /* IEEE1800-2005: A.6.8 */ : lpvalue '=' expression { PAssign*tmp = new PAssign($1,$3); FILE_NAME(tmp, @1); $$ = tmp; } | inc_or_dec_expression { $$ = pform_compressed_assign_from_inc_dec(@1, $1); } | compressed_statement { $$ = $1; } ; /* The function declaration rule matches the function declaration header, then pushes the function scope. This causes the definitions in the func_body to take on the scope of the function instead of the module. */ function_declaration /* IEEE1800-2005: A.2.6 */ : K_function K_automatic_opt data_type_or_implicit_or_void IDENTIFIER ';' { assert(current_function == 0); current_function = pform_push_function_scope(@1, $4, $2); } function_item_list statement_or_null_list_opt K_endfunction { current_function->set_ports($7); current_function->set_return($3); current_function_set_statement($8? @8 : @4, $8); pform_set_this_class(@4, current_function); pform_pop_scope(); current_function = 0; } endlabel_opt { // Last step: check any closing name. if ($11) { if (strcmp($4,$11) != 0) { yyerror(@11, "error: End label doesn't match " "function name"); } if (! gn_system_verilog()) { yyerror(@11, "error: Function end label require " "SystemVerilog."); } delete[]$11; } delete[]$4; } | K_function K_automatic_opt data_type_or_implicit_or_void IDENTIFIER { assert(current_function == 0); current_function = pform_push_function_scope(@1, $4, $2); } '(' tf_port_list_opt ')' ';' block_item_decls_opt statement_or_null_list_opt K_endfunction { current_function->set_ports($7); current_function->set_return($3); current_function_set_statement($11? @11 : @4, $11); pform_set_this_class(@4, current_function); pform_pop_scope(); current_function = 0; if ($7==0 && !gn_system_verilog()) { yyerror(@4, "error: Empty parenthesis syntax requires SystemVerilog."); } } endlabel_opt { // Last step: check any closing name. if ($14) { if (strcmp($4,$14) != 0) { yyerror(@14, "error: End label doesn't match " "function name"); } if (! gn_system_verilog()) { yyerror(@14, "error: Function end labels require " "SystemVerilog."); } delete[]$14; } delete[]$4; } /* Detect and recover from some errors. */ | K_function K_automatic_opt data_type_or_implicit_or_void IDENTIFIER error K_endfunction { /* */ if (current_function) { pform_pop_scope(); current_function = 0; } assert(current_function == 0); yyerror(@1, "error: Syntax error defining function."); yyerrok; } endlabel_opt { // Last step: check any closing name. if ($8) { if (strcmp($4,$8) != 0) { yyerror(@8, "error: End label doesn't match function name"); } if (! gn_system_verilog()) { yyerror(@8, "error: Function end labels require " "SystemVerilog."); } delete[]$8; } delete[]$4; } ; import_export /* IEEE1800-2012: A.2.9 */ : K_import { $$ = true; } | K_export { $$ = false; } ; implicit_class_handle /* IEEE1800-2005: A.8.4 */ : K_this { $$ = pform_create_this(); } | K_super { $$ = pform_create_super(); } ; /* SystemVerilog adds support for the increment/decrement expressions, which look like a++, --a, etc. These are primaries but are in their own rules because they can also be statements. Note that the operator can only take l-value expressions. */ inc_or_dec_expression /* IEEE1800-2005: A.4.3 */ : K_INCR lpvalue %prec UNARY_PREC { PEUnary*tmp = new PEUnary('I', $2); FILE_NAME(tmp, @2); $$ = tmp; } | lpvalue K_INCR %prec UNARY_PREC { PEUnary*tmp = new PEUnary('i', $1); FILE_NAME(tmp, @1); $$ = tmp; } | K_DECR lpvalue %prec UNARY_PREC { PEUnary*tmp = new PEUnary('D', $2); FILE_NAME(tmp, @2); $$ = tmp; } | lpvalue K_DECR %prec UNARY_PREC { PEUnary*tmp = new PEUnary('d', $1); FILE_NAME(tmp, @1); $$ = tmp; } ; inside_expression /* IEEE1800-2005 A.8.3 */ : expression K_inside '{' open_range_list '}' { yyerror(@2, "sorry: \"inside\" expressions not supported yet."); $$ = 0; } ; integer_vector_type /* IEEE1800-2005: A.2.2.1 */ : K_reg { $$ = IVL_VT_NO_TYPE; } /* Usually a synonym for logic. */ | K_bit { $$ = IVL_VT_BOOL; } | K_logic { $$ = IVL_VT_LOGIC; } | K_bool { $$ = IVL_VT_BOOL; } /* Icarus Verilog xtypes extension */ ; join_keyword /* IEEE1800-2005: A.6.3 */ : K_join { $$ = PBlock::BL_PAR; } | K_join_none { $$ = PBlock::BL_JOIN_NONE; } | K_join_any { $$ = PBlock::BL_JOIN_ANY; } ; jump_statement /* IEEE1800-2005: A.6.5 */ : K_break ';' { yyerror(@1, "sorry: break statements not supported."); $$ = 0; } | K_return ';' { PReturn*tmp = new PReturn(0); FILE_NAME(tmp, @1); $$ = tmp; } | K_return expression ';' { PReturn*tmp = new PReturn($2); FILE_NAME(tmp, @1); $$ = tmp; } ; /* Loop statements are kinds of statements. */ loop_statement /* IEEE1800-2005: A.6.8 */ : K_for '(' lpvalue '=' expression ';' expression ';' for_step ')' statement_or_null { PForStatement*tmp = new PForStatement($3, $5, $7, $9, $11); FILE_NAME(tmp, @1); $$ = tmp; } // Handle for_variable_declaration syntax by wrapping the for(...) // statement in a synthetic named block. We can name the block // after the variable that we are creating, that identifier is // safe in the controlling scope. | K_for '(' data_type IDENTIFIER '=' expression ';' expression ';' for_step ')' { static unsigned for_counter = 0; char for_block_name [64]; snprintf(for_block_name, sizeof for_block_name, "$ivl_for_loop%u", for_counter); for_counter += 1; PBlock*tmp = pform_push_block_scope(for_block_name, PBlock::BL_SEQ); FILE_NAME(tmp, @1); current_block_stack.push(tmp); listassign_list; decl_assignment_t*tmp_assign = new decl_assignment_t; tmp_assign->name = lex_strings.make($4); assign_list.push_back(tmp_assign); pform_makewire(@4, 0, str_strength, &assign_list, NetNet::REG, $3); } statement_or_null { pform_name_t tmp_hident; tmp_hident.push_back(name_component_t(lex_strings.make($4))); PEIdent*tmp_ident = pform_new_ident(tmp_hident); FILE_NAME(tmp_ident, @4); PForStatement*tmp_for = new PForStatement(tmp_ident, $6, $8, $10, $13); FILE_NAME(tmp_for, @1); pform_pop_scope(); vectortmp_for_list (1); tmp_for_list[0] = tmp_for; PBlock*tmp_blk = current_block_stack.top(); tmp_blk->set_statement(tmp_for_list); $$ = tmp_blk; delete[]$4; } | K_forever statement_or_null { PForever*tmp = new PForever($2); FILE_NAME(tmp, @1); $$ = tmp; } | K_repeat '(' expression ')' statement_or_null { PRepeat*tmp = new PRepeat($3, $5); FILE_NAME(tmp, @1); $$ = tmp; } | K_while '(' expression ')' statement_or_null { PWhile*tmp = new PWhile($3, $5); FILE_NAME(tmp, @1); $$ = tmp; } | K_do statement_or_null K_while '(' expression ')' ';' { PDoWhile*tmp = new PDoWhile($5, $2); FILE_NAME(tmp, @1); $$ = tmp; } // When matching a foreach loop, implicitly create a named block // to hold the definitions for the index variables. | K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' { static unsigned foreach_counter = 0; char for_block_name[64]; snprintf(for_block_name, sizeof for_block_name, "$ivl_foreach%u", foreach_counter); foreach_counter += 1; PBlock*tmp = pform_push_block_scope(for_block_name, PBlock::BL_SEQ); FILE_NAME(tmp, @1); current_block_stack.push(tmp); pform_make_foreach_declarations(@1, $5); } statement_or_null { PForeach*tmp_for = pform_make_foreach(@1, $3, $5, $9); pform_pop_scope(); vectortmp_for_list(1); tmp_for_list[0] = tmp_for; PBlock*tmp_blk = current_block_stack.top(); tmp_blk->set_statement(tmp_for_list); $$ = tmp_blk; } /* Error forms for loop statements. */ | K_for '(' lpvalue '=' expression ';' expression ';' error ')' statement_or_null { $$ = 0; yyerror(@1, "error: Error in for loop step assignment."); } | K_for '(' lpvalue '=' expression ';' error ';' for_step ')' statement_or_null { $$ = 0; yyerror(@1, "error: Error in for loop condition expression."); } | K_for '(' error ')' statement_or_null { $$ = 0; yyerror(@1, "error: Incomprehensible for loop."); } | K_while '(' error ')' statement_or_null { $$ = 0; yyerror(@1, "error: Error in while loop condition."); } | K_do statement_or_null K_while '(' error ')' ';' { $$ = 0; yyerror(@1, "error: Error in do/while loop condition."); } | K_foreach '(' IDENTIFIER '[' error ']' ')' statement_or_null { $$ = 0; yyerror(@4, "error: Errors in foreach loop variables list."); } ; /* TODO: Replace register_variable_list with list_of_variable_decl_assignments. */ list_of_variable_decl_assignments /* IEEE1800-2005 A.2.3 */ : variable_decl_assignment { list*tmp = new list; tmp->push_back($1); $$ = tmp; } | list_of_variable_decl_assignments ',' variable_decl_assignment { list*tmp = $1; tmp->push_back($3); $$ = tmp; } ; variable_decl_assignment /* IEEE1800-2005 A.2.3 */ : IDENTIFIER dimensions_opt { decl_assignment_t*tmp = new decl_assignment_t; tmp->name = lex_strings.make($1); if ($2) { tmp->index = *$2; delete $2; } delete[]$1; $$ = tmp; } | IDENTIFIER '=' expression { decl_assignment_t*tmp = new decl_assignment_t; tmp->name = lex_strings.make($1); tmp->expr .reset($3); delete[]$1; $$ = tmp; } | IDENTIFIER '=' K_new '(' ')' { decl_assignment_t*tmp = new decl_assignment_t; tmp->name = lex_strings.make($1); PENewClass*expr = new PENewClass; FILE_NAME(expr, @3); tmp->expr .reset(expr); delete[]$1; $$ = tmp; } ; loop_variables /* IEEE1800-2005: A.6.8 */ : loop_variables ',' IDENTIFIER { list*tmp = $1; tmp->push_back(lex_strings.make($3)); delete[]$3; $$ = tmp; } | IDENTIFIER { list*tmp = new list; tmp->push_back(lex_strings.make($1)); delete[]$1; $$ = tmp; } ; method_qualifier /* IEEE1800-2005: A.1.8 */ : K_virtual | class_item_qualifier ; method_qualifier_opt : method_qualifier | ; modport_declaration /* IEEE1800-2012: A.2.9 */ : K_modport { if (!pform_in_interface()) yyerror(@1, "error: modport declarations are only allowed " "in interfaces."); } modport_item_list ';' modport_item_list : modport_item | modport_item_list ',' modport_item ; modport_item : IDENTIFIER { pform_start_modport_item(@1, $1); } '(' modport_ports_list ')' { pform_end_modport_item(@1); } ; /* The modport_ports_list is a LALR(2) grammar. When the parser sees a ',' it needs to look ahead to the next token to decide whether it is a continuation of the preceding modport_ports_declaration, or the start of a new modport_ports_declaration. bison only supports LALR(1), so we have to handcraft a mini parser for this part of the syntax. last_modport_port holds the state for this mini parser.*/ modport_ports_list : modport_ports_declaration | modport_ports_list ',' modport_ports_declaration | modport_ports_list ',' modport_simple_port { if (last_modport_port.type == MP_SIMPLE) { pform_add_modport_port(@3, last_modport_port.direction, $3->name, $3->parm); } else { yyerror(@3, "error: modport expression not allowed here."); } delete $3; } | modport_ports_list ',' modport_tf_port { if (last_modport_port.type != MP_TF) yyerror(@3, "error: task/function declaration not allowed here."); } | modport_ports_list ',' IDENTIFIER { if (last_modport_port.type == MP_SIMPLE) { pform_add_modport_port(@3, last_modport_port.direction, lex_strings.make($3), 0); } else if (last_modport_port.type != MP_TF) { yyerror(@3, "error: list of identifiers not allowed here."); } delete[] $3; } | modport_ports_list ',' { yyerror(@2, "error: NULL port declarations are not allowed"); } ; modport_ports_declaration : attribute_list_opt port_direction IDENTIFIER { last_modport_port.type = MP_SIMPLE; last_modport_port.direction = $2; pform_add_modport_port(@3, $2, lex_strings.make($3), 0); delete[] $3; delete $1; } | attribute_list_opt port_direction modport_simple_port { last_modport_port.type = MP_SIMPLE; last_modport_port.direction = $2; pform_add_modport_port(@3, $2, $3->name, $3->parm); delete $3; delete $1; } | attribute_list_opt import_export IDENTIFIER { last_modport_port.type = MP_TF; last_modport_port.is_import = $2; yyerror(@3, "sorry: modport task/function ports are not yet supported."); delete[] $3; delete $1; } | attribute_list_opt import_export modport_tf_port { last_modport_port.type = MP_TF; last_modport_port.is_import = $2; yyerror(@3, "sorry: modport task/function ports are not yet supported."); delete $1; } | attribute_list_opt K_clocking IDENTIFIER { last_modport_port.type = MP_CLOCKING; last_modport_port.direction = NetNet::NOT_A_PORT; yyerror(@3, "sorry: modport clocking declaration is not yet supported."); delete[] $3; delete $1; } ; modport_simple_port : '.' IDENTIFIER '(' expression ')' { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = $4; delete[]$2; $$ = tmp; } ; modport_tf_port : K_task IDENTIFIER | K_task IDENTIFIER '(' tf_port_list_opt ')' | K_function data_type_or_implicit_or_void IDENTIFIER | K_function data_type_or_implicit_or_void IDENTIFIER '(' tf_port_list_opt ')' ; non_integer_type /* IEEE1800-2005: A.2.2.1 */ : K_real { $$ = real_type_t::REAL; } | K_realtime { $$ = real_type_t::REAL; } | K_shortreal { $$ = real_type_t::SHORTREAL; } ; number : BASED_NUMBER { $$ = $1; based_size = 0;} | DEC_NUMBER { $$ = $1; based_size = 0;} | DEC_NUMBER BASED_NUMBER { $$ = pform_verinum_with_size($1,$2, @2.text, @2.first_line); based_size = 0; } | UNBASED_NUMBER { $$ = $1; based_size = 0;} | DEC_NUMBER UNBASED_NUMBER { yyerror(@1, "error: Unbased SystemVerilog literal cannot have " "a size."); $$ = $1; based_size = 0;} ; open_range_list /* IEEE1800-2005 A.2.11 */ : open_range_list ',' value_range | value_range ; package_declaration /* IEEE1800-2005 A.1.2 */ : K_package IDENTIFIER ';' { pform_start_package_declaration(@1, $2); } package_item_list_opt K_endpackage endlabel_opt { pform_end_package_declaration(@1); // If an end label is present make sure it match the package name. if ($7) { if (strcmp($2,$7) != 0) { yyerror(@7, "error: End label doesn't match package name"); } delete[]$7; } delete[]$2; } ; module_package_import_list_opt : | package_import_list ; package_import_list : package_import_declaration | package_import_list package_import_declaration ; package_import_declaration /* IEEE1800-2005 A.2.1.3 */ : K_import package_import_item_list ';' { } ; package_import_item : PACKAGE_IDENTIFIER K_SCOPE_RES IDENTIFIER { pform_package_import(@2, $1, $3); delete[]$3; } | PACKAGE_IDENTIFIER K_SCOPE_RES '*' { pform_package_import(@2, $1, 0); } ; package_import_item_list : package_import_item_list',' package_import_item | package_import_item ; package_item /* IEEE1800-2005 A.1.10 */ : timeunits_declaration | K_parameter param_type parameter_assign_list ';' | K_localparam param_type localparam_assign_list ';' | type_declaration | function_declaration | task_declaration | data_declaration | class_declaration ; package_item_list : package_item_list package_item | package_item ; package_item_list_opt : package_item_list | ; port_direction /* IEEE1800-2005 A.1.3 */ : K_input { $$ = NetNet::PINPUT; } | K_output { $$ = NetNet::POUTPUT; } | K_inout { $$ = NetNet::PINOUT; } | K_ref { $$ = NetNet::PREF; if (!gn_system_verilog()) { yyerror(@1, "error: Reference ports (ref) require SystemVerilog."); $$ = NetNet::PINPUT; } } ; /* port_direction_opt is used in places where the port direction is optional. The default direction is selected by the context, which needs to notice the PIMPLICIT direction. */ port_direction_opt : port_direction { $$ = $1; } | { $$ = NetNet::PIMPLICIT; } ; property_expr /* IEEE1800-2012 A.2.10 */ : expression ; /* The property_qualifier rule is as literally described in the LRM, but the use is usually as { property_qualifier }, which is implemented by the property_qualifier_opt rule below. */ property_qualifier /* IEEE1800-2005 A.1.8 */ : class_item_qualifier | random_qualifier ; property_qualifier_opt /* IEEE1800-2005 A.1.8: ... { property_qualifier } */ : property_qualifier_list { $$ = $1; } | { $$ = property_qualifier_t::make_none(); } ; property_qualifier_list /* IEEE1800-2005 A.1.8 */ : property_qualifier_list property_qualifier { $$ = $1 | $2; } | property_qualifier { $$ = $1; } ; /* The property_spec rule uses some helper rules to implement this rule from the LRM: [ clocking_event ] [ disable iff ( expression_or_dist ) ] property_expr This does it is a YACC friendly way. */ property_spec /* IEEE1800-2012 A.2.10 */ : clocking_event_opt property_spec_disable_iff_opt property_expr ; property_spec_disable_iff_opt /* */ : K_disable K_iff '(' expression ')' | ; random_qualifier /* IEEE1800-2005 A.1.8 */ : K_rand { $$ = property_qualifier_t::make_rand(); } | K_randc { $$ = property_qualifier_t::make_randc(); } ; /* real and realtime are exactly the same so save some code * with a common matching rule. */ real_or_realtime : K_real | K_realtime ; signing /* IEEE1800-2005: A.2.2.1 */ : K_signed { $$ = true; } | K_unsigned { $$ = false; } ; statement /* IEEE1800-2005: A.6.4 */ : attribute_list_opt statement_item { pform_bind_attributes($2->attributes, $1); $$ = $2; } ; /* Many places where statements are allowed can actually take a statement or a null statement marked with a naked semi-colon. */ statement_or_null /* IEEE1800-2005: A.6.4 */ : statement { $$ = $1; } | attribute_list_opt ';' { $$ = 0; } ; stream_expression : expression ; stream_expression_list : stream_expression_list ',' stream_expression | stream_expression ; stream_operator : K_LS | K_RS ; streaming_concatenation /* IEEE1800-2005: A.8.1 */ : '{' stream_operator '{' stream_expression_list '}' '}' { /* streaming concatenation is a SystemVerilog thing. */ if (gn_system_verilog()) { yyerror(@2, "sorry: Streaming concatenation not supported."); $$ = 0; } else { yyerror(@2, "error: Streaming concatenation requires SystemVerilog"); $$ = 0; } } ; /* The task declaration rule matches the task declaration header, then pushes the function scope. This causes the definitions in the task_body to take on the scope of the task instead of the module. */ task_declaration /* IEEE1800-2005: A.2.7 */ : K_task K_automatic_opt IDENTIFIER ';' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } task_item_list_opt statement_or_null_list_opt K_endtask { current_task->set_ports($6); current_task_set_statement(@3, $7); pform_set_this_class(@3, current_task); pform_pop_scope(); current_task = 0; if ($7 && $7->size() > 1 && !gn_system_verilog()) { yyerror(@7, "error: Task body with multiple statements requres SystemVerilog."); } delete $7; } endlabel_opt { // Last step: check any closing name. This is done late so // that the parser can look ahead to detect the present // endlabel_opt but still have the pform_endmodule() called // early enough that the lexor can know we are outside the // module. if ($10) { if (strcmp($3,$10) != 0) { yyerror(@10, "error: End label doesn't match task name"); } if (! gn_system_verilog()) { yyerror(@10, "error: Task end labels require " "SystemVerilog."); } delete[]$10; } delete[]$3; } | K_task K_automatic_opt IDENTIFIER '(' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } tf_port_list ')' ';' block_item_decls_opt statement_or_null_list_opt K_endtask { current_task->set_ports($6); current_task_set_statement(@3, $10); pform_set_this_class(@3, current_task); pform_pop_scope(); current_task = 0; if ($10) delete $10; } endlabel_opt { // Last step: check any closing name. This is done late so // that the parser can look ahead to detect the present // endlabel_opt but still have the pform_endmodule() called // early enough that the lexor can know we are outside the // module. if ($13) { if (strcmp($3,$13) != 0) { yyerror(@13, "error: End label doesn't match task name"); } if (! gn_system_verilog()) { yyerror(@13, "error: Task end labels require " "SystemVerilog."); } delete[]$13; } delete[]$3; } | K_task K_automatic_opt IDENTIFIER '(' ')' ';' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } block_item_decls_opt statement_or_null_list K_endtask { current_task->set_ports(0); current_task_set_statement(@3, $9); pform_set_this_class(@3, current_task); if (! current_task->method_of()) { cerr << @3 << ": warning: task definition for \"" << $3 << "\" has an empty port declaration list!" << endl; } pform_pop_scope(); current_task = 0; if ($9->size() > 1 && !gn_system_verilog()) { yyerror(@9, "error: Task body with multiple statements requres SystemVerilog."); } delete $9; } endlabel_opt { // Last step: check any closing name. This is done late so // that the parser can look ahead to detect the present // endlabel_opt but still have the pform_endmodule() called // early enough that the lexor can know we are outside the // module. if ($12) { if (strcmp($3,$12) != 0) { yyerror(@12, "error: End label doesn't match task name"); } if (! gn_system_verilog()) { yyerror(@12, "error: Task end labels require " "SystemVerilog."); } delete[]$12; } delete[]$3; } | K_task K_automatic_opt IDENTIFIER error K_endtask { assert(current_task == 0); } endlabel_opt { // Last step: check any closing name. This is done late so // that the parser can look ahead to detect the present // endlabel_opt but still have the pform_endmodule() called // early enough that the lexor can know we are outside the // module. if ($7) { if (strcmp($3,$7) != 0) { yyerror(@7, "error: End label doesn't match task name"); } if (! gn_system_verilog()) { yyerror(@7, "error: Task end labels require " "SystemVerilog."); } delete[]$7; } delete[]$3; } ; tf_port_declaration /* IEEE1800-2005: A.2.7 */ : port_direction K_reg_opt unsigned_signed_opt dimensions_opt list_of_identifiers ';' { vector*tmp = pform_make_task_ports(@1, $1, $2 ? IVL_VT_LOGIC : IVL_VT_NO_TYPE, $3, $4, $5); $$ = tmp; } /* When the port is an integer, infer a signed vector of the integer shape. Generate a range ([31:0]) to make it work. */ | port_direction K_integer list_of_identifiers ';' { list*range_stub = make_range_from_width(integer_width); vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, true, range_stub, $3, true); $$ = tmp; } /* Ports can be time with a width of [63:0] (unsigned). */ | port_direction K_time list_of_identifiers ';' { list*range_stub = make_range_from_width(64); vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, false, range_stub, $3); $$ = tmp; } /* Ports can be real or realtime. */ | port_direction real_or_realtime list_of_identifiers ';' { vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_REAL, true, 0, $3); $$ = tmp; } ; /* These rules for tf_port_item are slightly expanded from the strict rules in the LRM to help with LALR parsing. NOTE: Some of these rules should be folded into the "data_type" variant which uses the data_type rule to match data type declarations. That some rules do not use the data_type production is a consequence of legacy. */ tf_port_item /* IEEE1800-2005: A.2.7 */ : port_direction_opt data_type_or_implicit IDENTIFIER dimensions_opt tf_port_item_expr_opt { vector*tmp; NetNet::PortType use_port_type = $1==NetNet::PIMPLICIT? NetNet::PINPUT : $1; perm_string name = lex_strings.make($3); list* ilist = list_from_identifier($3); if (($2 == 0) && ($1==NetNet::PIMPLICIT)) { // Detect special case this is an undecorated // identifier and we need to get the declaration from // left context. if ($4 != 0) { yyerror(@4, "internal error: How can there be an unpacked range here?\n"); } tmp = pform_make_task_ports(@3, use_port_type, port_declaration_context.data_type, ilist); } else { // Otherwise, the decorations for this identifier // indicate the type. Save the type for any right // context that may come later. port_declaration_context.port_type = use_port_type; if ($2 == 0) { $2 = new vector_type_t(IVL_VT_LOGIC, false, 0); FILE_NAME($2, @3); } port_declaration_context.data_type = $2; tmp = pform_make_task_ports(@3, use_port_type, $2, ilist); } if ($4 != 0) { pform_set_reg_idx(name, $4); } $$ = tmp; if ($5) { assert(tmp->size()==1); tmp->front().defe = $5; } } /* Rules to match error cases... */ | port_direction_opt data_type_or_implicit IDENTIFIER error { yyerror(@3, "error: Error in task/function port item after port name %s.", $3); yyerrok; $$ = 0; } ; /* This rule matches the [ = ] part of the tf_port_item rules. */ tf_port_item_expr_opt : '=' expression { if (! gn_system_verilog()) { yyerror(@1, "error: Task/function default arguments require " "SystemVerilog."); } $$ = $2; } | { $$ = 0; } ; tf_port_list /* IEEE1800-2005: A.2.7 */ : tf_port_list ',' tf_port_item { vector*tmp; if ($1 && $3) { size_t s1 = $1->size(); tmp = $1; tmp->resize(tmp->size()+$3->size()); for (size_t idx = 0 ; idx < $3->size() ; idx += 1) tmp->at(s1+idx) = $3->at(idx); delete $3; } else if ($1) { tmp = $1; } else { tmp = $3; } $$ = tmp; } | tf_port_item { $$ = $1; } /* Rules to handle some errors in tf_port_list items. */ | error ',' tf_port_item { yyerror(@2, "error: Syntax error in task/function port declaration."); $$ = $3; } | tf_port_list ',' { yyerror(@2, "error: NULL port declarations are not allowed."); $$ = $1; } | tf_port_list ';' { yyerror(@2, "error: ';' is an invalid port declaration separator."); $$ = $1; } ; /* NOTE: Icarus Verilog is a little more generous with the timeunits declarations by allowing them to happen in multiple places in the file. So the rule is adjusted to be invoked by the "description" rule. This theoretically allows files to be concatenated together and still compile. */ timeunits_declaration /* IEEE1800-2005: A.1.2 */ : K_timeunit TIME_LITERAL ';' { pform_set_timeunit($2, false, false); } | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' { pform_set_timeunit($2, false, false); pform_set_timeprecision($4, false, false); } | K_timeprecision TIME_LITERAL ';' { pform_set_timeprecision($2, false, false); } ; value_range /* IEEE1800-2005: A.8.3 */ : expression { } | '[' expression ':' expression ']' { } ; variable_dimension /* IEEE1800-2005: A.2.5 */ : '[' expression ':' expression ']' { list *tmp = new list; pform_range_t index ($2,$4); tmp->push_back(index); $$ = tmp; } | '[' expression ']' { // SystemVerilog canonical range if (generation_flag < GN_VER2005_SV) { warn_count += 1; cerr << @2 << ": warning: Use of SystemVerilog [size] dimension. " << "Use at least -g2005-sv to remove this warning." << endl; } list *tmp = new list; pform_range_t index; index.first = new PENumber(new verinum((uint64_t)0, integer_width)); index.second = new PEBinary('-', $2, new PENumber(new verinum((uint64_t)1, integer_width))); tmp->push_back(index); $$ = tmp; } | '[' ']' { list *tmp = new list; pform_range_t index (0,0); tmp->push_back(index); $$ = tmp; } | '[' '$' ']' { // SystemVerilog queue list *tmp = new list; pform_range_t index (new PENull,0); if (!gn_system_verilog()) { yyerror("error: Queue declarations require SystemVerilog."); } tmp->push_back(index); $$ = tmp; } ; /* Verilog-2001 supports attribute lists, which can be attached to a variety of different objects. The syntax inside the (* *) is a comma separated list of names or names with assigned values. */ attribute_list_opt : attribute_instance_list { $$ = $1; } | { $$ = 0; } ; attribute_instance_list : K_PSTAR K_STARP { $$ = 0; } | K_PSTAR attribute_list K_STARP { $$ = $2; } | attribute_instance_list K_PSTAR K_STARP { $$ = $1; } | attribute_instance_list K_PSTAR attribute_list K_STARP { list*tmp = $1; if (tmp) { tmp->splice(tmp->end(), *$3); delete $3; $$ = tmp; } else $$ = $3; } ; attribute_list : attribute_list ',' attribute { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } | attribute { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } ; attribute : IDENTIFIER { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($1); tmp->parm = 0; delete[]$1; $$ = tmp; } | IDENTIFIER '=' expression { PExpr*tmp = $3; named_pexpr_t*tmp2 = new named_pexpr_t; tmp2->name = lex_strings.make($1); tmp2->parm = tmp; delete[]$1; $$ = tmp2; } ; /* The block_item_decl is used in function definitions, task definitions, module definitions and named blocks. Wherever a new scope is entered, the source may declare new registers and integers. This rule matches those declarations. The containing rule has presumably set up the scope. */ block_item_decl /* variable declarations. Note that data_type can be 0 if we are recovering from an error. */ : data_type register_variable_list ';' { if ($1) pform_set_data_type(@1, $1, $2, NetNet::REG, attributes_in_context); } | K_reg data_type register_variable_list ';' { if ($2) pform_set_data_type(@2, $2, $3, NetNet::REG, attributes_in_context); } | K_event event_variable_list ';' { if ($2) pform_make_events($2, @1.text, @1.first_line); } | K_parameter param_type parameter_assign_list ';' | K_localparam param_type localparam_assign_list ';' /* Blocks can have type declarations. */ | type_declaration /* Recover from errors that happen within variable lists. Use the trailing semi-colon to resync the parser. */ | K_integer error ';' { yyerror(@1, "error: syntax error in integer variable list."); yyerrok; } | K_time error ';' { yyerror(@1, "error: syntax error in time variable list."); yyerrok; } | K_parameter error ';' { yyerror(@1, "error: syntax error in parameter list."); yyerrok; } | K_localparam error ';' { yyerror(@1, "error: syntax error localparam list."); yyerrok; } ; block_item_decls : block_item_decl | block_item_decls block_item_decl ; block_item_decls_opt : block_item_decls { $$ = true; } | { $$ = false; } ; /* Type declarations are parsed here. The rule actions call pform functions that add the declaration to the current lexical scope. */ type_declaration : K_typedef data_type IDENTIFIER dimensions_opt ';' { perm_string name = lex_strings.make($3); pform_set_typedef(name, $2, $4); delete[]$3; } /* If the IDENTIFIER already is a typedef, it is possible for this code to override the definition, but only if the typedef is inherited from a different scope. */ | K_typedef data_type TYPE_IDENTIFIER ';' { perm_string name = lex_strings.make($3.text); if (pform_test_type_identifier_local(name)) { yyerror(@3, "error: Typedef identifier \"%s\" is already a type name.", $3.text); } else { pform_set_typedef(name, $2, NULL); } delete[]$3.text; } /* These are forward declarations... */ | K_typedef K_class IDENTIFIER ';' { // Create a synthetic typedef for the class name so that the // lexor detects the name as a type. perm_string name = lex_strings.make($3); class_type_t*tmp = new class_type_t(name); FILE_NAME(tmp, @3); pform_set_typedef(name, tmp, NULL); delete[]$3; } | K_typedef K_enum IDENTIFIER ';' { yyerror(@1, "sorry: Enum forward declarations not supported yet."); } | K_typedef K_struct IDENTIFIER ';' { yyerror(@1, "sorry: Struct forward declarations not supported yet."); } | K_typedef K_union IDENTIFIER ';' { yyerror(@1, "sorry: Union forward declarations not supported yet."); } | K_typedef IDENTIFIER ';' { // Create a synthetic typedef for the class name so that the // lexor detects the name as a type. perm_string name = lex_strings.make($2); class_type_t*tmp = new class_type_t(name); FILE_NAME(tmp, @2); pform_set_typedef(name, tmp, NULL); delete[]$2; } | K_typedef error ';' { yyerror(@2, "error: Syntax error in typedef clause."); yyerrok; } ; /* The structure for an enumeration data type is the keyword "enum", followed by the enumeration values in curly braces. Also allow for an optional base type. The default base type is "int", but it can be any of the integral or vector types. */ enum_data_type : K_enum '{' enum_name_list '}' { enum_type_t*enum_type = new enum_type_t; FILE_NAME(enum_type, @1); enum_type->names .reset($3); enum_type->base_type = IVL_VT_BOOL; enum_type->signed_flag = true; enum_type->integer_flag = false; enum_type->range.reset(make_range_from_width(32)); $$ = enum_type; } | K_enum atom2_type signed_unsigned_opt '{' enum_name_list '}' { enum_type_t*enum_type = new enum_type_t; FILE_NAME(enum_type, @1); enum_type->names .reset($5); enum_type->base_type = IVL_VT_BOOL; enum_type->signed_flag = $3; enum_type->integer_flag = false; enum_type->range.reset(make_range_from_width($2)); $$ = enum_type; } | K_enum K_integer signed_unsigned_opt '{' enum_name_list '}' { enum_type_t*enum_type = new enum_type_t; FILE_NAME(enum_type, @1); enum_type->names .reset($5); enum_type->base_type = IVL_VT_LOGIC; enum_type->signed_flag = $3; enum_type->integer_flag = true; enum_type->range.reset(make_range_from_width(integer_width)); $$ = enum_type; } | K_enum K_logic unsigned_signed_opt dimensions_opt '{' enum_name_list '}' { enum_type_t*enum_type = new enum_type_t; FILE_NAME(enum_type, @1); enum_type->names .reset($6); enum_type->base_type = IVL_VT_LOGIC; enum_type->signed_flag = $3; enum_type->integer_flag = false; enum_type->range.reset($4 ? $4 : make_range_from_width(1)); $$ = enum_type; } | K_enum K_reg unsigned_signed_opt dimensions_opt '{' enum_name_list '}' { enum_type_t*enum_type = new enum_type_t; FILE_NAME(enum_type, @1); enum_type->names .reset($6); enum_type->base_type = IVL_VT_LOGIC; enum_type->signed_flag = $3; enum_type->integer_flag = false; enum_type->range.reset($4 ? $4 : make_range_from_width(1)); $$ = enum_type; } | K_enum K_bit unsigned_signed_opt dimensions_opt '{' enum_name_list '}' { enum_type_t*enum_type = new enum_type_t; FILE_NAME(enum_type, @1); enum_type->names .reset($6); enum_type->base_type = IVL_VT_BOOL; enum_type->signed_flag = $3; enum_type->integer_flag = false; enum_type->range.reset($4 ? $4 : make_range_from_width(1)); $$ = enum_type; } ; enum_name_list : enum_name { $$ = $1; } | enum_name_list ',' enum_name { list*lst = $1; lst->splice(lst->end(), *$3); delete $3; $$ = lst; } ; pos_neg_number : number { $$ = $1; } | '-' number { verinum tmp = -(*($2)); *($2) = tmp; $$ = $2; } ; enum_name : IDENTIFIER { perm_string name = lex_strings.make($1); delete[]$1; $$ = make_named_number(name); } | IDENTIFIER '[' pos_neg_number ']' { perm_string name = lex_strings.make($1); long count = check_enum_seq_value(@1, $3, false); delete[]$1; $$ = make_named_numbers(name, 0, count-1); delete $3; } | IDENTIFIER '[' pos_neg_number ':' pos_neg_number ']' { perm_string name = lex_strings.make($1); $$ = make_named_numbers(name, check_enum_seq_value(@1, $3, true), check_enum_seq_value(@1, $5, true)); delete[]$1; delete $3; delete $5; } | IDENTIFIER '=' expression { perm_string name = lex_strings.make($1); delete[]$1; $$ = make_named_number(name, $3); } | IDENTIFIER '[' pos_neg_number ']' '=' expression { perm_string name = lex_strings.make($1); long count = check_enum_seq_value(@1, $3, false); $$ = make_named_numbers(name, 0, count-1, $6); delete[]$1; delete $3; } | IDENTIFIER '[' pos_neg_number ':' pos_neg_number ']' '=' expression { perm_string name = lex_strings.make($1); $$ = make_named_numbers(name, check_enum_seq_value(@1, $3, true), check_enum_seq_value(@1, $5, true), $8); delete[]$1; delete $3; delete $5; } ; struct_data_type : K_struct K_packed_opt '{' struct_union_member_list '}' { struct_type_t*tmp = new struct_type_t; FILE_NAME(tmp, @1); tmp->packed_flag = $2; tmp->union_flag = false; tmp->members .reset($4); $$ = tmp; } | K_union K_packed_opt '{' struct_union_member_list '}' { struct_type_t*tmp = new struct_type_t; FILE_NAME(tmp, @1); tmp->packed_flag = $2; tmp->union_flag = true; tmp->members .reset($4); $$ = tmp; } | K_struct K_packed_opt '{' error '}' { yyerror(@3, "error: Errors in struct member list."); yyerrok; struct_type_t*tmp = new struct_type_t; FILE_NAME(tmp, @1); tmp->packed_flag = $2; tmp->union_flag = false; $$ = tmp; } | K_union K_packed_opt '{' error '}' { yyerror(@3, "error: Errors in union member list."); yyerrok; struct_type_t*tmp = new struct_type_t; FILE_NAME(tmp, @1); tmp->packed_flag = $2; tmp->union_flag = true; $$ = tmp; } ; /* This is an implementation of the rule snippet: struct_union_member { struct_union_member } that is used in the rule matching struct and union types in IEEE 1800-2012 A.2.2.1. */ struct_union_member_list : struct_union_member_list struct_union_member { list*tmp = $1; tmp->push_back($2); $$ = tmp; } | struct_union_member { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; struct_union_member /* IEEE 1800-2012 A.2.2.1 */ : attribute_list_opt data_type list_of_variable_decl_assignments ';' { struct_member_t*tmp = new struct_member_t; FILE_NAME(tmp, @2); tmp->type .reset($2); tmp->names .reset($3); $$ = tmp; } | error ';' { yyerror(@2, "Error in struct/union member."); yyerrok; $$ = 0; } ; case_item : expression_list_proper ':' statement_or_null { PCase::Item*tmp = new PCase::Item; tmp->expr = *$1; tmp->stat = $3; delete $1; $$ = tmp; } | K_default ':' statement_or_null { PCase::Item*tmp = new PCase::Item; tmp->stat = $3; $$ = tmp; } | K_default statement_or_null { PCase::Item*tmp = new PCase::Item; tmp->stat = $2; $$ = tmp; } | error ':' statement_or_null { yyerror(@2, "error: Incomprehensible case expression."); yyerrok; } ; case_items : case_items case_item { svector*tmp; tmp = new svector(*$1, $2); delete $1; $$ = tmp; } | case_item { svector*tmp = new svector(1); (*tmp)[0] = $1; $$ = tmp; } ; charge_strength : '(' K_small ')' | '(' K_medium ')' | '(' K_large ')' ; charge_strength_opt : charge_strength | ; defparam_assign : hierarchy_identifier '=' expression { pform_set_defparam(*$1, $3); delete $1; } ; defparam_assign_list : defparam_assign | dimensions defparam_assign { yyerror(@1, "error: defparam may not include a range."); delete $1; } | defparam_assign_list ',' defparam_assign ; delay1 : '#' delay_value_simple { list*tmp = new list; tmp->push_back($2); $$ = tmp; } | '#' '(' delay_value ')' { list*tmp = new list; tmp->push_back($3); $$ = tmp; } ; delay3 : '#' delay_value_simple { list*tmp = new list; tmp->push_back($2); $$ = tmp; } | '#' '(' delay_value ')' { list*tmp = new list; tmp->push_back($3); $$ = tmp; } | '#' '(' delay_value ',' delay_value ')' { list*tmp = new list; tmp->push_back($3); tmp->push_back($5); $$ = tmp; } | '#' '(' delay_value ',' delay_value ',' delay_value ')' { list*tmp = new list; tmp->push_back($3); tmp->push_back($5); tmp->push_back($7); $$ = tmp; } ; delay3_opt : delay3 { $$ = $1; } | { $$ = 0; } ; delay_value_list : delay_value { list*tmp = new list; tmp->push_back($1); $$ = tmp; } | delay_value_list ',' delay_value { list*tmp = $1; tmp->push_back($3); $$ = tmp; } ; delay_value : expression { PExpr*tmp = $1; $$ = tmp; } | expression ':' expression ':' expression { $$ = pform_select_mtm_expr($1, $3, $5); } ; delay_value_simple : DEC_NUMBER { verinum*tmp = $1; if (tmp == 0) { yyerror(@1, "internal error: delay."); $$ = 0; } else { $$ = new PENumber(tmp); FILE_NAME($$, @1); } based_size = 0; } | REALTIME { verireal*tmp = $1; if (tmp == 0) { yyerror(@1, "internal error: delay."); $$ = 0; } else { $$ = new PEFNumber(tmp); FILE_NAME($$, @1); } } | IDENTIFIER { PEIdent*tmp = new PEIdent(lex_strings.make($1)); FILE_NAME(tmp, @1); $$ = tmp; delete[]$1; } | TIME_LITERAL { int unit; based_size = 0; $$ = 0; if ($1 == 0 || !get_time_unit($1, unit)) yyerror(@1, "internal error: delay."); else { double p = pow(10.0, (double)(unit - pform_get_timeunit())); double time = atof($1) * p; verireal *v = new verireal(time); $$ = new PEFNumber(v); FILE_NAME($$, @1); } } ; /* The discipline and nature declarations used to take no ';' after the identifier. The 2.3 LRM adds the ';', but since there are programs written to the 2.1 and 2.2 standard that don't, we choose to make the ';' optional in this context. */ optional_semicolon : ';' | ; discipline_declaration : K_discipline IDENTIFIER optional_semicolon { pform_start_discipline($2); } discipline_items K_enddiscipline { pform_end_discipline(@1); delete[] $2; } ; discipline_items : discipline_items discipline_item | discipline_item ; discipline_item : K_domain K_discrete ';' { pform_discipline_domain(@1, IVL_DIS_DISCRETE); } | K_domain K_continuous ';' { pform_discipline_domain(@1, IVL_DIS_CONTINUOUS); } | K_potential IDENTIFIER ';' { pform_discipline_potential(@1, $2); delete[] $2; } | K_flow IDENTIFIER ';' { pform_discipline_flow(@1, $2); delete[] $2; } ; nature_declaration : K_nature IDENTIFIER optional_semicolon { pform_start_nature($2); } nature_items K_endnature { pform_end_nature(@1); delete[] $2; } ; nature_items : nature_items nature_item | nature_item ; nature_item : K_units '=' STRING ';' { delete[] $3; } | K_abstol '=' expression ';' | K_access '=' IDENTIFIER ';' { pform_nature_access(@1, $3); delete[] $3; } | K_idt_nature '=' IDENTIFIER ';' { delete[] $3; } | K_ddt_nature '=' IDENTIFIER ';' { delete[] $3; } ; config_declaration : K_config IDENTIFIER ';' K_design lib_cell_identifiers ';' list_of_config_rule_statements K_endconfig { cerr << @1 << ": sorry: config declarations are not supported and " "will be skipped." << endl; delete[] $2; } ; lib_cell_identifiers : /* The BNF implies this can be blank, but I'm not sure exactly what * this means. */ | lib_cell_identifiers lib_cell_id ; list_of_config_rule_statements : /* config rules are optional. */ | list_of_config_rule_statements config_rule_statement ; config_rule_statement : K_default K_liblist list_of_libraries ';' | K_instance hierarchy_identifier K_liblist list_of_libraries ';' { delete $2; } | K_instance hierarchy_identifier K_use lib_cell_id opt_config ';' { delete $2; } | K_cell lib_cell_id K_liblist list_of_libraries ';' | K_cell lib_cell_id K_use lib_cell_id opt_config ';' ; opt_config : /* The use clause takes an optional :config. */ | ':' K_config ; lib_cell_id : IDENTIFIER { delete[] $1; } | IDENTIFIER '.' IDENTIFIER { delete[] $1; delete[] $3; } ; list_of_libraries : /* A NULL library means use the parents cell library. */ | list_of_libraries IDENTIFIER { delete[] $2; } ; drive_strength : '(' dr_strength0 ',' dr_strength1 ')' { $$.str0 = $2.str0; $$.str1 = $4.str1; } | '(' dr_strength1 ',' dr_strength0 ')' { $$.str0 = $4.str0; $$.str1 = $2.str1; } | '(' dr_strength0 ',' K_highz1 ')' { $$.str0 = $2.str0; $$.str1 = IVL_DR_HiZ; } | '(' dr_strength1 ',' K_highz0 ')' { $$.str0 = IVL_DR_HiZ; $$.str1 = $2.str1; } | '(' K_highz1 ',' dr_strength0 ')' { $$.str0 = $4.str0; $$.str1 = IVL_DR_HiZ; } | '(' K_highz0 ',' dr_strength1 ')' { $$.str0 = IVL_DR_HiZ; $$.str1 = $4.str1; } ; drive_strength_opt : drive_strength { $$ = $1; } | { $$.str0 = IVL_DR_STRONG; $$.str1 = IVL_DR_STRONG; } ; dr_strength0 : K_supply0 { $$.str0 = IVL_DR_SUPPLY; } | K_strong0 { $$.str0 = IVL_DR_STRONG; } | K_pull0 { $$.str0 = IVL_DR_PULL; } | K_weak0 { $$.str0 = IVL_DR_WEAK; } ; dr_strength1 : K_supply1 { $$.str1 = IVL_DR_SUPPLY; } | K_strong1 { $$.str1 = IVL_DR_STRONG; } | K_pull1 { $$.str1 = IVL_DR_PULL; } | K_weak1 { $$.str1 = IVL_DR_WEAK; } ; clocking_event_opt /* */ : event_control | ; event_control /* A.K.A. clocking_event */ : '@' hierarchy_identifier { PEIdent*tmpi = new PEIdent(*$2); PEEvent*tmpe = new PEEvent(PEEvent::ANYEDGE, tmpi); PEventStatement*tmps = new PEventStatement(tmpe); FILE_NAME(tmps, @1); $$ = tmps; delete $2; } | '@' '(' event_expression_list ')' { PEventStatement*tmp = new PEventStatement(*$3); FILE_NAME(tmp, @1); delete $3; $$ = tmp; } | '@' '(' error ')' { yyerror(@1, "error: Malformed event control expression."); $$ = 0; } ; event_expression_list : event_expression { $$ = $1; } | event_expression_list K_or event_expression { svector*tmp = new svector(*$1, *$3); delete $1; delete $3; $$ = tmp; } | event_expression_list ',' event_expression { svector*tmp = new svector(*$1, *$3); delete $1; delete $3; $$ = tmp; } ; event_expression : K_posedge expression { PEEvent*tmp = new PEEvent(PEEvent::POSEDGE, $2); FILE_NAME(tmp, @1); svector*tl = new svector(1); (*tl)[0] = tmp; $$ = tl; } | K_negedge expression { PEEvent*tmp = new PEEvent(PEEvent::NEGEDGE, $2); FILE_NAME(tmp, @1); svector*tl = new svector(1); (*tl)[0] = tmp; $$ = tl; } | expression { PEEvent*tmp = new PEEvent(PEEvent::ANYEDGE, $1); FILE_NAME(tmp, @1); svector*tl = new svector(1); (*tl)[0] = tmp; $$ = tl; } ; /* A branch probe expression applies a probe function (potential or flow) to a branch. The branch may be implicit as a pair of nets or explicit as a named branch. Elaboration will check that the function name really is a nature attribute identifier. */ branch_probe_expression : IDENTIFIER '(' IDENTIFIER ',' IDENTIFIER ')' { $$ = pform_make_branch_probe_expression(@1, $1, $3, $5); } | IDENTIFIER '(' IDENTIFIER ')' { $$ = pform_make_branch_probe_expression(@1, $1, $3); } ; expression : expr_primary { $$ = $1; } | inc_or_dec_expression { $$ = $1; } | inside_expression { $$ = $1; } | '+' attribute_list_opt expr_primary %prec UNARY_PREC { $$ = $3; } | '-' attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('-', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '~' attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('~', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '&' attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('&', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '!' attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('!', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '|' attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('|', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '^' attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('^', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '~' '&' attribute_list_opt expr_primary %prec UNARY_PREC { yyerror(@1, "error: '~' '&' is not a valid expression. " "Please use operator '~&' instead."); $$ = 0; } | '~' '|' attribute_list_opt expr_primary %prec UNARY_PREC { yyerror(@1, "error: '~' '|' is not a valid expression. " "Please use operator '~|' instead."); $$ = 0; } | '~' '^' attribute_list_opt expr_primary %prec UNARY_PREC { yyerror(@1, "error: '~' '^' is not a valid expression. " "Please use operator '~^' instead."); $$ = 0; } | K_NAND attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('A', $3); FILE_NAME(tmp, @3); $$ = tmp; } | K_NOR attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('N', $3); FILE_NAME(tmp, @3); $$ = tmp; } | K_NXOR attribute_list_opt expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('X', $3); FILE_NAME(tmp, @3); $$ = tmp; } | '!' error %prec UNARY_PREC { yyerror(@1, "error: Operand of unary ! " "is not a primary expression."); $$ = 0; } | '^' error %prec UNARY_PREC { yyerror(@1, "error: Operand of reduction ^ " "is not a primary expression."); $$ = 0; } | expression '^' attribute_list_opt expression { PEBinary*tmp = new PEBinary('^', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_POW attribute_list_opt expression { PEBinary*tmp = new PEBPower('p', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '*' attribute_list_opt expression { PEBinary*tmp = new PEBinary('*', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '/' attribute_list_opt expression { PEBinary*tmp = new PEBinary('/', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '%' attribute_list_opt expression { PEBinary*tmp = new PEBinary('%', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '+' attribute_list_opt expression { PEBinary*tmp = new PEBinary('+', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '-' attribute_list_opt expression { PEBinary*tmp = new PEBinary('-', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '&' attribute_list_opt expression { PEBinary*tmp = new PEBinary('&', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '|' attribute_list_opt expression { PEBinary*tmp = new PEBinary('|', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_NAND attribute_list_opt expression { PEBinary*tmp = new PEBinary('A', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_NOR attribute_list_opt expression { PEBinary*tmp = new PEBinary('O', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_NXOR attribute_list_opt expression { PEBinary*tmp = new PEBinary('X', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '<' attribute_list_opt expression { PEBinary*tmp = new PEBComp('<', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '>' attribute_list_opt expression { PEBinary*tmp = new PEBComp('>', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_LS attribute_list_opt expression { PEBinary*tmp = new PEBShift('l', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_RS attribute_list_opt expression { PEBinary*tmp = new PEBShift('r', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_RSS attribute_list_opt expression { PEBinary*tmp = new PEBShift('R', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_EQ attribute_list_opt expression { PEBinary*tmp = new PEBComp('e', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_CEQ attribute_list_opt expression { PEBinary*tmp = new PEBComp('E', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_LE attribute_list_opt expression { PEBinary*tmp = new PEBComp('L', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_GE attribute_list_opt expression { PEBinary*tmp = new PEBComp('G', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_NE attribute_list_opt expression { PEBinary*tmp = new PEBComp('n', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_CNE attribute_list_opt expression { PEBinary*tmp = new PEBComp('N', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_LOR attribute_list_opt expression { PEBinary*tmp = new PEBLogic('o', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression K_LAND attribute_list_opt expression { PEBinary*tmp = new PEBLogic('a', $1, $4); FILE_NAME(tmp, @2); $$ = tmp; } | expression '?' attribute_list_opt expression ':' expression { PETernary*tmp = new PETernary($1, $4, $6); FILE_NAME(tmp, @2); $$ = tmp; } ; expr_mintypmax : expression { $$ = $1; } | expression ':' expression ':' expression { switch (min_typ_max_flag) { case MIN: $$ = $1; delete $3; delete $5; break; case TYP: delete $1; $$ = $3; delete $5; break; case MAX: delete $1; delete $3; $$ = $5; break; } if (min_typ_max_warn > 0) { cerr << $$->get_fileline() << ": warning: choosing "; switch (min_typ_max_flag) { case MIN: cerr << "min"; break; case TYP: cerr << "typ"; break; case MAX: cerr << "max"; break; } cerr << " expression." << endl; min_typ_max_warn -= 1; } } ; /* Many contexts take a comma separated list of expressions. Null expressions can happen anywhere in the list, so there are two extra rules in expression_list_with_nuls for parsing and installing those nulls. The expression_list_proper rules do not allow null items in the expression list, so can be used where nul expressions are not allowed. */ expression_list_with_nuls : expression_list_with_nuls ',' expression { list*tmp = $1; tmp->push_back($3); $$ = tmp; } | expression { list*tmp = new list; tmp->push_back($1); $$ = tmp; } | { list*tmp = new list; tmp->push_back(0); $$ = tmp; } | expression_list_with_nuls ',' { list*tmp = $1; tmp->push_back(0); $$ = tmp; } ; expression_list_proper : expression_list_proper ',' expression { list*tmp = $1; tmp->push_back($3); $$ = tmp; } | expression { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; expr_primary : number { assert($1); PENumber*tmp = new PENumber($1); FILE_NAME(tmp, @1); $$ = tmp; } | REALTIME { PEFNumber*tmp = new PEFNumber($1); FILE_NAME(tmp, @1); $$ = tmp; } | STRING { PEString*tmp = new PEString($1); FILE_NAME(tmp, @1); $$ = tmp; } | TIME_LITERAL { int unit; based_size = 0; $$ = 0; if ($1 == 0 || !get_time_unit($1, unit)) yyerror(@1, "internal error: delay."); else { double p = pow(10.0, (double)(unit - pform_get_timeunit())); double time = atof($1) * p; verireal *v = new verireal(time); $$ = new PEFNumber(v); FILE_NAME($$, @1); } } | SYSTEM_IDENTIFIER { perm_string tn = lex_strings.make($1); PECallFunction*tmp = new PECallFunction(tn); FILE_NAME(tmp, @1); $$ = tmp; delete[]$1; } /* There are a few special cases (notably $bits argument) where the expression may be a type name. Let the elaborator sort this out. */ | TYPE_IDENTIFIER { PETypename*tmp = new PETypename($1.type); FILE_NAME(tmp,@1); $$ = tmp; delete[]$1.text; } /* The hierarchy_identifier rule matches simple identifiers as well as indexed arrays and part selects */ | hierarchy_identifier { PEIdent*tmp = pform_new_ident(*$1); FILE_NAME(tmp, @1); $$ = tmp; delete $1; } | PACKAGE_IDENTIFIER K_SCOPE_RES hierarchy_identifier { $$ = pform_package_ident(@2, $1, $3); delete $3; } /* An identifier followed by an expression list in parentheses is a function call. If a system identifier, then a system function call. It can also be a call to a class method (function). */ | hierarchy_identifier '(' expression_list_with_nuls ')' { list*expr_list = $3; strip_tail_items(expr_list); PECallFunction*tmp = pform_make_call_function(@1, *$1, *expr_list); delete $1; $$ = tmp; } | implicit_class_handle '.' hierarchy_identifier '(' expression_list_with_nuls ')' { pform_name_t*t_name = $1; while (! $3->empty()) { t_name->push_back($3->front()); $3->pop_front(); } list*expr_list = $5; strip_tail_items(expr_list); PECallFunction*tmp = pform_make_call_function(@1, *t_name, *expr_list); delete $1; delete $3; $$ = tmp; } | SYSTEM_IDENTIFIER '(' expression_list_proper ')' { perm_string tn = lex_strings.make($1); PECallFunction*tmp = new PECallFunction(tn, *$3); FILE_NAME(tmp, @1); delete[]$1; $$ = tmp; } | PACKAGE_IDENTIFIER K_SCOPE_RES IDENTIFIER '(' expression_list_proper ')' { perm_string use_name = lex_strings.make($3); PECallFunction*tmp = new PECallFunction($1, use_name, *$5); FILE_NAME(tmp, @3); delete[]$3; $$ = tmp; } | SYSTEM_IDENTIFIER '(' ')' { perm_string tn = lex_strings.make($1); const vectorempty; PECallFunction*tmp = new PECallFunction(tn, empty); FILE_NAME(tmp, @1); delete[]$1; $$ = tmp; if (!gn_system_verilog()) { yyerror(@1, "error: Empty function argument list requires SystemVerilog."); } } | implicit_class_handle { PEIdent*tmp = new PEIdent(*$1); FILE_NAME(tmp,@1); delete $1; $$ = tmp; } | implicit_class_handle '.' hierarchy_identifier { pform_name_t*t_name = $1; while (! $3->empty()) { t_name->push_back($3->front()); $3->pop_front(); } PEIdent*tmp = new PEIdent(*t_name); FILE_NAME(tmp,@1); delete $1; delete $3; $$ = tmp; } /* Many of the VAMS built-in functions are available as builtin functions with $system_function equivalents. */ | K_acos '(' expression ')' { perm_string tn = perm_string::literal("$acos"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_acosh '(' expression ')' { perm_string tn = perm_string::literal("$acosh"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_asin '(' expression ')' { perm_string tn = perm_string::literal("$asin"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_asinh '(' expression ')' { perm_string tn = perm_string::literal("$asinh"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_atan '(' expression ')' { perm_string tn = perm_string::literal("$atan"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_atanh '(' expression ')' { perm_string tn = perm_string::literal("$atanh"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_atan2 '(' expression ',' expression ')' { perm_string tn = perm_string::literal("$atan2"); PECallFunction*tmp = make_call_function(tn, $3, $5); FILE_NAME(tmp,@1); $$ = tmp; } | K_ceil '(' expression ')' { perm_string tn = perm_string::literal("$ceil"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_cos '(' expression ')' { perm_string tn = perm_string::literal("$cos"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_cosh '(' expression ')' { perm_string tn = perm_string::literal("$cosh"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_exp '(' expression ')' { perm_string tn = perm_string::literal("$exp"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_floor '(' expression ')' { perm_string tn = perm_string::literal("$floor"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_hypot '(' expression ',' expression ')' { perm_string tn = perm_string::literal("$hypot"); PECallFunction*tmp = make_call_function(tn, $3, $5); FILE_NAME(tmp,@1); $$ = tmp; } | K_ln '(' expression ')' { perm_string tn = perm_string::literal("$ln"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_log '(' expression ')' { perm_string tn = perm_string::literal("$log10"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_pow '(' expression ',' expression ')' { perm_string tn = perm_string::literal("$pow"); PECallFunction*tmp = make_call_function(tn, $3, $5); FILE_NAME(tmp,@1); $$ = tmp; } | K_sin '(' expression ')' { perm_string tn = perm_string::literal("$sin"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_sinh '(' expression ')' { perm_string tn = perm_string::literal("$sinh"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_sqrt '(' expression ')' { perm_string tn = perm_string::literal("$sqrt"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_tan '(' expression ')' { perm_string tn = perm_string::literal("$tan"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_tanh '(' expression ')' { perm_string tn = perm_string::literal("$tanh"); PECallFunction*tmp = make_call_function(tn, $3); FILE_NAME(tmp,@1); $$ = tmp; } /* These mathematical functions are conveniently expressed as unary and binary expressions. They behave much like unary/binary operators, even though they are parsed as functions. */ | K_abs '(' expression ')' { PEUnary*tmp = new PEUnary('m', $3); FILE_NAME(tmp,@1); $$ = tmp; } | K_max '(' expression ',' expression ')' { PEBinary*tmp = new PEBinary('M', $3, $5); FILE_NAME(tmp,@1); $$ = tmp; } | K_min '(' expression ',' expression ')' { PEBinary*tmp = new PEBinary('m', $3, $5); FILE_NAME(tmp,@1); $$ = tmp; } /* Parenthesized expressions are primaries. */ | '(' expr_mintypmax ')' { $$ = $2; } /* Various kinds of concatenation expressions. */ | '{' expression_list_proper '}' { PEConcat*tmp = new PEConcat(*$2); FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | '{' expression '{' expression_list_proper '}' '}' { PExpr*rep = $2; PEConcat*tmp = new PEConcat(*$4, rep); FILE_NAME(tmp, @1); delete $4; $$ = tmp; } | '{' expression '{' expression_list_proper '}' error '}' { PExpr*rep = $2; PEConcat*tmp = new PEConcat(*$4, rep); FILE_NAME(tmp, @1); delete $4; $$ = tmp; yyerror(@5, "error: Syntax error between internal '}' " "and closing '}' of repeat concatenation."); yyerrok; } | '{' '}' { // This is the empty queue syntax. if (gn_system_verilog()) { list empty_list; PEConcat*tmp = new PEConcat(empty_list); FILE_NAME(tmp, @1); $$ = tmp; } else { yyerror(@1, "error: Concatenations are not allowed to be empty."); $$ = 0; } } /* Cast expressions are primaries */ | DEC_NUMBER '\'' '(' expression ')' { PExpr*base = $4; if (gn_system_verilog()) { PECastSize*tmp = new PECastSize($1->as_ulong(), base); FILE_NAME(tmp, @1); delete $1; $$ = tmp; } else { yyerror(@1, "error: Size cast requires SystemVerilog."); $$ = base; } } | data_type '\'' '(' expression ')' { PExpr*base = $4; if (gn_system_verilog()) { PECastType*tmp = new PECastType($1, base); FILE_NAME(tmp, @1); $$ = tmp; } else { yyerror(@1, "error: Type cast requires SystemVerilog."); $$ = base; } } /* Aggregate literals are primaries. */ | assignment_pattern { $$ = $1; } /* SystemVerilog supports streaming concatenation */ | streaming_concatenation { $$ = $1; } | K_null { PENull*tmp = new PENull; FILE_NAME(tmp, @1); $$ = tmp; } ; /* A function_item_list borrows the task_port_item run to match declarations of ports. We check later to make sure there are no output or inout ports actually used. The function_item is the same as tf_item_declaration. */ function_item_list_opt : function_item_list { $$ = $1; } | { $$ = 0; } ; function_item_list : function_item { $$ = $1; } | function_item_list function_item { /* */ if ($1 && $2) { vector*tmp = $1; size_t s1 = tmp->size(); tmp->resize(s1 + $2->size()); for (size_t idx = 0 ; idx < $2->size() ; idx += 1) tmp->at(s1+idx) = $2->at(idx); delete $2; $$ = tmp; } else if ($1) { $$ = $1; } else { $$ = $2; } } ; function_item : tf_port_declaration { $$ = $1; } | block_item_decl { $$ = 0; } ; /* A gate_instance is a module instantiation or a built in part type. In any case, the gate has a set of connections to ports. */ gate_instance : IDENTIFIER '(' expression_list_with_nuls ')' { lgate*tmp = new lgate; tmp->name = $1; tmp->parms = $3; tmp->file = @1.text; tmp->lineno = @1.first_line; delete[]$1; $$ = tmp; } | IDENTIFIER dimensions '(' expression_list_with_nuls ')' { lgate*tmp = new lgate; list*rng = $2; tmp->name = $1; tmp->parms = $4; tmp->range = rng->front(); rng->pop_front(); assert(rng->empty()); tmp->file = @1.text; tmp->lineno = @1.first_line; delete[]$1; delete rng; $$ = tmp; } | '(' expression_list_with_nuls ')' { lgate*tmp = new lgate; tmp->name = ""; tmp->parms = $2; tmp->file = @1.text; tmp->lineno = @1.first_line; $$ = tmp; } /* Degenerate modules can have no ports. */ | IDENTIFIER dimensions { lgate*tmp = new lgate; list*rng = $2; tmp->name = $1; tmp->parms = 0; tmp->parms_by_name = 0; tmp->range = rng->front(); rng->pop_front(); assert(rng->empty()); tmp->file = @1.text; tmp->lineno = @1.first_line; delete[]$1; delete rng; $$ = tmp; } /* Modules can also take ports by port-name expressions. */ | IDENTIFIER '(' port_name_list ')' { lgate*tmp = new lgate; tmp->name = $1; tmp->parms = 0; tmp->parms_by_name = $3; tmp->file = @1.text; tmp->lineno = @1.first_line; delete[]$1; $$ = tmp; } | IDENTIFIER dimensions '(' port_name_list ')' { lgate*tmp = new lgate; list*rng = $2; tmp->name = $1; tmp->parms = 0; tmp->parms_by_name = $4; tmp->range = rng->front(); rng->pop_front(); assert(rng->empty()); tmp->file = @1.text; tmp->lineno = @1.first_line; delete[]$1; delete rng; $$ = tmp; } | IDENTIFIER '(' error ')' { lgate*tmp = new lgate; tmp->name = $1; tmp->parms = 0; tmp->parms_by_name = 0; tmp->file = @1.text; tmp->lineno = @1.first_line; yyerror(@2, "error: Syntax error in instance port " "expression(s)."); delete[]$1; $$ = tmp; } | IDENTIFIER dimensions '(' error ')' { lgate*tmp = new lgate; tmp->name = $1; tmp->parms = 0; tmp->parms_by_name = 0; tmp->file = @1.text; tmp->lineno = @1.first_line; yyerror(@3, "error: Syntax error in instance port " "expression(s)."); delete[]$1; $$ = tmp; } ; gate_instance_list : gate_instance_list ',' gate_instance { svector*tmp1 = $1; lgate*tmp2 = $3; svector*out = new svector (*tmp1, *tmp2); delete tmp1; delete tmp2; $$ = out; } | gate_instance { svector*tmp = new svector(1); (*tmp)[0] = *$1; delete $1; $$ = tmp; } ; gatetype : K_and { $$ = PGBuiltin::AND; } | K_nand { $$ = PGBuiltin::NAND; } | K_or { $$ = PGBuiltin::OR; } | K_nor { $$ = PGBuiltin::NOR; } | K_xor { $$ = PGBuiltin::XOR; } | K_xnor { $$ = PGBuiltin::XNOR; } | K_buf { $$ = PGBuiltin::BUF; } | K_bufif0 { $$ = PGBuiltin::BUFIF0; } | K_bufif1 { $$ = PGBuiltin::BUFIF1; } | K_not { $$ = PGBuiltin::NOT; } | K_notif0 { $$ = PGBuiltin::NOTIF0; } | K_notif1 { $$ = PGBuiltin::NOTIF1; } ; switchtype : K_nmos { $$ = PGBuiltin::NMOS; } | K_rnmos { $$ = PGBuiltin::RNMOS; } | K_pmos { $$ = PGBuiltin::PMOS; } | K_rpmos { $$ = PGBuiltin::RPMOS; } | K_cmos { $$ = PGBuiltin::CMOS; } | K_rcmos { $$ = PGBuiltin::RCMOS; } | K_tran { $$ = PGBuiltin::TRAN; } | K_rtran { $$ = PGBuiltin::RTRAN; } | K_tranif0 { $$ = PGBuiltin::TRANIF0; } | K_tranif1 { $$ = PGBuiltin::TRANIF1; } | K_rtranif0 { $$ = PGBuiltin::RTRANIF0; } | K_rtranif1 { $$ = PGBuiltin::RTRANIF1; } ; /* A general identifier is a hierarchical name, with the right most name the base of the identifier. This rule builds up a hierarchical name from the left to the right, forming a list of names. */ hierarchy_identifier : IDENTIFIER { $$ = new pform_name_t; $$->push_back(name_component_t(lex_strings.make($1))); delete[]$1; } | hierarchy_identifier '.' IDENTIFIER { pform_name_t * tmp = $1; tmp->push_back(name_component_t(lex_strings.make($3))); delete[]$3; $$ = tmp; } | hierarchy_identifier '[' expression ']' { pform_name_t * tmp = $1; name_component_t&tail = tmp->back(); index_component_t itmp; itmp.sel = index_component_t::SEL_BIT; itmp.msb = $3; tail.index.push_back(itmp); $$ = tmp; } | hierarchy_identifier '[' '$' ']' { pform_name_t * tmp = $1; name_component_t&tail = tmp->back(); if (! gn_system_verilog()) { yyerror(@3, "error: Last element expression ($) " "requires SystemVerilog. Try enabling SystemVerilog."); } index_component_t itmp; itmp.sel = index_component_t::SEL_BIT_LAST; itmp.msb = 0; itmp.lsb = 0; tail.index.push_back(itmp); $$ = tmp; } | hierarchy_identifier '[' expression ':' expression ']' { pform_name_t * tmp = $1; name_component_t&tail = tmp->back(); index_component_t itmp; itmp.sel = index_component_t::SEL_PART; itmp.msb = $3; itmp.lsb = $5; tail.index.push_back(itmp); $$ = tmp; } | hierarchy_identifier '[' expression K_PO_POS expression ']' { pform_name_t * tmp = $1; name_component_t&tail = tmp->back(); index_component_t itmp; itmp.sel = index_component_t::SEL_IDX_UP; itmp.msb = $3; itmp.lsb = $5; tail.index.push_back(itmp); $$ = tmp; } | hierarchy_identifier '[' expression K_PO_NEG expression ']' { pform_name_t * tmp = $1; name_component_t&tail = tmp->back(); index_component_t itmp; itmp.sel = index_component_t::SEL_IDX_DO; itmp.msb = $3; itmp.lsb = $5; tail.index.push_back(itmp); $$ = tmp; } ; /* This is a list of identifiers. The result is a list of strings, each one of the identifiers in the list. These are simple, non-hierarchical names separated by ',' characters. */ list_of_identifiers : IDENTIFIER { $$ = list_from_identifier($1); } | list_of_identifiers ',' IDENTIFIER { $$ = list_from_identifier($1, $3); } ; list_of_port_identifiers : IDENTIFIER { $$ = make_port_list($1, 0); } | IDENTIFIER '=' expression { $$ = make_port_list($1, $3); } | list_of_port_identifiers ',' IDENTIFIER { $$ = make_port_list($1, $3, 0); } | list_of_port_identifiers ',' IDENTIFIER '=' expression { $$ = make_port_list($1, $3, $5); } ; /* The list_of_ports and list_of_port_declarations rules are the port list formats for module ports. The list_of_ports_opt rule is only used by the module start rule. The first, the list_of_ports, is the 1364-1995 format, a list of port names, including .name() syntax. The list_of_port_declarations the 1364-2001 format, an in-line declaration of the ports. In both cases, the list_of_ports and list_of_port_declarations returns an array of Module::port_t* items that include the name of the port internally and externally. The actual creation of the nets/variables is done in the declaration, whether internal to the port list or in amongst the module items. */ list_of_ports : port_opt { vector*tmp = new vector(1); (*tmp)[0] = $1; $$ = tmp; } | list_of_ports ',' port_opt { vector*tmp = $1; tmp->push_back($3); $$ = tmp; } ; list_of_port_declarations : port_declaration { vector*tmp = new vector(1); (*tmp)[0] = $1; $$ = tmp; } | list_of_port_declarations ',' port_declaration { vector*tmp = $1; tmp->push_back($3); $$ = tmp; } | list_of_port_declarations ',' IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($3); ptmp = pform_module_port_reference(name, @3.text, @3.first_line); vector*tmp = $1; tmp->push_back(ptmp); /* Get the port declaration details, the port type and what not, from context data stored by the last port_declaration rule. */ pform_module_define_port(@3, name, port_declaration_context.port_type, port_declaration_context.port_net_type, port_declaration_context.data_type, 0); delete[]$3; $$ = tmp; } | list_of_port_declarations ',' { yyerror(@2, "error: NULL port declarations are not " "allowed."); } | list_of_port_declarations ';' { yyerror(@2, "error: ';' is an invalid port declaration " "separator."); } ; port_declaration : attribute_list_opt K_input net_type_opt data_type_or_implicit IDENTIFIER dimensions_opt { Module::port_t*ptmp; perm_string name = lex_strings.make($5); data_type_t*use_type = $4; if ($6) use_type = new uarray_type_t(use_type, $6); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::PINPUT, $3, use_type, $1); port_declaration_context.port_type = NetNet::PINPUT; port_declaration_context.port_net_type = $3; port_declaration_context.data_type = $4; delete[]$5; $$ = ptmp; } | attribute_list_opt K_input K_wreal IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($4); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); real_type_t*real_type = new real_type_t(real_type_t::REAL); FILE_NAME(real_type, @3); pform_module_define_port(@2, name, NetNet::PINPUT, NetNet::WIRE, real_type, $1); port_declaration_context.port_type = NetNet::PINPUT; port_declaration_context.port_net_type = NetNet::WIRE; port_declaration_context.data_type = real_type; delete[]$4; $$ = ptmp; } | attribute_list_opt K_inout net_type_opt data_type_or_implicit IDENTIFIER dimensions_opt { Module::port_t*ptmp; perm_string name = lex_strings.make($5); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::PINOUT, $3, $4, $1); port_declaration_context.port_type = NetNet::PINOUT; port_declaration_context.port_net_type = $3; port_declaration_context.data_type = $4; delete[]$5; if ($6) { yyerror(@6, "sorry: Inout ports with unpacked dimensions not supported."); delete $6; } $$ = ptmp; } | attribute_list_opt K_inout K_wreal IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($4); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); real_type_t*real_type = new real_type_t(real_type_t::REAL); FILE_NAME(real_type, @3); pform_module_define_port(@2, name, NetNet::PINOUT, NetNet::WIRE, real_type, $1); port_declaration_context.port_type = NetNet::PINOUT; port_declaration_context.port_net_type = NetNet::WIRE; port_declaration_context.data_type = real_type; delete[]$4; $$ = ptmp; } | attribute_list_opt K_output net_type_opt data_type_or_implicit IDENTIFIER dimensions_opt { Module::port_t*ptmp; perm_string name = lex_strings.make($5); data_type_t*use_dtype = $4; if ($6) use_dtype = new uarray_type_t(use_dtype, $6); NetNet::Type use_type = $3; if (use_type == NetNet::IMPLICIT) { if (vector_type_t*dtype = dynamic_cast ($4)) { if (dtype->reg_flag) use_type = NetNet::REG; else if (dtype->implicit_flag) use_type = NetNet::IMPLICIT; else use_type = NetNet::IMPLICIT_REG; // The SystemVerilog types that can show up as // output ports are implicitly (on the inside) // variables because "reg" is not valid syntax // here. } else if (dynamic_cast ($4)) { use_type = NetNet::IMPLICIT_REG; } else if (dynamic_cast ($4)) { use_type = NetNet::IMPLICIT_REG; } else if (enum_type_t*etype = dynamic_cast ($4)) { if(etype->base_type == IVL_VT_LOGIC) use_type = NetNet::IMPLICIT_REG; } } ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, use_type, use_dtype, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = use_type; port_declaration_context.data_type = $4; delete[]$5; $$ = ptmp; } | attribute_list_opt K_output K_wreal IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($4); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); real_type_t*real_type = new real_type_t(real_type_t::REAL); FILE_NAME(real_type, @3); pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::WIRE, real_type, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = NetNet::WIRE; port_declaration_context.data_type = real_type; delete[]$4; $$ = ptmp; } | attribute_list_opt K_output net_type_opt data_type_or_implicit IDENTIFIER '=' expression { Module::port_t*ptmp; perm_string name = lex_strings.make($5); NetNet::Type use_type = $3; if (use_type == NetNet::IMPLICIT) { if (vector_type_t*dtype = dynamic_cast ($4)) { if (dtype->reg_flag) use_type = NetNet::REG; else use_type = NetNet::IMPLICIT_REG; } else { use_type = NetNet::IMPLICIT_REG; } } ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, use_type, $4, $1); port_declaration_context.port_type = NetNet::PINOUT; port_declaration_context.port_net_type = use_type; port_declaration_context.data_type = $4; pform_make_reginit(@5, name, $7); delete[]$5; $$ = ptmp; } ; net_type_opt : net_type { $$ = $1; } | { $$ = NetNet::IMPLICIT; } ; /* * The signed_opt rule will return "true" if K_signed is present, * for "false" otherwise. This rule corresponds to the declaration * defaults for reg/bit/logic. * * The signed_unsigned_opt rule with match K_signed or K_unsigned * and return true or false as appropriate. The default is * "true". This corresponds to the declaration defaults for * byte/shortint/int/longint. */ unsigned_signed_opt : K_signed { $$ = true; } | K_unsigned { $$ = false; } | { $$ = false; } ; signed_unsigned_opt : K_signed { $$ = true; } | K_unsigned { $$ = false; } | { $$ = true; } ; /* * In some places we can take any of the 4 2-value atom-type * names. All the context needs to know if that type is its width. */ atom2_type : K_byte { $$ = 8; } | K_shortint { $$ = 16; } | K_int { $$ = 32; } | K_longint { $$ = 64; } ; /* An lpvalue is the expression that can go on the left side of a procedural assignment. This rule handles only procedural assignments. It is more limited than the general expr_primary rule to reflect the rules for assignment l-values. */ lpvalue : hierarchy_identifier { PEIdent*tmp = pform_new_ident(*$1); FILE_NAME(tmp, @1); $$ = tmp; delete $1; } | implicit_class_handle '.' hierarchy_identifier { pform_name_t*t_name = $1; while (!$3->empty()) { t_name->push_back($3->front()); $3->pop_front(); } PEIdent*tmp = new PEIdent(*t_name); FILE_NAME(tmp, @1); $$ = tmp; delete $1; delete $3; } | '{' expression_list_proper '}' { PEConcat*tmp = new PEConcat(*$2); FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | streaming_concatenation { yyerror(@1, "sorry: streaming concatenation not supported in l-values."); $$ = 0; } ; /* Continuous assignments have a list of individual assignments. */ cont_assign : lpvalue '=' expression { list*tmp = new list; tmp->push_back($1); tmp->push_back($3); $$ = tmp; } ; cont_assign_list : cont_assign_list ',' cont_assign { list*tmp = $1; tmp->splice(tmp->end(), *$3); delete $3; $$ = tmp; } | cont_assign { $$ = $1; } ; /* We allow zero, one or two unique declarations. */ local_timeunit_prec_decl_opt : /* Empty */ | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' { pform_set_timeunit($2, true, false); have_timeunit_decl = true; pform_set_timeprecision($4, true, false); have_timeprec_decl = true; } | local_timeunit_prec_decl | local_timeunit_prec_decl local_timeunit_prec_decl2 ; /* By setting the appropriate have_time???_decl we allow only one declaration of each type in this module. */ local_timeunit_prec_decl : K_timeunit TIME_LITERAL ';' { pform_set_timeunit($2, true, false); have_timeunit_decl = true; } | K_timeprecision TIME_LITERAL ';' { pform_set_timeprecision($2, true, false); have_timeprec_decl = true; } ; local_timeunit_prec_decl2 : K_timeunit TIME_LITERAL ';' { pform_set_timeunit($2, true, false); have_timeunit_decl = true; } | K_timeprecision TIME_LITERAL ';' { pform_set_timeprecision($2, true, false); have_timeprec_decl = true; } /* As the second item this form is always a check. */ | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' { pform_set_timeunit($2, true, true); pform_set_timeprecision($4, true, true); } ; /* This is the global structure of a module. A module is a start section, with optional ports, then an optional list of module items, and finally an end marker. */ module : attribute_list_opt module_start IDENTIFIER { pform_startmodule(@2, $3, $2==K_program, $2==K_interface, $1); } module_package_import_list_opt module_parameter_port_list_opt module_port_list_opt module_attribute_foreign ';' { pform_module_set_ports($7); } local_timeunit_prec_decl_opt { have_timeunit_decl = true; // Every thing past here is have_timeprec_decl = true; // a check! pform_check_timeunit_prec(); } module_item_list_opt module_end { Module::UCDriveType ucd; // The lexor detected `unconnected_drive directives and // marked what it found in the uc_drive variable. Use that // to generate a UCD flag for the module. switch (uc_drive) { case UCD_NONE: default: ucd = Module::UCD_NONE; break; case UCD_PULL0: ucd = Module::UCD_PULL0; break; case UCD_PULL1: ucd = Module::UCD_PULL1; break; } // Check that program/endprogram and module/endmodule // keywords match. if ($2 != $14) { switch ($2) { case K_module: yyerror(@14, "error: module not closed by endmodule."); break; case K_program: yyerror(@14, "error: program not closed by endprogram."); break; case K_interface: yyerror(@14, "error: interface not closed by endinterface."); break; default: break; } } pform_endmodule($3, in_celldefine, ucd); have_timeunit_decl = false; // We will allow decls again. have_timeprec_decl = false; } endlabel_opt { // Last step: check any closing name. This is done late so // that the parser can look ahead to detect the present // endlabel_opt but still have the pform_endmodule() called // early enough that the lexor can know we are outside the // module. if ($16) { if (strcmp($3,$16) != 0) { switch ($2) { case K_module: yyerror(@16, "error: End label doesn't match " "module name."); break; case K_program: yyerror(@16, "error: End label doesn't match " "program name."); break; case K_interface: yyerror(@16, "error: End label doesn't match " "interface name."); break; default: break; } } if (($2 == K_module) && (! gn_system_verilog())) { yyerror(@8, "error: Module end labels require " "SystemVerilog."); } delete[]$16; } delete[]$3; } ; /* Modules start with a module/macromodule, program, or interface keyword, and end with a endmodule, endprogram, or endinterface keyword. The syntax for modules programs, and interfaces is almost identical, so let semantics sort out the differences. */ module_start : K_module { $$ = K_module; } | K_macromodule { $$ = K_module; } | K_program { $$ = K_program; } | K_interface { $$ = K_interface; } ; module_end : K_endmodule { $$ = K_module; } | K_endprogram { $$ = K_program; } | K_endinterface { $$ = K_interface; } ; endlabel_opt : ':' IDENTIFIER { $$ = $2; } | { $$ = 0; } ; module_attribute_foreign : K_PSTAR IDENTIFIER K_integer IDENTIFIER '=' STRING ';' K_STARP { $$ = 0; } | { $$ = 0; } ; module_port_list_opt : '(' list_of_ports ')' { $$ = $2; } | '(' list_of_port_declarations ')' { $$ = $2; } | { $$ = 0; } | '(' error ')' { yyerror(@2, "Errors in port declarations."); yyerrok; $$ = 0; } ; /* Module declarations include optional ANSI style module parameter ports. These are simply advance ways to declare parameters, so that the port declarations may use them. */ module_parameter_port_list_opt : | '#' '(' module_parameter_port_list ')' ; module_parameter_port_list : K_parameter param_type parameter_assign | module_parameter_port_list ',' parameter_assign | module_parameter_port_list ',' K_parameter param_type parameter_assign ; module_item /* Modules can contain further sub-module definitions. */ : module | attribute_list_opt net_type data_type_or_implicit delay3_opt net_variable_list ';' { data_type_t*data_type = $3; if (data_type == 0) { data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); FILE_NAME(data_type, @2); } pform_set_data_type(@2, data_type, $5, $2, $1); if ($4 != 0) { yyerror(@2, "sorry: net delays not supported."); delete $4; } delete $1; } | attribute_list_opt K_wreal delay3 net_variable_list ';' { real_type_t*tmpt = new real_type_t(real_type_t::REAL); pform_set_data_type(@2, tmpt, $4, NetNet::WIRE, $1); if ($3 != 0) { yyerror(@3, "sorry: net delays not supported."); delete $3; } delete $1; } | attribute_list_opt K_wreal net_variable_list ';' { real_type_t*tmpt = new real_type_t(real_type_t::REAL); pform_set_data_type(@2, tmpt, $3, NetNet::WIRE, $1); delete $1; } /* Very similar to the rule above, but this takes a list of net_decl_assigns, which are = assignment declarations. */ | attribute_list_opt net_type data_type_or_implicit delay3_opt net_decl_assigns ';' { data_type_t*data_type = $3; if (data_type == 0) { data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); FILE_NAME(data_type, @2); } pform_makewire(@2, $4, str_strength, $5, $2, data_type); if ($1) { yywarn(@2, "Attributes are not supported on net declaration " "assignments and will be discarded."); delete $1; } } /* This form doesn't have the range, but does have strengths. This gives strength to the assignment drivers. */ | attribute_list_opt net_type data_type_or_implicit drive_strength net_decl_assigns ';' { data_type_t*data_type = $3; if (data_type == 0) { data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); FILE_NAME(data_type, @2); } pform_makewire(@2, 0, $4, $5, $2, data_type); if ($1) { yywarn(@2, "Attributes are not supported on net declaration " "assignments and will be discarded."); delete $1; } } | attribute_list_opt K_wreal net_decl_assigns ';' { real_type_t*data_type = new real_type_t(real_type_t::REAL); pform_makewire(@2, 0, str_strength, $3, NetNet::WIRE, data_type); if ($1) { yywarn(@2, "Attributes are not supported on net declaration " "assignments and will be discarded."); delete $1; } } | K_trireg charge_strength_opt dimensions_opt delay3_opt list_of_identifiers ';' { yyerror(@1, "sorry: trireg nets not supported."); delete $3; delete $4; } | attribute_list_opt port_direction unsigned_signed_opt dimensions_opt delay3_opt list_of_identifiers ';' { pform_set_port_type(@2, $6, $4, $3, $2, $1); } /* The next two rules handle Verilog 2001 statements of the form: input wire signed [h:l] ; This creates the wire and sets the port type all at once. */ | attribute_list_opt port_direction net_type unsigned_signed_opt dimensions_opt list_of_identifiers ';' { pform_makewire(@2, $5, $4, $6, $3, $2, IVL_VT_NO_TYPE, $1, SR_BOTH); } | attribute_list_opt K_output var_type unsigned_signed_opt dimensions_opt list_of_port_identifiers ';' { list >::const_iterator pp; list*tmp = new list; for (pp = $6->begin(); pp != $6->end(); ++ pp ) { tmp->push_back((*pp).first); } pform_makewire(@2, $5, $4, tmp, $3, NetNet::POUTPUT, IVL_VT_NO_TYPE, $1, SR_BOTH); for (pp = $6->begin(); pp != $6->end(); ++ pp ) { if ((*pp).second) { pform_make_reginit(@2, (*pp).first, (*pp).second); } } delete $6; } | attribute_list_opt port_direction K_wreal list_of_identifiers ';' { pform_makewire(@2, 0, true, $4, NetNet::WIRE, $2, IVL_VT_REAL, $1, SR_BOTH); } /* var_type declaration (reg variables) cannot be input or output, because the port declaration implies an external driver, which cannot be attached to a reg. These rules catch that error early. */ | attribute_list_opt K_input var_type unsigned_signed_opt dimensions_opt list_of_identifiers ';' { pform_makewire(@2, $5, $4, $6, $3, NetNet::PINPUT, IVL_VT_NO_TYPE, $1); yyerror(@3, "error: reg variables cannot be inputs."); } | attribute_list_opt K_inout var_type unsigned_signed_opt dimensions_opt list_of_identifiers ';' { pform_makewire(@2, $5, $4, $6, $3, NetNet::PINOUT, IVL_VT_NO_TYPE, $1); yyerror(@3, "error: reg variables cannot be inouts."); } | attribute_list_opt port_direction unsigned_signed_opt dimensions_opt delay3_opt error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; if ($4) delete $4; if ($5) delete $5; yyerrok; } /* Maybe this is a discipline declaration? If so, then the lexor will see the discipline name as an identifier. We match it to the discipline or type name semantically. */ | DISCIPLINE_IDENTIFIER list_of_identifiers ';' { pform_attach_discipline(@1, $1, $2); } /* block_item_decl rule is shared with task blocks and named begin/end. Careful to pass attributes to the block_item_decl. */ | attribute_list_opt { attributes_in_context = $1; } block_item_decl { delete attributes_in_context; attributes_in_context = 0; } /* */ | K_defparam { if (pform_in_interface()) yyerror(@1, "error: Parameter overrides are not allowed " "in interfaces."); } defparam_assign_list ';' /* Most gate types have an optional drive strength and optional two/three-value delay. These rules handle the different cases. We check that the actual number of delays is correct later. */ | attribute_list_opt gatetype gate_instance_list ';' { pform_makegates(@2, $2, str_strength, 0, $3, $1); } | attribute_list_opt gatetype delay3 gate_instance_list ';' { pform_makegates(@2, $2, str_strength, $3, $4, $1); } | attribute_list_opt gatetype drive_strength gate_instance_list ';' { pform_makegates(@2, $2, $3, 0, $4, $1); } | attribute_list_opt gatetype drive_strength delay3 gate_instance_list ';' { pform_makegates(@2, $2, $3, $4, $5, $1); } /* The switch type gates do not support a strength. */ | attribute_list_opt switchtype gate_instance_list ';' { pform_makegates(@2, $2, str_strength, 0, $3, $1); } | attribute_list_opt switchtype delay3 gate_instance_list ';' { pform_makegates(@2, $2, str_strength, $3, $4, $1); } /* Pullup and pulldown devices cannot have delays, and their strengths are limited. */ | K_pullup gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLUP, pull_strength, 0, $2, 0); } | K_pulldown gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLDOWN, pull_strength, 0, $2, 0); } | K_pullup '(' dr_strength1 ')' gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLUP, $3, 0, $5, 0); } | K_pullup '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLUP, $3, 0, $7, 0); } | K_pullup '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLUP, $5, 0, $7, 0); } | K_pulldown '(' dr_strength0 ')' gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLDOWN, $3, 0, $5, 0); } | K_pulldown '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLDOWN, $5, 0, $7, 0); } | K_pulldown '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';' { pform_makegates(@1, PGBuiltin::PULLDOWN, $3, 0, $7, 0); } /* This rule handles instantiations of modules and user defined primitives. These devices to not have delay lists or strengths, but then can have parameter lists. */ | attribute_list_opt IDENTIFIER parameter_value_opt gate_instance_list ';' { perm_string tmp1 = lex_strings.make($2); pform_make_modgates(@2, tmp1, $3, $4); delete[]$2; if ($1) delete $1; } | attribute_list_opt IDENTIFIER parameter_value_opt error ';' { yyerror(@2, "error: Invalid module instantiation"); delete[]$2; if ($1) delete $1; } /* Continuous assignment can have an optional drive strength, then an optional delay3 that applies to all the assignments in the cont_assign_list. */ | K_assign drive_strength_opt delay3_opt cont_assign_list ';' { pform_make_pgassign_list($4, $3, $2, @1.text, @1.first_line); } /* Always and initial items are behavioral processes. */ | attribute_list_opt K_always statement_item { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS, $3, $1); FILE_NAME(tmp, @2); } | attribute_list_opt K_initial statement_item { PProcess*tmp = pform_make_behavior(IVL_PR_INITIAL, $3, $1); FILE_NAME(tmp, @2); } | attribute_list_opt K_final statement_item { PProcess*tmp = pform_make_behavior(IVL_PR_FINAL, $3, $1); FILE_NAME(tmp, @2); } | attribute_list_opt K_analog analog_statement { pform_make_analog_behavior(@2, IVL_PR_ALWAYS, $3); } | attribute_list_opt assertion_item | class_declaration | task_declaration | function_declaration /* A generate region can contain further module items. Actually, it is supposed to be limited to certain kinds of module items, but the semantic tests will check that for us. Do check that the generate/endgenerate regions do not nest. Generate schemes nest, but generate regions do not. */ | K_generate generate_item_list_opt K_endgenerate { // Test for bad nesting. I understand it, but it is illegal. if (pform_parent_generate()) { cerr << @1 << ": error: Generate/endgenerate regions cannot nest." << endl; cerr << @1 << ": : Try removing optional generate/endgenerate keywords," << endl; cerr << @1 << ": : or move them to surround the parent generate scheme." << endl; error_count += 1; } } | K_genvar list_of_identifiers ';' { pform_genvars(@1, $2); } | K_for '(' IDENTIFIER '=' expression ';' expression ';' IDENTIFIER '=' expression ')' { pform_start_generate_for(@1, $3, $5, $7, $9, $11); } generate_block { pform_endgenerate(); } | generate_if generate_block_opt K_else { pform_start_generate_else(@1); } generate_block { pform_endgenerate(); } | generate_if generate_block_opt %prec less_than_K_else { pform_endgenerate(); } | K_case '(' expression ')' { pform_start_generate_case(@1, $3); } generate_case_items K_endcase { pform_endgenerate(); } | modport_declaration | package_import_declaration /* 1364-2001 and later allow specparam declarations outside specify blocks. */ | attribute_list_opt K_specparam { if (pform_in_interface()) yyerror(@1, "error: specparam declarations are not allowed " "in interfaces."); } specparam_decl ';' /* specify blocks are parsed but ignored. */ | K_specify { if (pform_in_interface()) yyerror(@1, "error: specify blocks are not allowed " "in interfaces."); } specify_item_list_opt K_endspecify | K_specify error K_endspecify { yyerror(@1, "error: syntax error in specify block"); yyerrok; } /* These rules match various errors that the user can type into module items. These rules try to catch them at a point where a reasonable error message can be produced. */ | error ';' { yyerror(@2, "error: invalid module item."); yyerrok; } | K_assign error '=' expression ';' { yyerror(@1, "error: syntax error in left side " "of continuous assignment."); yyerrok; } | K_assign error ';' { yyerror(@1, "error: syntax error in " "continuous assignment"); yyerrok; } | K_function error K_endfunction endlabel_opt { yyerror(@1, "error: I give up on this " "function definition."); if ($4) { if (!gn_system_verilog()) { yyerror(@4, "error: Function end names require " "SystemVerilog."); } delete[]$4; } yyerrok; } /* These rules are for the Icarus Verilog specific $attribute extensions. Then catch the parameters of the $attribute keyword. */ | KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' ';' { perm_string tmp3 = lex_strings.make($3); perm_string tmp5 = lex_strings.make($5); pform_set_attrib(tmp3, tmp5, $7); delete[] $3; delete[] $5; } | KK_attribute '(' error ')' ';' { yyerror(@1, "error: Malformed $attribute parameter list."); } | K_timeunit_check TIME_LITERAL ';' { pform_set_timeunit($2, true, true); } | K_timeunit_check TIME_LITERAL '/' TIME_LITERAL ';' { pform_set_timeunit($2, true, true); pform_set_timeprecision($4, true, true); } | K_timeprecision_check TIME_LITERAL ';' { pform_set_timeprecision($2, true, true); } ; module_item_list : module_item_list module_item | module_item ; module_item_list_opt : module_item_list | ; generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } ; generate_case_items : generate_case_items generate_case_item | generate_case_item ; generate_case_item : expression_list_proper ':' { pform_generate_case_item(@1, $1); } generate_block_opt { pform_endgenerate(); } | K_default ':' { pform_generate_case_item(@1, 0); } generate_block_opt { pform_endgenerate(); } ; generate_item : module_item /* Handle some anachronistic syntax cases. */ | K_begin generate_item_list_opt K_end { /* Detect and warn about anachronistic begin/end use */ if (generation_flag > GN_VER2001 && warn_anachronisms) { warn_count += 1; cerr << @1 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl; } } | K_begin ':' IDENTIFIER { pform_start_generate_nblock(@1, $3); } generate_item_list_opt K_end { /* Detect and warn about anachronistic named begin/end use */ if (generation_flag > GN_VER2001 && warn_anachronisms) { warn_count += 1; cerr << @1 << ": warning: Anachronistic use of named begin/end to surround generate schemes." << endl; } pform_endgenerate(); } ; generate_item_list : generate_item_list generate_item | generate_item ; generate_item_list_opt : generate_item_list | ; /* A generate block is the thing within a generate scheme. It may be a single module item, an anonymous block of module items, or a named module item. In all cases, the meat is in the module items inside, and the processing is done by the module_item rules. We only need to take note here of the scope name, if any. */ generate_block : module_item | K_begin generate_item_list_opt K_end | K_begin ':' IDENTIFIER generate_item_list_opt K_end endlabel_opt { pform_generate_block_name($3); if ($6) { if (strcmp($3,$6) != 0) { yyerror(@6, "error: End label doesn't match " "begin name"); } if (! gn_system_verilog()) { yyerror(@6, "error: Begin end labels require " "SystemVerilog."); } delete[]$6; } delete[]$3; } ; generate_block_opt : generate_block | ';' ; /* A net declaration assignment allows the programmer to combine the net declaration and the continuous assignment into a single statement. Note that the continuous assignment statement is generated as a side effect, and all I pass up is the name of the l-value. */ net_decl_assign : IDENTIFIER '=' expression { net_decl_assign_t*tmp = new net_decl_assign_t; tmp->next = tmp; tmp->name = lex_strings.make($1); tmp->expr = $3; delete[]$1; $$ = tmp; } ; net_decl_assigns : net_decl_assigns ',' net_decl_assign { net_decl_assign_t*tmp = $1; $3->next = tmp->next; tmp->next = $3; $$ = tmp; } | net_decl_assign { $$ = $1; } ; bit_logic : K_logic { $$ = IVL_VT_LOGIC; } | K_bool { $$ = IVL_VT_BOOL; /* Icarus misc */} | K_bit { $$ = IVL_VT_BOOL; /* IEEE1800 / IEEE1364-2009 */} ; bit_logic_opt : bit_logic | { $$ = IVL_VT_NO_TYPE; } ; net_type : K_wire { $$ = NetNet::WIRE; } | K_tri { $$ = NetNet::TRI; } | K_tri1 { $$ = NetNet::TRI1; } | K_supply0 { $$ = NetNet::SUPPLY0; } | K_wand { $$ = NetNet::WAND; } | K_triand { $$ = NetNet::TRIAND; } | K_tri0 { $$ = NetNet::TRI0; } | K_supply1 { $$ = NetNet::SUPPLY1; } | K_wor { $$ = NetNet::WOR; } | K_trior { $$ = NetNet::TRIOR; } | K_wone { $$ = NetNet::UNRESOLVED_WIRE; cerr << @1.text << ":" << @1.first_line << ": warning: " "'wone' is deprecated, please use 'uwire' " "instead." << endl; } | K_uwire { $$ = NetNet::UNRESOLVED_WIRE; } ; var_type : K_reg { $$ = NetNet::REG; } ; param_type : bit_logic_opt unsigned_signed_opt dimensions_opt { param_active_range = $3; param_active_signed = $2; if (($1 == IVL_VT_NO_TYPE) && ($3 != 0)) param_active_type = IVL_VT_LOGIC; else param_active_type = $1; } | K_integer { param_active_range = make_range_from_width(integer_width); param_active_signed = true; param_active_type = IVL_VT_LOGIC; } | K_time { param_active_range = make_range_from_width(64); param_active_signed = false; param_active_type = IVL_VT_LOGIC; } | real_or_realtime { param_active_range = 0; param_active_signed = true; param_active_type = IVL_VT_REAL; } | atom2_type { param_active_range = make_range_from_width($1); param_active_signed = true; param_active_type = IVL_VT_BOOL; } ; /* parameter and localparam assignment lists are broken into separate BNF so that I can call slightly different parameter handling code. localparams parse the same as parameters, they just behave differently when someone tries to override them. */ parameter_assign_list : parameter_assign | parameter_assign_list ',' parameter_assign ; localparam_assign_list : localparam_assign | localparam_assign_list ',' localparam_assign ; parameter_assign : IDENTIFIER '=' expression parameter_value_ranges_opt { PExpr*tmp = $3; pform_set_parameter(@1, lex_strings.make($1), param_active_type, param_active_signed, param_active_range, tmp, $4); delete[]$1; } ; localparam_assign : IDENTIFIER '=' expression { PExpr*tmp = $3; pform_set_localparam(@1, lex_strings.make($1), param_active_type, param_active_signed, param_active_range, tmp); delete[]$1; } ; parameter_value_ranges_opt : parameter_value_ranges { $$ = $1; } | { $$ = 0; } ; parameter_value_ranges : parameter_value_ranges parameter_value_range { $$ = $2; $$->next = $1; } | parameter_value_range { $$ = $1; $$->next = 0; } ; parameter_value_range : from_exclude '[' value_range_expression ':' value_range_expression ']' { $$ = pform_parameter_value_range($1, false, $3, false, $5); } | from_exclude '[' value_range_expression ':' value_range_expression ')' { $$ = pform_parameter_value_range($1, false, $3, true, $5); } | from_exclude '(' value_range_expression ':' value_range_expression ']' { $$ = pform_parameter_value_range($1, true, $3, false, $5); } | from_exclude '(' value_range_expression ':' value_range_expression ')' { $$ = pform_parameter_value_range($1, true, $3, true, $5); } | K_exclude expression { $$ = pform_parameter_value_range(true, false, $2, false, $2); } ; value_range_expression : expression { $$ = $1; } | K_inf { $$ = 0; } | '+' K_inf { $$ = 0; } | '-' K_inf { $$ = 0; } ; from_exclude : K_from { $$ = false; } | K_exclude { $$ = true; } ; /* The parameters of a module instance can be overridden by writing a list of expressions in a syntax much like a delay list. (The difference being the list can have any length.) The pform that attaches the expression list to the module checks that the expressions are constant. Although the BNF in IEEE1364-1995 implies that parameter value lists must be in parentheses, in practice most compilers will accept simple expressions outside of parentheses if there is only one value, so I'll accept simple numbers here. This also catches the case of a UDP with a single delay value, so we need to accept real values as well as decimal ones. The parameter value by name syntax is OVI enhancement BTF-B06 as approved by WG1364 on 6/28/1998. */ parameter_value_opt : '#' '(' expression_list_with_nuls ')' { struct parmvalue_t*tmp = new struct parmvalue_t; tmp->by_order = $3; tmp->by_name = 0; $$ = tmp; } | '#' '(' parameter_value_byname_list ')' { struct parmvalue_t*tmp = new struct parmvalue_t; tmp->by_order = 0; tmp->by_name = $3; $$ = tmp; } | '#' DEC_NUMBER { assert($2); PENumber*tmp = new PENumber($2); FILE_NAME(tmp, @1); struct parmvalue_t*lst = new struct parmvalue_t; lst->by_order = new list; lst->by_order->push_back(tmp); lst->by_name = 0; $$ = lst; based_size = 0; } | '#' REALTIME { assert($2); PEFNumber*tmp = new PEFNumber($2); FILE_NAME(tmp, @1); struct parmvalue_t*lst = new struct parmvalue_t; lst->by_order = new list; lst->by_order->push_back(tmp); lst->by_name = 0; $$ = lst; } | '#' error { yyerror(@1, "error: syntax error in parameter value " "assignment list."); $$ = 0; } | { $$ = 0; } ; parameter_value_byname : '.' IDENTIFIER '(' expression ')' { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = $4; delete[]$2; $$ = tmp; } | '.' IDENTIFIER '(' ')' { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = 0; delete[]$2; $$ = tmp; } ; parameter_value_byname_list : parameter_value_byname { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } | parameter_value_byname_list ',' parameter_value_byname { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } ; /* The port (of a module) is a fairly complex item. Each port is handled as a Module::port_t object. A simple port reference has a name and a PExpr object, but more complex constructs are possible where the name can be attached to a list of PWire objects. The port_reference returns a Module::port_t, and so does the port_reference_list. The port_reference_list may have built up a list of PWires in the port_t object, but it is still a single Module::port_t object. The port rule below takes the built up Module::port_t object and tweaks its name as needed. */ port : port_reference { $$ = $1; } /* This syntax attaches an external name to the port reference so that the caller can bind by name to non-trivial port references. The port_t object gets its PWire from the port_reference, but its name from the IDENTIFIER. */ | '.' IDENTIFIER '(' port_reference ')' { Module::port_t*tmp = $4; tmp->name = lex_strings.make($2); delete[]$2; $$ = tmp; } /* A port can also be a concatenation of port references. In this case the port does not have a name available to the outside, only positional parameter passing is possible here. */ | '{' port_reference_list '}' { Module::port_t*tmp = $2; tmp->name = perm_string(); $$ = tmp; } /* This attaches a name to a port reference concatenation list so that parameter passing be name is possible. */ | '.' IDENTIFIER '(' '{' port_reference_list '}' ')' { Module::port_t*tmp = $5; tmp->name = lex_strings.make($2); delete[]$2; $$ = tmp; } ; port_opt : port { $$ = $1; } | { $$ = 0; } ; /* The port_name rule is used with a module is being *instantiated*, and not when it is being declared. See the port rule if you are looking for the ports of a module declaration. */ port_name : '.' IDENTIFIER '(' expression ')' { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = $4; delete[]$2; $$ = tmp; } | '.' IDENTIFIER '(' error ')' { yyerror(@3, "error: invalid port connection expression."); named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = 0; delete[]$2; $$ = tmp; } | '.' IDENTIFIER '(' ')' { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = 0; delete[]$2; $$ = tmp; } | '.' IDENTIFIER { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make($2); tmp->parm = new PEIdent(lex_strings.make($2), true); FILE_NAME(tmp->parm, @1); delete[]$2; $$ = tmp; } | K_DOTSTAR { named_pexpr_t*tmp = new named_pexpr_t; tmp->name = lex_strings.make("*"); tmp->parm = 0; $$ = tmp; } ; port_name_list : port_name_list ',' port_name { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } | port_name { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } ; /* A port reference is an internal (to the module) name of the port, possibly with a part of bit select to attach it to specific bits of a signal fully declared inside the module. The parser creates a PEIdent for every port reference, even if the signal is bound to different ports. The elaboration figures out the mess that this creates. The port_reference (and the port_reference_list below) puts the port reference PEIdent into the port_t object to pass it up to the module declaration code. */ port_reference : IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($1); ptmp = pform_module_port_reference(name, @1.text, @1.first_line); delete[]$1; $$ = ptmp; } | IDENTIFIER '[' expression ':' expression ']' { index_component_t itmp; itmp.sel = index_component_t::SEL_PART; itmp.msb = $3; itmp.lsb = $5; name_component_t ntmp (lex_strings.make($1)); ntmp.index.push_back(itmp); pform_name_t pname; pname.push_back(ntmp); PEIdent*wtmp = new PEIdent(pname); FILE_NAME(wtmp, @1); Module::port_t*ptmp = new Module::port_t; ptmp->name = perm_string(); ptmp->expr.push_back(wtmp); delete[]$1; $$ = ptmp; } | IDENTIFIER '[' expression ']' { index_component_t itmp; itmp.sel = index_component_t::SEL_BIT; itmp.msb = $3; itmp.lsb = 0; name_component_t ntmp (lex_strings.make($1)); ntmp.index.push_back(itmp); pform_name_t pname; pname.push_back(ntmp); PEIdent*tmp = new PEIdent(pname); FILE_NAME(tmp, @1); Module::port_t*ptmp = new Module::port_t; ptmp->name = perm_string(); ptmp->expr.push_back(tmp); delete[]$1; $$ = ptmp; } | IDENTIFIER '[' error ']' { yyerror(@1, "error: invalid port bit select"); Module::port_t*ptmp = new Module::port_t; PEIdent*wtmp = new PEIdent(lex_strings.make($1)); FILE_NAME(wtmp, @1); ptmp->name = lex_strings.make($1); ptmp->expr.push_back(wtmp); delete[]$1; $$ = ptmp; } ; port_reference_list : port_reference { $$ = $1; } | port_reference_list ',' port_reference { Module::port_t*tmp = $1; append(tmp->expr, $3->expr); delete $3; $$ = tmp; } ; /* The range is a list of variable dimensions. */ dimensions_opt : { $$ = 0; } | dimensions { $$ = $1; } ; dimensions : variable_dimension { $$ = $1; } | dimensions variable_dimension { list *tmp = $1; if ($2) { tmp->splice(tmp->end(), *$2); delete $2; } $$ = tmp; } ; /* The register_variable rule is matched only when I am parsing variables in a "reg" definition. I therefore know that I am creating registers and I do not need to let the containing rule handle it. The register variable list simply packs them together so that bit ranges can be assigned. */ register_variable : IDENTIFIER dimensions_opt { perm_string name = lex_strings.make($1); pform_makewire(@1, name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); pform_set_reg_idx(name, $2); $$ = $1; } | IDENTIFIER dimensions_opt '=' expression { perm_string name = lex_strings.make($1); pform_makewire(@1, name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); pform_set_reg_idx(name, $2); pform_make_reginit(@1, name, $4); $$ = $1; } ; register_variable_list : register_variable { list*tmp = new list; tmp->push_back(lex_strings.make($1)); $$ = tmp; delete[]$1; } | register_variable_list ',' register_variable { list*tmp = $1; tmp->push_back(lex_strings.make($3)); $$ = tmp; delete[]$3; } ; net_variable : IDENTIFIER dimensions_opt { perm_string name = lex_strings.make($1); pform_makewire(@1, name, NetNet::IMPLICIT, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); pform_set_reg_idx(name, $2); $$ = $1; } ; net_variable_list : net_variable { list*tmp = new list; tmp->push_back(lex_strings.make($1)); $$ = tmp; delete[]$1; } | net_variable_list ',' net_variable { list*tmp = $1; tmp->push_back(lex_strings.make($3)); $$ = tmp; delete[]$3; } ; event_variable : IDENTIFIER dimensions_opt { if ($2) { yyerror(@2, "sorry: event arrays are not supported."); delete $2; } $$ = $1; } ; event_variable_list : event_variable { $$ = list_from_identifier($1); } | event_variable_list ',' event_variable { $$ = list_from_identifier($1, $3); } ; specify_item : K_specparam specparam_decl ';' | specify_simple_path_decl ';' { pform_module_specify_path($1); } | specify_edge_path_decl ';' { pform_module_specify_path($1); } | K_if '(' expression ')' specify_simple_path_decl ';' { PSpecPath*tmp = $5; if (tmp) { tmp->conditional = true; tmp->condition = $3; } pform_module_specify_path(tmp); } | K_if '(' expression ')' specify_edge_path_decl ';' { PSpecPath*tmp = $5; if (tmp) { tmp->conditional = true; tmp->condition = $3; } pform_module_specify_path(tmp); } | K_ifnone specify_simple_path_decl ';' { PSpecPath*tmp = $2; if (tmp) { tmp->conditional = true; tmp->condition = 0; } pform_module_specify_path(tmp); } | K_ifnone specify_edge_path_decl ';' { yyerror(@1, "Sorry: ifnone with an edge-sensitive path is " "not supported."); yyerrok; } | K_Sfullskew '(' spec_reference_event ',' spec_reference_event ',' delay_value ',' delay_value spec_notifier_opt ')' ';' { delete $7; delete $9; } | K_Shold '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $7; } | K_Snochange '(' spec_reference_event ',' spec_reference_event ',' delay_value ',' delay_value spec_notifier_opt ')' ';' { delete $7; delete $9; } | K_Speriod '(' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $5; } | K_Srecovery '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $7; } | K_Srecrem '(' spec_reference_event ',' spec_reference_event ',' delay_value ',' delay_value spec_notifier_opt ')' ';' { delete $7; delete $9; } | K_Sremoval '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $7; } | K_Ssetup '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $7; } | K_Ssetuphold '(' spec_reference_event ',' spec_reference_event ',' delay_value ',' delay_value spec_notifier_opt ')' ';' { delete $7; delete $9; } | K_Sskew '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $7; } | K_Stimeskew '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' { delete $7; } | K_Swidth '(' spec_reference_event ',' delay_value ',' expression spec_notifier_opt ')' ';' { delete $5; delete $7; } | K_Swidth '(' spec_reference_event ',' delay_value ')' ';' { delete $5; } | K_pulsestyle_onevent specify_path_identifiers ';' { delete $2; } | K_pulsestyle_ondetect specify_path_identifiers ';' { delete $2; } | K_showcancelled specify_path_identifiers ';' { delete $2; } | K_noshowcancelled specify_path_identifiers ';' { delete $2; } ; specify_item_list : specify_item | specify_item_list specify_item ; specify_item_list_opt : /* empty */ { } | specify_item_list { } specify_edge_path_decl : specify_edge_path '=' '(' delay_value_list ')' { $$ = pform_assign_path_delay($1, $4); } | specify_edge_path '=' delay_value_simple { list*tmp = new list; tmp->push_back($3); $$ = pform_assign_path_delay($1, tmp); } ; edge_operator : K_posedge { $$ = true; } | K_negedge { $$ = false; } ; specify_edge_path : '(' specify_path_identifiers spec_polarity K_EG '(' specify_path_identifiers polarity_operator expression ')' ')' { int edge_flag = 0; $$ = pform_make_specify_edge_path(@1, edge_flag, $2, $3, false, $6, $8); } | '(' edge_operator specify_path_identifiers spec_polarity K_EG '(' specify_path_identifiers polarity_operator expression ')' ')' { int edge_flag = $2? 1 : -1; $$ = pform_make_specify_edge_path(@1, edge_flag, $3, $4, false, $7, $9);} | '(' specify_path_identifiers spec_polarity K_SG '(' specify_path_identifiers polarity_operator expression ')' ')' { int edge_flag = 0; $$ = pform_make_specify_edge_path(@1, edge_flag, $2, $3, true, $6, $8); } | '(' edge_operator specify_path_identifiers spec_polarity K_SG '(' specify_path_identifiers polarity_operator expression ')' ')' { int edge_flag = $2? 1 : -1; $$ = pform_make_specify_edge_path(@1, edge_flag, $3, $4, true, $7, $9); } ; polarity_operator : K_PO_POS | K_PO_NEG | ':' ; specify_simple_path_decl : specify_simple_path '=' '(' delay_value_list ')' { $$ = pform_assign_path_delay($1, $4); } | specify_simple_path '=' delay_value_simple { list*tmp = new list; tmp->push_back($3); $$ = pform_assign_path_delay($1, tmp); } | specify_simple_path '=' '(' error ')' { yyerror(@3, "Syntax error in delay value list."); yyerrok; $$ = 0; } ; specify_simple_path : '(' specify_path_identifiers spec_polarity K_EG specify_path_identifiers ')' { $$ = pform_make_specify_path(@1, $2, $3, false, $5); } | '(' specify_path_identifiers spec_polarity K_SG specify_path_identifiers ')' { $$ = pform_make_specify_path(@1, $2, $3, true, $5); } | '(' error ')' { yyerror(@1, "Invalid simple path"); yyerrok; } ; specify_path_identifiers : IDENTIFIER { list*tmp = new list; tmp->push_back(lex_strings.make($1)); $$ = tmp; delete[]$1; } | IDENTIFIER '[' expr_primary ']' { list*tmp = new list; tmp->push_back(lex_strings.make($1)); $$ = tmp; delete[]$1; } | specify_path_identifiers ',' IDENTIFIER { list*tmp = $1; tmp->push_back(lex_strings.make($3)); $$ = tmp; delete[]$3; } | specify_path_identifiers ',' IDENTIFIER '[' expr_primary ']' { list*tmp = $1; tmp->push_back(lex_strings.make($3)); $$ = tmp; delete[]$3; } ; specparam : IDENTIFIER '=' expression { PExpr*tmp = $3; pform_set_specparam(@1, lex_strings.make($1), param_active_range, tmp); delete[]$1; } | IDENTIFIER '=' expression ':' expression ':' expression { PExpr*tmp = 0; switch (min_typ_max_flag) { case MIN: tmp = $3; delete $5; delete $7; break; case TYP: delete $3; tmp = $5; delete $7; break; case MAX: delete $3; delete $5; tmp = $7; break; } if (min_typ_max_warn > 0) { cerr << tmp->get_fileline() << ": warning: choosing "; switch (min_typ_max_flag) { case MIN: cerr << "min"; break; case TYP: cerr << "typ"; break; case MAX: cerr << "max"; break; } cerr << " expression." << endl; min_typ_max_warn -= 1; } pform_set_specparam(@1, lex_strings.make($1), param_active_range, tmp); delete[]$1; } | PATHPULSE_IDENTIFIER '=' expression { delete[]$1; delete $3; } | PATHPULSE_IDENTIFIER '=' '(' expression ',' expression ')' { delete[]$1; delete $4; delete $6; } ; specparam_list : specparam | specparam_list ',' specparam ; specparam_decl : specparam_list | dimensions { param_active_range = $1; } specparam_list { param_active_range = 0; } ; spec_polarity : '+' { $$ = '+'; } | '-' { $$ = '-'; } | { $$ = 0; } ; spec_reference_event : K_posedge expression { delete $2; } | K_negedge expression { delete $2; } | K_posedge expr_primary K_TAND expression { delete $2; delete $4; } | K_negedge expr_primary K_TAND expression { delete $2; delete $4; } | K_edge '[' edge_descriptor_list ']' expr_primary { delete $5; } | K_edge '[' edge_descriptor_list ']' expr_primary K_TAND expression { delete $5; delete $7; } | expr_primary K_TAND expression { delete $1; delete $3; } | expr_primary { delete $1; } ; /* The edge_descriptor is detected by the lexor as the various 2-letter edge sequences that are supported here. For now, we don't care what they are, because we do not yet support specify edge events. */ edge_descriptor_list : edge_descriptor_list ',' K_edge_descriptor | K_edge_descriptor ; spec_notifier_opt : /* empty */ { } | spec_notifier { } ; spec_notifier : ',' { args_after_notifier = 0; } | ',' hierarchy_identifier { args_after_notifier = 0; delete $2; } | spec_notifier ',' { args_after_notifier += 1; } | spec_notifier ',' hierarchy_identifier { args_after_notifier += 1; if (args_after_notifier >= 3) { cerr << @3 << ": warning: timing checks are not supported " "and delayed signal \"" << *$3 << "\" will not be driven." << endl; } delete $3; } /* How do we match this path? */ | IDENTIFIER { args_after_notifier = 0; delete[]$1; } ; statement_item /* This is roughly statement_item in the LRM */ /* assign and deassign statements are procedural code to do structural assignments, and to turn that structural assignment off. This is stronger than any other assign, but weaker than the force assignments. */ : K_assign lpvalue '=' expression ';' { PCAssign*tmp = new PCAssign($2, $4); FILE_NAME(tmp, @1); $$ = tmp; } | K_deassign lpvalue ';' { PDeassign*tmp = new PDeassign($2); FILE_NAME(tmp, @1); $$ = tmp; } /* Force and release statements are similar to assignments, syntactically, but they will be elaborated differently. */ | K_force lpvalue '=' expression ';' { PForce*tmp = new PForce($2, $4); FILE_NAME(tmp, @1); $$ = tmp; } | K_release lpvalue ';' { PRelease*tmp = new PRelease($2); FILE_NAME(tmp, @1); $$ = tmp; } /* begin-end blocks come in a variety of forms, including named and anonymous. The named blocks can also carry their own reg variables, which are placed in the scope created by the block name. These are handled by pushing the scope name, then matching the declarations. The scope is popped at the end of the block. */ | K_begin K_end { PBlock*tmp = new PBlock(PBlock::BL_SEQ); FILE_NAME(tmp, @1); $$ = tmp; } /* In SystemVerilog an unnamed block can contain variable declarations. */ | K_begin { PBlock*tmp = pform_push_block_scope(0, PBlock::BL_SEQ); FILE_NAME(tmp, @1); current_block_stack.push(tmp); } block_item_decls_opt { if ($3) { if (! gn_system_verilog()) { yyerror("error: Variable declaration in unnamed block " "requires SystemVerilog."); } } else { /* If there are no declarations in the scope then just delete it. */ pform_pop_scope(); assert(! current_block_stack.empty()); PBlock*tmp = current_block_stack.top(); current_block_stack.pop(); delete tmp; } } statement_or_null_list K_end { PBlock*tmp; if ($3) { pform_pop_scope(); assert(! current_block_stack.empty()); tmp = current_block_stack.top(); current_block_stack.pop(); } else { tmp = new PBlock(PBlock::BL_SEQ); FILE_NAME(tmp, @1); } if ($5) tmp->set_statement(*$5); delete $5; $$ = tmp; } | K_begin ':' IDENTIFIER { PBlock*tmp = pform_push_block_scope($3, PBlock::BL_SEQ); FILE_NAME(tmp, @1); current_block_stack.push(tmp); } block_item_decls_opt statement_or_null_list_opt K_end endlabel_opt { pform_pop_scope(); assert(! current_block_stack.empty()); PBlock*tmp = current_block_stack.top(); current_block_stack.pop(); if ($6) tmp->set_statement(*$6); delete $6; if ($8) { if (strcmp($3,$8) != 0) { yyerror(@8, "error: End label doesn't match begin name"); } if (! gn_system_verilog()) { yyerror(@8, "error: Begin end labels require " "SystemVerilog."); } delete[]$8; } delete[]$3; $$ = tmp; } /* fork-join blocks are very similar to begin-end blocks. In fact, from the parser's perspective there is no real difference. All we need to do is remember that this is a parallel block so that the code generator can do the right thing. */ | K_fork join_keyword { PBlock*tmp = new PBlock($2); FILE_NAME(tmp, @1); $$ = tmp; } /* In SystemVerilog an unnamed block can contain variable declarations. */ | K_fork { PBlock*tmp = pform_push_block_scope(0, PBlock::BL_PAR); FILE_NAME(tmp, @1); current_block_stack.push(tmp); } block_item_decls_opt { if ($3) { if (! gn_system_verilog()) { yyerror("error: Variable declaration in unnamed block " "requires SystemVerilog."); } } else { /* If there are no declarations in the scope then just delete it. */ pform_pop_scope(); assert(! current_block_stack.empty()); PBlock*tmp = current_block_stack.top(); current_block_stack.pop(); delete tmp; } } statement_or_null_list join_keyword { PBlock*tmp; if ($3) { pform_pop_scope(); assert(! current_block_stack.empty()); tmp = current_block_stack.top(); current_block_stack.pop(); tmp->set_join_type($6); } else { tmp = new PBlock($6); FILE_NAME(tmp, @1); } if ($5) tmp->set_statement(*$5); delete $5; $$ = tmp; } | K_fork ':' IDENTIFIER { PBlock*tmp = pform_push_block_scope($3, PBlock::BL_PAR); FILE_NAME(tmp, @1); current_block_stack.push(tmp); } block_item_decls_opt statement_or_null_list_opt join_keyword endlabel_opt { pform_pop_scope(); assert(! current_block_stack.empty()); PBlock*tmp = current_block_stack.top(); current_block_stack.pop(); tmp->set_join_type($7); if ($6) tmp->set_statement(*$6); delete $6; if ($8) { if (strcmp($3,$8) != 0) { yyerror(@8, "error: End label doesn't match fork name"); } if (! gn_system_verilog()) { yyerror(@8, "error: Fork end labels require " "SystemVerilog."); } delete[]$8; } delete[]$3; $$ = tmp; } | K_disable hierarchy_identifier ';' { PDisable*tmp = new PDisable(*$2); FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | K_disable K_fork ';' { pform_name_t tmp_name; PDisable*tmp = new PDisable(tmp_name); FILE_NAME(tmp, @1); $$ = tmp; } | K_TRIGGER hierarchy_identifier ';' { PTrigger*tmp = new PTrigger(*$2); FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | loop_statement { $$ = $1; } | jump_statement { $$ = $1; } | K_case '(' expression ')' case_items K_endcase { PCase*tmp = new PCase(NetCase::EQ, $3, $5); FILE_NAME(tmp, @1); $$ = tmp; } | K_casex '(' expression ')' case_items K_endcase { PCase*tmp = new PCase(NetCase::EQX, $3, $5); FILE_NAME(tmp, @1); $$ = tmp; } | K_casez '(' expression ')' case_items K_endcase { PCase*tmp = new PCase(NetCase::EQZ, $3, $5); FILE_NAME(tmp, @1); $$ = tmp; } | K_case '(' expression ')' error K_endcase { yyerrok; } | K_casex '(' expression ')' error K_endcase { yyerrok; } | K_casez '(' expression ')' error K_endcase { yyerrok; } | K_if '(' expression ')' statement_or_null %prec less_than_K_else { PCondit*tmp = new PCondit($3, $5, 0); FILE_NAME(tmp, @1); $$ = tmp; } | K_if '(' expression ')' statement_or_null K_else statement_or_null { PCondit*tmp = new PCondit($3, $5, $7); FILE_NAME(tmp, @1); $$ = tmp; } | K_if '(' error ')' statement_or_null %prec less_than_K_else { yyerror(@1, "error: Malformed conditional expression."); $$ = $5; } | K_if '(' error ')' statement_or_null K_else statement_or_null { yyerror(@1, "error: Malformed conditional expression."); $$ = $5; } /* SystemVerilog adds the compressed_statement */ | compressed_statement ';' { $$ = $1; } /* increment/decrement expressions can also be statements. When used as statements, we can rewrite a++ as a += 1, and so on. */ | inc_or_dec_expression ';' { $$ = pform_compressed_assign_from_inc_dec(@1, $1); } /* */ | delay1 statement_or_null { PExpr*del = $1->front(); assert($1->size() == 1); delete $1; PDelayStatement*tmp = new PDelayStatement(del, $2); FILE_NAME(tmp, @1); $$ = tmp; } | event_control statement_or_null { PEventStatement*tmp = $1; if (tmp == 0) { yyerror(@1, "error: Invalid event control."); $$ = 0; } else { tmp->set_statement($2); $$ = tmp; } } | '@' '*' statement_or_null { PEventStatement*tmp = new PEventStatement; FILE_NAME(tmp, @1); tmp->set_statement($3); $$ = tmp; } | '@' '(' '*' ')' statement_or_null { PEventStatement*tmp = new PEventStatement; FILE_NAME(tmp, @1); tmp->set_statement($5); $$ = tmp; } /* Various assignment statements */ | lpvalue '=' expression ';' { PAssign*tmp = new PAssign($1,$3); FILE_NAME(tmp, @1); $$ = tmp; } | error '=' expression ';' { yyerror(@2, "Syntax in assignment statement l-value."); yyerrok; $$ = new PNoop; } | lpvalue K_LE expression ';' { PAssignNB*tmp = new PAssignNB($1,$3); FILE_NAME(tmp, @1); $$ = tmp; } | error K_LE expression ';' { yyerror(@2, "Syntax in assignment statement l-value."); yyerrok; $$ = new PNoop; } | lpvalue '=' delay1 expression ';' { PExpr*del = $3->front(); $3->pop_front(); assert($3->empty()); PAssign*tmp = new PAssign($1,del,$4); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_LE delay1 expression ';' { PExpr*del = $3->front(); $3->pop_front(); assert($3->empty()); PAssignNB*tmp = new PAssignNB($1,del,$4); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue '=' event_control expression ';' { PAssign*tmp = new PAssign($1,0,$3,$4); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue '=' K_repeat '(' expression ')' event_control expression ';' { PAssign*tmp = new PAssign($1,$5,$7,$8); FILE_NAME(tmp,@1); tmp->set_lineno(@1.first_line); $$ = tmp; } | lpvalue K_LE event_control expression ';' { PAssignNB*tmp = new PAssignNB($1,0,$3,$4); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_LE K_repeat '(' expression ')' event_control expression ';' { PAssignNB*tmp = new PAssignNB($1,$5,$7,$8); FILE_NAME(tmp, @1); $$ = tmp; } /* The IEEE1800 standard defines dynamic_array_new assignment as a different rule from regular assignment. That implies that the dynamic_array_new is not an expression in general, which makes some sense. Elaboration should make sure the lpvalue is an array name. */ | lpvalue '=' dynamic_array_new ';' { PAssign*tmp = new PAssign($1,$3); FILE_NAME(tmp, @1); $$ = tmp; } /* The class new and dynamic array new expressions are special, so sit in rules of their own. */ | lpvalue '=' class_new ';' { PAssign*tmp = new PAssign($1,$3); FILE_NAME(tmp, @1); $$ = tmp; } | K_wait '(' expression ')' statement_or_null { PEventStatement*tmp; PEEvent*etmp = new PEEvent(PEEvent::POSITIVE, $3); tmp = new PEventStatement(etmp); FILE_NAME(tmp,@1); tmp->set_statement($5); $$ = tmp; } | K_wait K_fork ';' { PEventStatement*tmp = new PEventStatement(0); FILE_NAME(tmp,@1); $$ = tmp; } | SYSTEM_IDENTIFIER '(' expression_list_with_nuls ')' ';' { PCallTask*tmp = new PCallTask(lex_strings.make($1), *$3); FILE_NAME(tmp,@1); delete[]$1; delete $3; $$ = tmp; } | SYSTEM_IDENTIFIER ';' { listpt; PCallTask*tmp = new PCallTask(lex_strings.make($1), pt); FILE_NAME(tmp,@1); delete[]$1; $$ = tmp; } | hierarchy_identifier '(' expression_list_with_nuls ')' ';' { PCallTask*tmp = pform_make_call_task(@1, *$1, *$3); delete $1; delete $3; $$ = tmp; } | hierarchy_identifier K_with '{' constraint_block_item_list_opt '}' ';' { /* ....randomize with { } */ if ($1 && peek_tail_name(*$1) == "randomize") { if (!gn_system_verilog()) yyerror(@2, "error: Randomize with constraint requires SystemVerilog."); else yyerror(@2, "sorry: Randomize with constraint not supported."); } else { yyerror(@2, "error: Constraint block can only be applied to randomize method."); } listpt; PCallTask*tmp = new PCallTask(*$1, pt); FILE_NAME(tmp, @1); delete $1; $$ = tmp; } | implicit_class_handle '.' hierarchy_identifier '(' expression_list_with_nuls ')' ';' { pform_name_t*t_name = $1; while (! $3->empty()) { t_name->push_back($3->front()); $3->pop_front(); } PCallTask*tmp = new PCallTask(*t_name, *$5); FILE_NAME(tmp, @1); delete $1; delete $3; delete $5; $$ = tmp; } | hierarchy_identifier ';' { listpt; PCallTask*tmp = pform_make_call_task(@1, *$1, pt); delete $1; $$ = tmp; } /* IEEE1800 A.1.8: class_constructor_declaration with a call to parent constructor. Note that the implicit_class_handle must be K_super ("this.new" makes little sense) but that would cause a conflict. Anyhow, this statement must be in the beginning of a constructor, but let the elaborator figure that out. */ | implicit_class_handle '.' K_new '(' expression_list_with_nuls ')' ';' { PChainConstructor*tmp = new PChainConstructor(*$5); FILE_NAME(tmp, @3); delete $1; $$ = tmp; } | hierarchy_identifier '(' error ')' ';' { yyerror(@3, "error: Syntax error in task arguments."); listpt; PCallTask*tmp = pform_make_call_task(@1, *$1, pt); delete $1; $$ = tmp; } | error ';' { yyerror(@2, "error: malformed statement"); yyerrok; $$ = new PNoop; } ; compressed_statement : lpvalue K_PLUS_EQ expression { PAssign*tmp = new PAssign($1, '+', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_MINUS_EQ expression { PAssign*tmp = new PAssign($1, '-', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_MUL_EQ expression { PAssign*tmp = new PAssign($1, '*', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_DIV_EQ expression { PAssign*tmp = new PAssign($1, '/', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_MOD_EQ expression { PAssign*tmp = new PAssign($1, '%', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_AND_EQ expression { PAssign*tmp = new PAssign($1, '&', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_OR_EQ expression { PAssign*tmp = new PAssign($1, '|', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_XOR_EQ expression { PAssign*tmp = new PAssign($1, '^', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_LS_EQ expression { PAssign *tmp = new PAssign($1, 'l', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_RS_EQ expression { PAssign*tmp = new PAssign($1, 'r', $3); FILE_NAME(tmp, @1); $$ = tmp; } | lpvalue K_RSS_EQ expression { PAssign *tmp = new PAssign($1, 'R', $3); FILE_NAME(tmp, @1); $$ = tmp; } ; statement_or_null_list_opt : statement_or_null_list { $$ = $1; } | { $$ = 0; } ; statement_or_null_list : statement_or_null_list statement_or_null { vector*tmp = $1; if ($2) tmp->push_back($2); $$ = tmp; } | statement_or_null { vector*tmp = new vector(0); if ($1) tmp->push_back($1); $$ = tmp; } ; analog_statement : branch_probe_expression K_CONTRIBUTE expression ';' { $$ = pform_contribution_statement(@2, $1, $3); } ; /* Task items are, other than the statement, task port items and other block items. */ task_item : block_item_decl { $$ = new vector(0); } | tf_port_declaration { $$ = $1; } ; task_item_list : task_item_list task_item { vector*tmp = $1; size_t s1 = tmp->size(); tmp->resize(s1 + $2->size()); for (size_t idx = 0 ; idx < $2->size() ; idx += 1) tmp->at(s1 + idx) = $2->at(idx); delete $2; $$ = tmp; } | task_item { $$ = $1; } ; task_item_list_opt : task_item_list { $$ = $1; } | { $$ = 0; } ; tf_port_list_opt : tf_port_list { $$ = $1; } | { $$ = 0; } ; /* Note that the lexor notices the "table" keyword and starts the UDPTABLE state. It needs to happen there so that all the characters in the table are interpreted in that mode. It is still up to this rule to take us out of the UDPTABLE state. */ udp_body : K_table udp_entry_list K_endtable { lex_end_table(); $$ = $2; } | K_table K_endtable { lex_end_table(); yyerror(@1, "error: Empty UDP table."); $$ = 0; } | K_table error K_endtable { lex_end_table(); yyerror(@2, "Errors in UDP table"); yyerrok; $$ = 0; } ; udp_entry_list : udp_comb_entry_list | udp_sequ_entry_list ; udp_comb_entry : udp_input_list ':' udp_output_sym ';' { char*tmp = new char[strlen($1)+3]; strcpy(tmp, $1); char*tp = tmp+strlen(tmp); *tp++ = ':'; *tp++ = $3; *tp++ = 0; delete[]$1; $$ = tmp; } ; udp_comb_entry_list : udp_comb_entry { list*tmp = new list; tmp->push_back($1); delete[]$1; $$ = tmp; } | udp_comb_entry_list udp_comb_entry { list*tmp = $1; tmp->push_back($2); delete[]$2; $$ = tmp; } ; udp_sequ_entry_list : udp_sequ_entry { list*tmp = new list; tmp->push_back($1); delete[]$1; $$ = tmp; } | udp_sequ_entry_list udp_sequ_entry { list*tmp = $1; tmp->push_back($2); delete[]$2; $$ = tmp; } ; udp_sequ_entry : udp_input_list ':' udp_input_sym ':' udp_output_sym ';' { char*tmp = new char[strlen($1)+5]; strcpy(tmp, $1); char*tp = tmp+strlen(tmp); *tp++ = ':'; *tp++ = $3; *tp++ = ':'; *tp++ = $5; *tp++ = 0; $$ = tmp; } ; udp_initial : K_initial IDENTIFIER '=' number ';' { PExpr*etmp = new PENumber($4); PEIdent*itmp = new PEIdent(lex_strings.make($2)); PAssign*atmp = new PAssign(itmp, etmp); FILE_NAME(atmp, @2); delete[]$2; $$ = atmp; } ; udp_init_opt : udp_initial { $$ = $1; } | { $$ = 0; } ; udp_input_list : udp_input_sym { char*tmp = new char[2]; tmp[0] = $1; tmp[1] = 0; $$ = tmp; } | udp_input_list udp_input_sym { char*tmp = new char[strlen($1)+2]; strcpy(tmp, $1); char*tp = tmp+strlen(tmp); *tp++ = $2; *tp++ = 0; delete[]$1; $$ = tmp; } ; udp_input_sym : '0' { $$ = '0'; } | '1' { $$ = '1'; } | 'x' { $$ = 'x'; } | '?' { $$ = '?'; } | 'b' { $$ = 'b'; } | '*' { $$ = '*'; } | '%' { $$ = '%'; } | 'f' { $$ = 'f'; } | 'F' { $$ = 'F'; } | 'l' { $$ = 'l'; } | 'h' { $$ = 'h'; } | 'B' { $$ = 'B'; } | 'r' { $$ = 'r'; } | 'R' { $$ = 'R'; } | 'M' { $$ = 'M'; } | 'n' { $$ = 'n'; } | 'N' { $$ = 'N'; } | 'p' { $$ = 'p'; } | 'P' { $$ = 'P'; } | 'Q' { $$ = 'Q'; } | 'q' { $$ = 'q'; } | '_' { $$ = '_'; } | '+' { $$ = '+'; } | DEC_NUMBER { yyerror(@1, "internal error: Input digits parse as decimal number!"); $$ = '0'; } ; udp_output_sym : '0' { $$ = '0'; } | '1' { $$ = '1'; } | 'x' { $$ = 'x'; } | '-' { $$ = '-'; } | DEC_NUMBER { yyerror(@1, "internal error: Output digits parse as decimal number!"); $$ = '0'; } ; /* Port declarations create wires for the inputs and the output. The makes for these ports are scoped within the UDP, so there is no hierarchy involved. */ udp_port_decl : K_input list_of_identifiers ';' { $$ = pform_make_udp_input_ports($2); } | K_output IDENTIFIER ';' { perm_string pname = lex_strings.make($2); PWire*pp = new PWire(pname, NetNet::IMPLICIT, NetNet::POUTPUT, IVL_VT_LOGIC); vector*tmp = new vector(1); (*tmp)[0] = pp; $$ = tmp; delete[]$2; } | K_reg IDENTIFIER ';' { perm_string pname = lex_strings.make($2); PWire*pp = new PWire(pname, NetNet::REG, NetNet::PIMPLICIT, IVL_VT_LOGIC); vector*tmp = new vector(1); (*tmp)[0] = pp; $$ = tmp; delete[]$2; } | K_reg K_output IDENTIFIER ';' { perm_string pname = lex_strings.make($3); PWire*pp = new PWire(pname, NetNet::REG, NetNet::POUTPUT, IVL_VT_LOGIC); vector*tmp = new vector(1); (*tmp)[0] = pp; $$ = tmp; delete[]$3; } ; udp_port_decls : udp_port_decl { $$ = $1; } | udp_port_decls udp_port_decl { vector*tmp = $1; size_t s1 = $1->size(); tmp->resize(s1+$2->size()); for (size_t idx = 0 ; idx < $2->size() ; idx += 1) tmp->at(s1+idx) = $2->at(idx); $$ = tmp; delete $2; } ; udp_port_list : IDENTIFIER { list*tmp = new list; tmp->push_back(lex_strings.make($1)); delete[]$1; $$ = tmp; } | udp_port_list ',' IDENTIFIER { list*tmp = $1; tmp->push_back(lex_strings.make($3)); delete[]$3; $$ = tmp; } ; udp_reg_opt: K_reg { $$ = true; } | { $$ = false; }; udp_initial_expr_opt : '=' expression { $$ = $2; } | { $$ = 0; } ; udp_input_declaration_list : K_input IDENTIFIER { list*tmp = new list; tmp->push_back(lex_strings.make($2)); $$ = tmp; delete[]$2; } | udp_input_declaration_list ',' K_input IDENTIFIER { list*tmp = $1; tmp->push_back(lex_strings.make($4)); $$ = tmp; delete[]$4; } ; udp_primitive /* This is the syntax for primitives that uses the IEEE1364-1995 format. The ports are simply names in the port list, and the declarations are in the body. */ : K_primitive IDENTIFIER '(' udp_port_list ')' ';' udp_port_decls udp_init_opt udp_body K_endprimitive endlabel_opt { perm_string tmp2 = lex_strings.make($2); pform_make_udp(tmp2, $4, $7, $9, $8, @2.text, @2.first_line); if ($11) { if (strcmp($2,$11) != 0) { yyerror(@11, "error: End label doesn't match " "primitive name"); } if (! gn_system_verilog()) { yyerror(@11, "error: Primitive end labels " "require SystemVerilog."); } delete[]$11; } delete[]$2; } /* This is the syntax for IEEE1364-2001 format definitions. The port names and declarations are all in the parameter list. */ | K_primitive IDENTIFIER '(' K_output udp_reg_opt IDENTIFIER udp_initial_expr_opt ',' udp_input_declaration_list ')' ';' udp_body K_endprimitive endlabel_opt { perm_string tmp2 = lex_strings.make($2); perm_string tmp6 = lex_strings.make($6); pform_make_udp(tmp2, $5, tmp6, $7, $9, $12, @2.text, @2.first_line); if ($14) { if (strcmp($2,$14) != 0) { yyerror(@14, "error: End label doesn't match " "primitive name"); } if (! gn_system_verilog()) { yyerror(@14, "error: Primitive end labels " "require SystemVerilog."); } delete[]$14; } delete[]$2; delete[]$6; } ; /* Many keywords can be optional in the syntax, although their presence is significant. This is a fairly common pattern so collect those rules here. */ K_automatic_opt: K_automatic { $$ = true; } | { $$ = false; } ; K_packed_opt : K_packed { $$ = true; } | { $$ = false; } ; K_reg_opt : K_reg { $$ = true; } | { $$ = false; } ; K_static_opt : K_static { $$ = true; } | { $$ = false; } ; K_virtual_opt : K_virtual { $$ = true; } | { $$ = false; } ; iverilog-10_1/parse_api.h000066400000000000000000000051511265551621300154600ustar00rootroot00000000000000#ifndef IVL_parse_api_H #define IVL_parse_api_H /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "StringHeap.h" # include # include # include # include class Design; class Module; class PClass; class PPackage; class PTaskFunc; class PUdp; class data_type_t; struct enum_type_t; /* * These are maps of the modules and primitives parsed from the * Verilog source into pform for elaboration. The parser adds modules * to these maps as it compiles modules in the Verilog source. */ extern std::map pform_modules; extern std::map pform_primitives; extern std::map pform_typedefs; extern std::set pform_enum_sets; extern std::map pform_tasks; extern std::map pform_classes; extern std::map pform_packages; extern void pform_dump(std::ostream&out, const PClass*pac); extern void pform_dump(std::ostream&out, const PPackage*pac); extern void pform_dump(std::ostream&out, const PTaskFunc*tf); extern void elaborate_rootscope_enumerations(Design*des); extern void elaborate_rootscope_classes(Design*des); extern void elaborate_rootscope_tasks(Design*des); /* * This code actually invokes the parser to make modules. The first * parameter is the name of the file that is to be parsed. The * optional second parameter is the opened descriptor for the file. If * the descriptor is 0 (or skipped) then the function will attempt to * open the file on its own. */ extern int pform_parse(const char*path, FILE*file =0); extern string vl_file; extern void pform_set_timescale(int units, int prec, const char*file, unsigned lineno); extern int def_ts_units; extern int def_ts_prec; #endif /* IVL_parse_api_H */ iverilog-10_1/parse_misc.cc000066400000000000000000000035561265551621300160070ustar00rootroot00000000000000/* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "parse_misc.h" # include # include # include extern const char*vl_file; unsigned error_count = 0; unsigned warn_count = 0; unsigned long based_size = 0; std::ostream& operator << (std::ostream&o, const YYLTYPE&loc) { if (loc.text) o << loc.text << ":"; else o << "<>:"; o << loc.first_line; return o; } void VLerror(const char*msg) { error_count += 1; cerr << yylloc.text << ":" << yylloc.first_line << ": " << msg << endl; } void VLerror(const YYLTYPE&loc, const char*msg, ...) { va_list ap; va_start(ap, msg); fprintf(stderr, "%s:%d: ", loc.text, loc.first_line); vfprintf(stderr, msg, ap); va_end(ap); fprintf(stderr, "\n"); error_count += 1; based_size = 0; /* Clear the base information if we have an error. */ } void yywarn(const YYLTYPE&loc, const char*msg) { warn_count += 1; cerr << loc << ": warning: " << msg << endl; } int VLwrap() { return -1; } iverilog-10_1/parse_misc.h000066400000000000000000000074741265551621300156540ustar00rootroot00000000000000#ifndef IVL_parse_misc_H #define IVL_parse_misc_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include "compiler.h" # include "pform.h" /* * The vlltype supports the passing of detailed source file location * information between the lexical analyzer and the parser. Defining * YYLTYPE compels the lexor to use this type and not something other. */ struct vlltype { int first_line; int first_column; int last_line; int last_column; const char*text; std::string get_fileline() const; }; # define YYLTYPE struct vlltype class LineInfo; inline void FILE_NAME(LineInfo*tmp, const struct vlltype&where) { tmp->set_lineno(where.first_line); tmp->set_file(filename_strings.make(where.text)); } /* This for compatibility with new and older bison versions. */ #ifndef yylloc # define yylloc VLlloc #endif extern YYLTYPE yylloc; /* * Interface into the lexical analyzer. ... */ extern int VLlex(); extern void VLerror(const char*msg); extern void VLerror(const YYLTYPE&loc, const char*msg, ...) __attribute__((format(printf,2,3))); #define yywarn VLwarn extern void VLwarn(const YYLTYPE&loc, const char*msg); extern void destroy_lexor(); extern ostream& operator << (ostream&, const YYLTYPE&loc); extern unsigned error_count, warn_count; extern unsigned long based_size; extern bool in_celldefine; enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; extern UCDriveType uc_drive; /* * Flags to control if we are declaring or checking a timeunit or * timeprecision statement. */ extern bool have_timeunit_decl; extern bool have_timeprec_decl; /* * The parser signals back to the lexor that the next identifier * should be in the package scope. For example, if the source is * :: * Then the parser calls this function to set the package context so * that the lexor can interpret in the package context. */ extern void lex_in_package_scope(PPackage*pkg); /* * Test if this identifier is a type identifier in the current * context. The pform code needs to help the lexor here because the * parser detects typedefs and marks the typedef'ed identifiers as * type names. */ extern data_type_t* pform_test_type_identifier(const char*txt); extern data_type_t* pform_test_type_identifier(PPackage*pkg, const char*txt); extern bool pform_test_type_identifier_local(perm_string txt); /* * Test if this identifier is a package name. The pform needs to help * the lexor here because the parser detects packages and saves them. */ extern PPackage* pform_test_package_identifier(const char*txt); /* * Export these functions because we have to generate PENumber class * in pform.cc for user defparam definition from command file. */ extern verinum*make_unsized_dec(const char*txt); extern verinum*make_undef_highz_dec(const char*txt); extern verinum*make_unsized_binary(const char*txt); extern verinum*make_unsized_octal(const char*txt); extern verinum*make_unsized_hex(const char*txt); extern char* strdupnew(char const *str); #endif /* IVL_parse_misc_H */ iverilog-10_1/pform.cc000066400000000000000000003172641265551621300150110ustar00rootroot00000000000000/* * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "pform.h" # include "parse_misc.h" # include "parse_api.h" # include "PClass.h" # include "PEvent.h" # include "PPackage.h" # include "PUdp.h" # include "PGenerate.h" # include "PModport.h" # include "PSpec.h" # include "discipline.h" # include # include # include # include # include # include # include # include # include "ivl_assert.h" # include "ivl_alloc.h" /* * The "// synthesis translate_on/off" meta-comments cause this flag * to be turned off or on. The pform_make_behavior and similar * functions look at this flag and may choose to add implicit ivl * synthesis flags. */ static bool pform_mc_translate_flag = true; void pform_mc_translate_on(bool flag) { pform_mc_translate_flag = flag; } /* * The pform_modules is a map of the modules that have been defined in * the top level. This should not contain nested modules/programs. * pform_primitives is similar, but for UDP primitives. */ map pform_modules; map pform_primitives; /* * typedefs in the $root scope go here. */ mappform_typedefs; setpform_enum_sets; /* * Class definitions in the $root scope go here. */ map pform_classes; /* * Task and function definitions in the $root scope go here. */ map pform_tasks; std::string vlltype::get_fileline() const { ostringstream buf; buf << (text? text : "") << ":" << first_line; string res = buf.str(); return res; } /* * Parse configuration file with format =, where key * is the hierarchical name of a valid parameter name, and value * is the value user wants to assign to. The value should be constant. */ void parm_to_defparam_list(const string¶m) { char* key; char* value; unsigned off = param.find('='); if (off > param.size()) { key = strdup(param.c_str()); value = (char*)malloc(1); *value = '\0'; } else { key = strdup(param.substr(0, off).c_str()); value = strdup(param.substr(off+1).c_str()); } // Resolve hierarchical name for defparam. Remember // to deal with bit select for generate scopes. Bit // select expression should be constant integer. pform_name_t name; char *nkey = key; char *ptr = strchr(key, '.'); while (ptr != 0) { *ptr++ = '\0'; // Find if bit select is applied, this would be something // like - scope[2].param = 10 char *bit_l = strchr(nkey, '['); if (bit_l !=0) { *bit_l++ = '\0'; char *bit_r = strchr(bit_l, ']'); if (bit_r == 0) { cerr << ": error: missing ']' for defparam: " << nkey << endl; free(key); free(value); return; } *bit_r = '\0'; int i = 0; while (*(bit_l+i) != '\0') if (!isdigit(*(bit_l+i++))) { cerr << ": error: scope index expression is not constant: " << nkey << endl; free(key); free(value); return; } name_component_t tmp(lex_strings.make(nkey)); index_component_t index; index.sel = index_component_t::SEL_BIT; verinum *seln = new verinum(atoi(bit_l)); PENumber *sel = new PENumber(seln); index.msb = sel; index.lsb = sel; tmp.index.push_back(index); name.push_back(tmp); } else // no bit select name.push_back(name_component_t(lex_strings.make(nkey))); nkey = ptr; ptr = strchr(nkey, '.'); } name.push_back(name_component_t(lex_strings.make(nkey))); // Resolve value to PExpr class. Should support all kind of constant // format including based number, dec number, real number and string. if (*value == '"') { // string type char *buf = strdup (value); char *buf_ptr = buf+1; // Parse until another '"' or '\0' while (*buf_ptr != '"' && *buf_ptr != '\0') { buf_ptr++; // Check for escape, especially '\"', which does not mean the // end of string. if (*buf_ptr == '\\' && *(buf_ptr+1) != '\0') buf_ptr += 2; } if (*buf_ptr == '\0') // String end without '"' cerr << ": error: missing close quote of string for defparam: " << name << endl; else if (*(buf_ptr+1) != 0) { // '"' appears within string with no escape cerr << buf_ptr << endl; cerr << ": error: \'\"\' appears within string value for defparam: " << name << ". Ignore characters after \'\"\'" << endl; } *buf_ptr = '\0'; buf_ptr = buf+1; // Remember to use 'new' to allocate string for PEString // because 'delete' is used by its destructor. char *nchar = strcpy(new char [strlen(buf_ptr)+1], buf_ptr); PExpr* ndec = new PEString(nchar); Module::user_defparms.push_back( make_pair(name, ndec) ); free(buf); } else { // number type char *num = strchr(value, '\''); if (num != 0) { verinum *val; // BASED_NUMBER, something like - scope.parameter='b11 // make sure to check 'h' first because 'b'&'d' may be included // in hex format if (strchr(num, 'h') || strchr(num, 'H')) val = make_unsized_hex(num); else if (strchr(num, 'd') || strchr(num, 'D')) if (strchr(num, 'x') || strchr(num, 'X') || strchr(num, 'z') || strchr(num, 'Z')) val = make_undef_highz_dec(num); else val = make_unsized_dec(num); else if (strchr(num, 'b') || strchr(num, 'B')) { val = make_unsized_binary(num); } else if (strchr(num, 'o') || strchr(num, 'O')) val = make_unsized_octal(num); else { cerr << ": error: value specify error for defparam: " << name << endl; free(key); free(value); return; } // BASED_NUMBER with size, something like - scope.parameter=2'b11 if (num != value) { *num = 0; verinum *siz = make_unsized_dec(value); val = pform_verinum_with_size(siz, val, "", 0); } PExpr* ndec = new PENumber(val); Module::user_defparms.push_back( make_pair(name, ndec) ); } else { // REALTIME, something like - scope.parameter=1.22 or scope.parameter=1e2 if (strchr(value, '.') || strchr(value, 'e') || strchr(value, 'E')) { verireal *val = new verireal(value); PExpr* nreal = new PEFNumber(val); Module::user_defparms.push_back( make_pair(name, nreal) ); } else { // DEC_NUMBER, something like - scope.parameter=3 verinum *val = make_unsized_dec(value); PExpr* ndec = new PENumber(val); Module::user_defparms.push_back( make_pair(name, ndec) ); } } } free(key); free(value); } /* * The lexor accesses the vl_* variables. */ string vl_file = ""; extern int VLparse(); /* This tracks the current module being processed. There can only be exactly one module currently being parsed, since Verilog does not allow nested module definitions. */ static listpform_cur_module; bool pform_library_flag = false; /* * Give each unnamed block that has a variable declaration a unique name. */ static unsigned scope_unnamed_block_with_decl = 1; /* increment this for generate schemes within a module, and set it to zero when a new module starts. */ static unsigned scope_generate_counter = 1; /* This tracks the current generate scheme being processed. This is always within a module. */ static PGenerate*pform_cur_generate = 0; /* This tracks the current modport list being processed. This is always within an interface. */ static PModport*pform_cur_modport = 0; static NetNet::Type pform_default_nettype = NetNet::WIRE; /* * These variables track the current time scale, as well as where the * timescale was set. This supports warnings about tangled timescales. */ static int pform_time_unit; static int pform_time_prec; /* These two flags check the initial timeprecision and timeunit * declaration inside a module. */ static bool tp_decl_flag = false; static bool tu_decl_flag = false; /* * Flags used to set time_from_timescale based on timeunit and * timeprecision. */ static bool tu_global_flag = false; static bool tp_global_flag = false; static bool tu_local_flag = false; static bool tp_local_flag = false; static char*pform_timescale_file = 0; static unsigned pform_timescale_line; static inline void FILE_NAME(LineInfo*obj, const char*file, unsigned lineno) { obj->set_lineno(lineno); obj->set_file(filename_strings.make(file)); } /* * The lexical_scope keeps track of the current lexical scope that is * being parsed. The lexical scope may stack, so the current scope may * have a parent, that is restored when the current scope ends. * * Items that have scoped names are put in the lexical_scope object. */ static LexicalScope* lexical_scope = 0; void pform_pop_scope() { assert(lexical_scope); lexical_scope = lexical_scope->parent_scope(); } static PScopeExtra* find_nearest_scopex(LexicalScope*scope) { PScopeExtra*scopex = dynamic_cast (scope); while (scope && !scopex) { scope = scope->parent_scope(); scopex = dynamic_cast (scope); } return scopex; } LexicalScope* pform_peek_scope(void) { assert(lexical_scope); return lexical_scope; } PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name) { PClass*class_scope = new PClass(name, lexical_scope); FILE_NAME(class_scope, loc); PScopeExtra*scopex = find_nearest_scopex(lexical_scope); assert(!pform_cur_generate); /* If no scope was found then this is being defined in the * compilation unit scope. */ if (scopex == 0) { pform_classes[name] = class_scope; lexical_scope = class_scope; return class_scope; } if (scopex->classes.find(name) != scopex->classes.end()) { cerr << class_scope->get_fileline() << ": error: duplicate " "definition for class '" << name << "' in '" << scopex->pscope_name() << "'." << endl; error_count += 1; } scopex->classes[name] = class_scope; scopex->classes_lexical .push_back(class_scope); lexical_scope = class_scope; return class_scope; } PPackage* pform_push_package_scope(const struct vlltype&loc, perm_string name) { PPackage*pkg_scope = new PPackage(name, lexical_scope); FILE_NAME(pkg_scope, loc); lexical_scope = pkg_scope; return pkg_scope; } PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) { perm_string task_name = lex_strings.make(name); PTask*task = new PTask(task_name, lexical_scope, is_auto); FILE_NAME(task, loc); PScopeExtra*scopex = find_nearest_scopex(lexical_scope); if ((scopex == 0) && !gn_system_verilog()) { cerr << task->get_fileline() << ": error: task declarations " "must be contained within a module." << endl; error_count += 1; } if (pform_cur_generate) { // Check if the task is already in the dictionary. if (pform_cur_generate->tasks.find(task->pscope_name()) != pform_cur_generate->tasks.end()) { cerr << task->get_fileline() << ": error: duplicate " "definition for task '" << name << "' in '" << pform_cur_module.front()->mod_name() << "' (generate)." << endl; error_count += 1; } pform_cur_generate->tasks[task->pscope_name()] = task; } else if (scopex) { // Check if the task is already in the dictionary. if (scopex->tasks.find(task->pscope_name()) != scopex->tasks.end()) { cerr << task->get_fileline() << ": error: duplicate " "definition for task '" << name << "' in '" << scopex->pscope_name() << "'." << endl; error_count += 1; } scopex->tasks[task->pscope_name()] = task; } else { if (pform_tasks.find(task_name) != pform_tasks.end()) { cerr << task->get_fileline() << ": error: " << "Duplicate definition for task '" << name << "' in $root scope." << endl; error_count += 1; } pform_tasks[task_name] = task; } lexical_scope = task; return task; } PFunction* pform_push_function_scope(const struct vlltype&loc, const char*name, bool is_auto) { perm_string func_name = lex_strings.make(name); PFunction*func = new PFunction(func_name, lexical_scope, is_auto); FILE_NAME(func, loc); PScopeExtra*scopex = find_nearest_scopex(lexical_scope); if ((scopex == 0) && (generation_flag < GN_VER2005_SV)) { cerr << func->get_fileline() << ": error: function declarations " "must be contained within a module." << endl; error_count += 1; } if (pform_cur_generate) { // Check if the function is already in the dictionary. if (pform_cur_generate->funcs.find(func->pscope_name()) != pform_cur_generate->funcs.end()) { cerr << func->get_fileline() << ": error: duplicate " "definition for function '" << name << "' in '" << pform_cur_module.front()->mod_name() << "' (generate)." << endl; error_count += 1; } pform_cur_generate->funcs[func->pscope_name()] = func; } else if (scopex != 0) { // Check if the function is already in the dictionary. if (scopex->funcs.find(func->pscope_name()) != scopex->funcs.end()) { cerr << func->get_fileline() << ": error: duplicate " "definition for function '" << name << "' in '" << scopex->pscope_name() << "'." << endl; error_count += 1; } scopex->funcs[func->pscope_name()] = func; } else { if (pform_tasks.find(func_name) != pform_tasks.end()) { cerr << func->get_fileline() << ": error: " << "Duplicate definition for function '" << name << "' in $root scope." << endl; error_count += 1; } pform_tasks[func_name] = func; } lexical_scope = func; return func; } PBlock* pform_push_block_scope(char*name, PBlock::BL_TYPE bt) { perm_string block_name; if (name) block_name = lex_strings.make(name); else { // Create a unique name for this unnamed block. char tmp[32]; snprintf(tmp, sizeof tmp, "$unm_blk_%u", scope_unnamed_block_with_decl); block_name = lex_strings.make(tmp); scope_unnamed_block_with_decl += 1; } PBlock*block = new PBlock(block_name, lexical_scope, bt); lexical_scope = block; return block; } /* * Create a new identifier. Check if this is an imported name. */ PEIdent* pform_new_ident(const pform_name_t&name) { LexicalScope*scope = pform_peek_scope(); map::const_iterator pkg = scope->imports.find(name.front().name); if (pkg == scope->imports.end()) return new PEIdent(name); // XXXX For now, do not support indexed imported names. assert(name.back().index.size() == 0); return new PEIdent(pkg->second, name); } PGenerate* pform_parent_generate(void) { return pform_cur_generate; } void pform_bind_attributes(map&attributes, list*attr, bool keep_attrs) { if (attr == 0) return; while (! attr->empty()) { named_pexpr_t tmp = attr->front(); attr->pop_front(); attributes[tmp.name] = tmp.parm; } if (!keep_attrs) delete attr; } bool pform_in_program_block() { if (pform_cur_module.empty()) return false; if (pform_cur_module.front()->program_block) return true; return false; } bool pform_in_interface() { if (pform_cur_module.empty()) return false; if (pform_cur_module.front()->is_interface) return true; return false; } static bool pform_at_module_level() { return (lexical_scope == pform_cur_module.front()) || (lexical_scope == pform_cur_generate); } PWire*pform_get_wire_in_scope(perm_string name) { return lexical_scope->wires_find(name); } static void pform_put_wire_in_scope(perm_string name, PWire*net) { lexical_scope->wires[name] = net; } static void pform_put_enum_type_in_scope(enum_type_t*enum_set) { if (lexical_scope) { ivl_assert(*enum_set, lexical_scope); lexical_scope->enum_sets.insert(enum_set); } else { pform_enum_sets.insert(enum_set); } } PWire*pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type) { PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { cur = new PWire(name, net_type, port_type, vt_type); pform_put_wire_in_scope(name, cur); } else { // If this is a duplicate wire, the data type has already // been set, then return NULL. if (cur->get_data_type() != IVL_VT_NO_TYPE) return 0; bool rc = cur->set_wire_type(net_type); assert(rc); rc = cur->set_data_type(vt_type); assert(rc); } return cur; } void pform_set_typedef(perm_string name, data_type_t*data_type, std::list*unp_ranges) { if(unp_ranges) data_type = new uarray_type_t(data_type, unp_ranges); // If we are in a lexical scope (i.e. a package or module) // then put the typedef into that scope. Otherwise, put it // into the $root scope. data_type_t*&ref = lexical_scope ? lexical_scope->typedefs[name] : pform_typedefs[name]; ivl_assert(*data_type, ref == 0); ref = data_type; if (enum_type_t*enum_type = dynamic_cast(data_type)) { pform_put_enum_type_in_scope(enum_type); } } static data_type_t* test_type_identifier_in_root(perm_string name) { map::iterator cur = pform_typedefs.find(name); if (cur != pform_typedefs.end()) return cur->second; else return 0; } data_type_t* pform_test_type_identifier(const char*txt) { perm_string name = lex_strings.make(txt); // If there is no lexical_scope yet, then look only in the // $root scope for typedefs. if (lexical_scope == 0) { return test_type_identifier_in_root(name); } LexicalScope*cur_scope = lexical_scope; do { map::iterator cur; // First look to see if this identifier is imported from // a package. If it is, see if it is a type in that // package. If it is, then great. If imported as // something other than a type, then give up now because // the name has at least shadowed any other possible // meaning for this name. map::iterator cur_pkg; cur_pkg = cur_scope->imports.find(name); if (cur_pkg != cur_scope->imports.end()) { PPackage*pkg = cur_pkg->second; cur = pkg->typedefs.find(name); if (cur != pkg->typedefs.end()) return cur->second; // Not a type. Give up. return 0; } cur = cur_scope->typedefs.find(name); if (cur != cur_scope->typedefs.end()) return cur->second; cur_scope = cur_scope->parent_scope(); } while (cur_scope); // See if there is a typedef in the $root scope. if (data_type_t*tmp = test_type_identifier_in_root(name)) return tmp; return 0; } /* * The parser uses this function to test if the name is a typedef in * the current scope. We use this to know if we can override the * definition because it shadows a containing scope. */ bool pform_test_type_identifier_local(perm_string name) { if (lexical_scope == 0) { if (test_type_identifier_in_root(name)) return true; else return false; } LexicalScope*cur_scope = lexical_scope; map::iterator cur; cur = cur_scope->typedefs.find(name); if (cur != cur_scope->typedefs.end()) return true; return false; } PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, const list&parms) { PECallFunction*tmp = 0; // First try to get the function name from a package. Check // the imports, and if the name is there, make the function as // a package member. do { if (name.size() != 1) break; perm_string use_name = peek_tail_name(name); map::iterator cur_pkg; cur_pkg = lexical_scope->imports.find(use_name); if (cur_pkg == lexical_scope->imports.end()) break; tmp = new PECallFunction(cur_pkg->second, use_name, parms); } while(0); if (tmp == 0) { tmp = new PECallFunction(name, parms); } FILE_NAME(tmp, loc); return tmp; } PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const list&parms) { PCallTask*tmp = 0; do { if (name.size() != 1) break; perm_string use_name = peek_tail_name(name); map::iterator cur_pkg; cur_pkg = lexical_scope->imports.find(use_name); if (cur_pkg == lexical_scope->imports.end()) break; tmp = new PCallTask(cur_pkg->second, name, parms); } while (0); if (tmp == 0) { tmp = new PCallTask(name, parms); } FILE_NAME(tmp, loc); return tmp; } void pform_make_foreach_declarations(const struct vlltype&loc, std::list*loop_vars) { static const struct str_pair_t str = { IVL_DR_STRONG, IVL_DR_STRONG }; listassign_list; for (list::const_iterator cur = loop_vars->begin() ; cur != loop_vars->end() ; ++ cur) { decl_assignment_t*tmp_assign = new decl_assignment_t; tmp_assign->name = lex_strings.make(*cur); assign_list.push_back(tmp_assign); } pform_makewire(loc, 0, str, &assign_list, NetNet::REG, &size_type); } PForeach* pform_make_foreach(const struct vlltype&loc, char*name, list*loop_vars, Statement*stmt) { perm_string use_name = lex_strings.make(name); delete[]name; if (loop_vars==0 || loop_vars->empty()) { cerr << loc.get_fileline() << ": error: " << "No loop variables at all in foreach index." << endl; error_count += 1; } ivl_assert(loc, loop_vars); PForeach*fe = new PForeach(use_name, *loop_vars, stmt); FILE_NAME(fe, loc); delete loop_vars; return fe; } static void pform_put_behavior_in_scope(PProcess*pp) { lexical_scope->behaviors.push_back(pp); } void pform_put_behavior_in_scope(AProcess*pp) { lexical_scope->analog_behaviors.push_back(pp); } void pform_set_default_nettype(NetNet::Type type, const char*file, unsigned lineno) { pform_default_nettype = type; if (! pform_cur_module.empty()) { cerr << file<<":"<mod_name() << " starts on line " << pform_cur_module.back()->get_fileline() << "." << endl; error_count += 1; } } static void pform_declare_implicit_nets(PExpr*expr) { /* If implicit net creation is turned off, then stop now. */ if (pform_default_nettype == NetNet::NONE) return; if (expr) expr->declare_implicit_nets(lexical_scope, pform_default_nettype); } /* * The lexor calls this function to set the active timescale when it * detects a `timescale directive. The function saves the directive * values (for use by modules) and if warnings are enabled checks to * see if some modules have no timescale. */ void pform_set_timescale(int unit, int prec, const char*file, unsigned lineno) { bool first_flag = true; assert(unit >= prec); pform_time_unit = unit; pform_time_prec = prec; /* A `timescale clears the timeunit/timeprecision state. */ tu_global_flag = false; tp_global_flag = false; if (pform_timescale_file) { free(pform_timescale_file); first_flag = false; } if (file) pform_timescale_file = strdup(file); else pform_timescale_file = 0; pform_timescale_line = lineno; if (!warn_timescale || !first_flag || !file) return; /* Look to see if we have any modules without a timescale. */ bool have_no_ts = false; map::iterator mod; for (mod = pform_modules.begin(); mod != pform_modules.end(); ++ mod ) { const Module*mp = (*mod).second; if (mp->time_from_timescale || mp->timescale_warn_done) continue; have_no_ts = true; break; } /* If we do then print a message for the new ones. */ if (have_no_ts) { cerr << file << ":" << lineno << ": warning: " << "Some modules have no timescale. This may cause" << endl; cerr << file << ":" << lineno << ": : " << "confusing timing results. Affected modules are:" << endl; for (mod = pform_modules.begin() ; mod != pform_modules.end() ; ++ mod ) { Module*mp = (*mod).second; if (mp->time_from_timescale || mp->timescale_warn_done) continue; mp->timescale_warn_done = true; cerr << file << ":" << lineno << ": : " << " -- module " << (*mod).first << " declared here: " << mp->get_fileline() << endl; } } } bool get_time_unit(const char*cp, int &unit) { const char *c; bool rc = true; if (strchr(cp, '_')) { VLerror(yylloc, "Invalid timeunit constant ('_' is not " "supported)."); return false; } c = strpbrk(cp, "munpfs"); if (!c) return false; if (*c == 's') unit = 0; else if (!strncmp(c, "ms", 2)) unit = -3; else if (!strncmp(c, "us", 2)) unit = -6; else if (!strncmp(c, "ns", 2)) unit = -9; else if (!strncmp(c, "ps", 2)) unit = -12; else if (!strncmp(c, "fs", 2)) unit = -15; else { rc = false; ostringstream msg; msg << "Invalid timeunit scale '" << cp << "'."; VLerror(msg.str().c_str()); } return rc; } /* * Get a timeunit or timeprecision value from a string. This is * similar to the code in lexor.lex for the `timescale directive. */ static bool get_time_unit_prec(const char*cp, int &res, bool is_unit) { /* We do not support a '_' in these time constants. */ if (strchr(cp, '_')) { if (is_unit) { VLerror(yylloc, "Invalid timeunit constant ('_' is not " "supported)."); } else { VLerror(yylloc, "Invalid timeprecision constant ('_' is not " "supported)."); } return true; } /* Check for the 1 digit. */ if (*cp != '1') { if (is_unit) { VLerror(yylloc, "Invalid timeunit constant (1st digit)."); } else { VLerror(yylloc, "Invalid timeprecision constant (1st digit)."); } return true; } cp += 1; /* Check the number of zeros after the 1. */ res = strspn(cp, "0"); if (res > 2) { if (is_unit) { VLerror(yylloc, "Invalid timeunit constant (number of " "zeros)."); } else { VLerror(yylloc, "Invalid timeprecision constant (number of " "zeros)."); } return true; } cp += res; /* Now process the scaling string. */ if (strncmp("s", cp, 1) == 0) { res -= 0; return false; } else if (strncmp("ms", cp, 2) == 0) { res -= 3; return false; } else if (strncmp("us", cp, 2) == 0) { res -= 6; return false; } else if (strncmp("ns", cp, 2) == 0) { res -= 9; return false; } else if (strncmp("ps", cp, 2) == 0) { res -= 12; return false; } else if (strncmp("fs", cp, 2) == 0) { res -= 15; return false; } ostringstream msg; msg << "Invalid "; if (is_unit) msg << "timeunit"; else msg << "timeprecision"; msg << " scale '" << cp << "'."; VLerror(msg.str().c_str()); return true; } void pform_set_timeunit(const char*txt, bool in_module, bool only_check) { int val; if (get_time_unit_prec(txt, val, true)) return; if (in_module) { if (!only_check) { pform_cur_module.front()->time_unit = val; tu_decl_flag = true; tu_local_flag = true; } else if (!tu_decl_flag) { VLerror(yylloc, "error: repeat timeunit found and the " "initial module timeunit is missing."); return; } else if (pform_cur_module.front()->time_unit != val) { VLerror(yylloc, "error: repeat timeunit does not match " "the initial module timeunit " "declaration."); return; } } else { /* Skip a global timeunit when `timescale is defined. */ if (pform_timescale_file) return; tu_global_flag = true; pform_time_unit = val; } } int pform_get_timeunit() { return pform_cur_module.front()->time_unit; } void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) { int val; if (get_time_unit_prec(txt, val, false)) return; if (in_module) { if (!only_check) { pform_cur_module.front()->time_precision = val; tp_decl_flag = true; tp_local_flag = true; } else if (!tp_decl_flag) { VLerror(yylloc, "error: repeat timeprecision found and the " "initial module timeprecision is missing."); return; } else if (pform_cur_module.front()->time_precision != val) { VLerror(yylloc, "error: repeat timeprecision does not match " "the initial module timeprecision " "declaration."); return; } } else { /* Skip a global timeprecision when `timescale is defined. */ if (pform_timescale_file) return; pform_time_prec = val; tp_global_flag=true; } } verinum* pform_verinum_with_size(verinum*siz, verinum*val, const char*file, unsigned lineno) { assert(siz->is_defined()); unsigned long size = siz->as_ulong(); if (size == 0) { cerr << file << ":" << lineno << ": error: Sized numeric constant " "must have a size greater than zero." << endl; error_count += 1; } verinum::V pad; switch (val->get(val->len()-1)) { case verinum::Vz: pad = verinum::Vz; break; case verinum::Vx: pad = verinum::Vx; break; default: pad = verinum::V0; break; } verinum*res = new verinum(pad, size, true); unsigned copy = val->len(); if (res->len() < copy) copy = res->len(); for (unsigned idx = 0 ; idx < copy ; idx += 1) { res->set(idx, val->get(idx)); } res->has_sign(val->has_sign()); bool trunc_flag = false; for (unsigned idx = copy ; idx < val->len() ; idx += 1) { if (val->get(idx) != pad) { trunc_flag = true; break; } } if (trunc_flag) { cerr << file << ":" << lineno << ": warning: Numeric constant " << "truncated to " << copy << " bits." << endl; } delete siz; delete val; return res; } void pform_startmodule(const struct vlltype&loc, const char*name, bool program_block, bool is_interface, list*attr) { if (! pform_cur_module.empty() && !gn_system_verilog()) { cerr << loc << ": error: Module definition " << name << " cannot nest into module " << pform_cur_module.front()->mod_name() << "." << endl; error_count += 1; } if (gn_system_verilog() && ! pform_cur_module.empty()) { if (pform_cur_module.front()->program_block) { cerr << loc << ": error: module, program, or interface " "declarations are not allowed in program " "blocks." << endl; error_count += 1; } if (pform_cur_module.front()->is_interface && !(program_block || is_interface)) { cerr << loc << ": error: module declarations are not " "allowed in interfaces." << endl; error_count += 1; } } perm_string lex_name = lex_strings.make(name); Module*cur_module = new Module(lexical_scope, lex_name); cur_module->program_block = program_block; cur_module->is_interface = is_interface; /* Set the local time unit/precision to the global value. */ cur_module->time_unit = pform_time_unit; cur_module->time_precision = pform_time_prec; tu_local_flag = tu_global_flag; tp_local_flag = tp_global_flag; /* If we have a timescale file then the time information is from * a timescale directive. */ cur_module->time_from_timescale = pform_timescale_file != 0; FILE_NAME(cur_module, loc); cur_module->library_flag = pform_library_flag; pform_cur_module.push_front(cur_module); lexical_scope = cur_module; /* The generate scheme numbering starts with *1*, not zero. That's just the way it is, thanks to the standard. */ scope_generate_counter = 1; if (warn_timescale && pform_timescale_file && (strcmp(pform_timescale_file,loc.text) != 0)) { cerr << cur_module->get_fileline() << ": warning: " << "timescale for " << name << " inherited from another file." << endl; cerr << pform_timescale_file << ":" << pform_timescale_line << ": ...: The inherited timescale is here." << endl; } pform_bind_attributes(cur_module->attributes, attr); } /* * In SystemVerilog we can have separate timeunit and timeprecision * declarations. We need to have the values worked out by time this * task is called. */ void pform_check_timeunit_prec() { assert(! pform_cur_module.empty()); if ((generation_flag & (GN_VER2005_SV | GN_VER2009 | GN_VER2012)) && (pform_cur_module.front()->time_unit < pform_cur_module.front()->time_precision)) { VLerror("error: a timeprecision is missing or is too large!"); } else assert(pform_cur_module.front()->time_unit >= pform_cur_module.front()->time_precision); } /* * This function is called by the parser to make a simple port * reference. This is a name without a .X(...), so the internal name * should be generated to be the same as the X. */ Module::port_t* pform_module_port_reference(perm_string name, const char*file, unsigned lineno) { Module::port_t*ptmp = new Module::port_t; PEIdent*tmp = new PEIdent(name); FILE_NAME(tmp, file, lineno); ptmp->name = name; ptmp->expr.push_back(tmp); return ptmp; } void pform_module_set_ports(vector*ports) { assert(! pform_cur_module.empty()); /* The parser parses ``module foo()'' as having one unconnected port, but it is really a module with no ports. Fix it up here. */ if (ports && (ports->size() == 1) && ((*ports)[0] == 0)) { delete ports; ports = 0; } if (ports != 0) { pform_cur_module.front()->ports = *ports; delete ports; } } void pform_endmodule(const char*name, bool inside_celldefine, Module::UCDriveType uc_drive_def) { assert(! pform_cur_module.empty()); Module*cur_module = pform_cur_module.front(); pform_cur_module.pop_front(); cur_module->time_from_timescale = (tu_local_flag && tp_local_flag) || (pform_timescale_file != 0); perm_string mod_name = cur_module->mod_name(); assert(strcmp(name, mod_name) == 0); cur_module->is_cell = inside_celldefine; cur_module->uc_drive = uc_drive_def; // If this is a root module, then there is no parent module // and we try to put this newly defined module into the global // root list of modules. Otherwise, this is a nested module // and we put it into the parent module scope to be elaborated // if needed. map&use_module_map = (pform_cur_module.empty()) ? pform_modules : pform_cur_module.front()->nested_modules; map::const_iterator test = use_module_map.find(mod_name); if (test != use_module_map.end()) { ostringstream msg; msg << "Module " << name << " was already declared here: " << test->second->get_fileline() << endl; VLerror(msg.str().c_str()); } else { use_module_map[mod_name] = cur_module; } // The current lexical scope should be this module by now, and // this module should not have a parent lexical scope. ivl_assert(*cur_module, lexical_scope == cur_module); pform_pop_scope(); ivl_assert(*cur_module, ! pform_cur_module.empty() || lexical_scope == 0); tp_decl_flag = false; tu_decl_flag = false; tu_local_flag = false; tp_local_flag = false; } static void pform_add_genvar(const struct vlltype&li, const perm_string&name, map&genvars) { LineInfo*lni = new LineInfo(); FILE_NAME(lni, li); if (genvars.find(name) != genvars.end()) { cerr << lni->get_fileline() << ": error: genvar '" << name << "' has already been declared." << endl; cerr << genvars[name]->get_fileline() << ": the previous declaration is here." << endl; error_count += 1; delete lni; } else { genvars[name] = lni; } } void pform_genvars(const struct vlltype&li, list*names) { list::const_iterator cur; for (cur = names->begin(); cur != names->end() ; *cur++) { if (pform_cur_generate) pform_add_genvar(li, *cur, pform_cur_generate->genvars); else pform_add_genvar(li, *cur, pform_cur_module.front()->genvars); } delete names; } void pform_start_generate_for(const struct vlltype&li, char*ident1, PExpr*init, PExpr*test, char*ident2, PExpr*next) { PGenerate*gen = new PGenerate(lexical_scope, scope_generate_counter++); lexical_scope = gen; FILE_NAME(gen, li); pform_cur_generate = gen; pform_cur_generate->scheme_type = PGenerate::GS_LOOP; pform_cur_generate->loop_index = lex_strings.make(ident1); pform_cur_generate->loop_init = init; pform_cur_generate->loop_test = test; pform_cur_generate->loop_step = next; delete[]ident1; delete[]ident2; } void pform_start_generate_if(const struct vlltype&li, PExpr*test) { PGenerate*gen = new PGenerate(lexical_scope, scope_generate_counter++); lexical_scope = gen; FILE_NAME(gen, li); pform_cur_generate = gen; pform_cur_generate->scheme_type = PGenerate::GS_CONDIT; pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = test; pform_cur_generate->loop_step = 0; } void pform_start_generate_else(const struct vlltype&li) { assert(pform_cur_generate); assert(pform_cur_generate->scheme_type == PGenerate::GS_CONDIT); PGenerate*cur = pform_cur_generate; pform_endgenerate(); PGenerate*gen = new PGenerate(lexical_scope, scope_generate_counter++); lexical_scope = gen; FILE_NAME(gen, li); pform_cur_generate = gen; pform_cur_generate->scheme_type = PGenerate::GS_ELSE; pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = cur->loop_test; pform_cur_generate->loop_step = 0; } /* * The GS_CASE version of the PGenerate contains only case items. The * items in turn contain the generated items themselves. */ void pform_start_generate_case(const struct vlltype&li, PExpr*expr) { PGenerate*gen = new PGenerate(lexical_scope, scope_generate_counter++); lexical_scope = gen; FILE_NAME(gen, li); pform_cur_generate = gen; pform_cur_generate->scheme_type = PGenerate::GS_CASE; pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = expr; pform_cur_generate->loop_step = 0; } /* * The named block generate case. */ void pform_start_generate_nblock(const struct vlltype&li, char*name) { PGenerate*gen = new PGenerate(lexical_scope, scope_generate_counter++); lexical_scope = gen; FILE_NAME(gen, li); pform_cur_generate = gen; pform_cur_generate->scheme_type = PGenerate::GS_NBLOCK; pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = 0; pform_cur_generate->loop_step = 0; pform_cur_generate->scope_name = lex_strings.make(name); delete[]name; } /* * The generate case item is a special case schema that takes its id * from the case schema that it is a part of. The idea is that the * case schema can only instantiate exactly one item, so the items * need not have a unique number. */ void pform_generate_case_item(const struct vlltype&li, list*expr_list) { assert(pform_cur_generate); assert(pform_cur_generate->scheme_type == PGenerate::GS_CASE); PGenerate*gen = new PGenerate(lexical_scope, pform_cur_generate->id_number); lexical_scope = gen; FILE_NAME(gen, li); pform_cur_generate = gen; pform_cur_generate->scheme_type = PGenerate::GS_CASE_ITEM; pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = 0; pform_cur_generate->loop_step = 0; if (expr_list != 0) { list::iterator expr_cur = expr_list->begin(); pform_cur_generate->item_test.resize(expr_list->size()); for (unsigned idx = 0 ; idx < expr_list->size() ; idx += 1) { pform_cur_generate->item_test[idx] = *expr_cur; ++ expr_cur; } assert(expr_cur == expr_list->end()); } } void pform_generate_block_name(char*name) { assert(pform_cur_generate != 0); assert(pform_cur_generate->scope_name == 0); pform_cur_generate->scope_name = lex_strings.make(name); } void pform_endgenerate() { assert(pform_cur_generate != 0); assert(! pform_cur_module.empty()); // If there is no explicit block name then generate a temporary // name. This will be replaced by the correct name later, once // we know all the explicit names in the surrounding scope. If // the naming scheme used here is changed, PGenerate::elaborate // must be changed to match. if (pform_cur_generate->scope_name == 0) { char tmp[16]; snprintf(tmp, sizeof tmp, "$gen%u", pform_cur_generate->id_number); pform_cur_generate->scope_name = lex_strings.make(tmp); } // The current lexical scope should be this generate construct by now ivl_assert(*pform_cur_generate, lexical_scope == pform_cur_generate); pform_pop_scope(); PGenerate*parent_generate = dynamic_cast(lexical_scope); if (parent_generate) { assert(pform_cur_generate->scheme_type == PGenerate::GS_CASE_ITEM || parent_generate->scheme_type != PGenerate::GS_CASE); parent_generate->generate_schemes.push_back(pform_cur_generate); } else { assert(pform_cur_generate->scheme_type != PGenerate::GS_CASE_ITEM); pform_cur_module.front()->generate_schemes.push_back(pform_cur_generate); } pform_cur_generate = parent_generate; } MIN_TYP_MAX min_typ_max_flag = TYP; unsigned min_typ_max_warn = 10; PExpr* pform_select_mtm_expr(PExpr*min, PExpr*typ, PExpr*max) { PExpr*res = 0; switch (min_typ_max_flag) { case MIN: res = min; delete typ; delete max; break; case TYP: res = typ; delete min; delete max; break; case MAX: res = max; delete min; delete typ; break; } if (min_typ_max_warn > 0) { cerr << res->get_fileline() << ": warning: choosing "; switch (min_typ_max_flag) { case MIN: cerr << "min"; break; case TYP: cerr << "typ"; break; case MAX: cerr << "max"; break; } cerr << " expression." << endl; min_typ_max_warn -= 1; } return res; } template <> inline svector::svector(unsigned size) : nitems_(size), items_(new perm_string[size]) { } static void process_udp_table(PUdp*udp, list*table, const char*file, unsigned lineno) { const bool synchronous_flag = udp->sequential; /* Interpret and check the table entry strings, to make sure they correspond to the inputs, output and output type. Make up vectors for the fully interpreted result that can be placed in the PUdp object. The table strings are made up by the parser to be two or three substrings separated by ';', i.e.: 0101:1:1 (synchronous device entry) 0101:0 (combinational device entry) The parser doesn't check that we got the right kind here, so this loop must watch out. */ svector input (table->size()); svector current (table->size()); svector output (table->size()); { unsigned idx = 0; for (list::iterator cur = table->begin() ; cur != table->end() ; ++ cur , idx += 1) { string tmp = *cur; /* Pull the input values from the string. */ assert(tmp.find(':') == (udp->ports.count() - 1)); input[idx] = tmp.substr(0, udp->ports.count()-1); tmp = tmp.substr(udp->ports.count()-1); assert(tmp[0] == ':'); /* If this is a synchronous device, get the current output string. */ if (synchronous_flag) { if (tmp.size() != 4) { cerr << file<<":"<tinput = input; udp->tcurrent = current; udp->toutput = output; } void pform_make_udp(perm_string name, list*parms, vector*decl, list*table, Statement*init_expr, const char*file, unsigned lineno) { unsigned local_errors = 0; assert(!parms->empty()); assert(decl); /* Put the declarations into a map, so that I can check them off with the parameters in the list. If the port is already in the map, merge the port type. I will rebuild a list of parameters for the PUdp object. */ map defs; for (unsigned idx = 0 ; idx < decl->size() ; idx += 1) { perm_string port_name = (*decl)[idx]->basename(); if (PWire*cur = defs[port_name]) { bool rc = true; assert((*decl)[idx]); if ((*decl)[idx]->get_port_type() != NetNet::PIMPLICIT) { rc = cur->set_port_type((*decl)[idx]->get_port_type()); assert(rc); } if ((*decl)[idx]->get_wire_type() != NetNet::IMPLICIT) { rc = cur->set_wire_type((*decl)[idx]->get_wire_type()); assert(rc); } } else { defs[port_name] = (*decl)[idx]; } } /* Put the parameters into a vector of wire descriptions. Look in the map for the definitions of the name. In this loop, the parms list in the list of ports in the port list of the UDP declaration, and the defs map maps that name to a PWire* created by an input or output declaration. */ svector pins (parms->size()); svector pin_names (parms->size()); { list::iterator cur; unsigned idx; for (cur = parms->begin(), idx = 0 ; cur != parms->end() ; ++ idx, ++ cur) { pins[idx] = defs[*cur]; pin_names[idx] = *cur; } } /* Check that the output is an output and the inputs are inputs. I can also make sure that only the single output is declared a register, if anything. The possible errors are: -- an input port (not the first) is missing an input declaration. -- An input port is declared output. */ assert(pins.count() > 0); do { if (pins[0] == 0) { cerr << file<<":"<get_port_type() != NetNet::POUTPUT) { cerr << file<<":"<get_port_type() != NetNet::PINPUT) { cerr << file<<":"<get_wire_type() == NetNet::REG) { cerr << file<<":"< 0) { delete parms; delete decl; delete table; delete init_expr; return; } /* Verify the "initial" statement, if present, to be sure that it only assigns to the output and the output is registered. Then save the initial value that I get. */ verinum::V init = verinum::Vx; if (init_expr) { // XXXX assert(pins[0]->get_wire_type() == NetNet::REG); PAssign*pa = dynamic_cast(init_expr); assert(pa); const PEIdent*id = dynamic_cast(pa->lval()); assert(id); // XXXX //assert(id->name() == pins[0]->name()); const PENumber*np = dynamic_cast(pa->rval()); assert(np); init = np->value()[0]; } // Put the primitive into the primitives table if (pform_primitives[name]) { VLerror("UDP primitive already exists."); } else { PUdp*udp = new PUdp(name, parms->size()); FILE_NAME(udp, file, lineno); // Detect sequential udp. if (pins[0]->get_wire_type() == NetNet::REG) udp->sequential = true; // Make the port list for the UDP for (unsigned idx = 0 ; idx < pins.count() ; idx += 1) udp->ports[idx] = pins[idx]->basename(); process_udp_table(udp, table, file, lineno); udp->initial = init; pform_primitives[name] = udp; } /* Delete the excess tables and lists from the parser. */ delete parms; delete decl; delete table; delete init_expr; } void pform_make_udp(perm_string name, bool synchronous_flag, perm_string out_name, PExpr*init_expr, list*parms, list*table, const char*file, unsigned lineno) { svector pins(parms->size() + 1); /* Make the PWire for the output port. */ pins[0] = new PWire(out_name, synchronous_flag? NetNet::REG : NetNet::WIRE, NetNet::POUTPUT, IVL_VT_LOGIC); FILE_NAME(pins[0], file, lineno); /* Make the PWire objects for the input ports. */ { list::iterator cur; unsigned idx; for (cur = parms->begin(), idx = 1 ; cur != parms->end() ; idx += 1, ++ cur) { assert(idx < pins.count()); pins[idx] = new PWire(*cur, NetNet::WIRE, NetNet::PINPUT, IVL_VT_LOGIC); FILE_NAME(pins[idx], file, lineno); } assert(idx == pins.count()); } /* Verify the initial expression, if present, to be sure that it only assigns to the output and the output is registered. Then save the initial value that I get. */ verinum::V init = verinum::Vx; if (init_expr) { // XXXX assert(pins[0]->get_wire_type() == NetNet::REG); PAssign*pa = dynamic_cast(init_expr); assert(pa); const PEIdent*id = dynamic_cast(pa->lval()); assert(id); // XXXX //assert(id->name() == pins[0]->name()); const PENumber*np = dynamic_cast(pa->rval()); assert(np); init = np->value()[0]; } // Put the primitive into the primitives table if (pform_primitives[name]) { VLerror("UDP primitive already exists."); } else { PUdp*udp = new PUdp(name, pins.count()); FILE_NAME(udp, file, lineno); // Detect sequential udp. udp->sequential = synchronous_flag; // Make the port list for the UDP for (unsigned idx = 0 ; idx < pins.count() ; idx += 1) udp->ports[idx] = pins[idx]->basename(); assert(udp); assert(table); process_udp_table(udp, table, file, lineno); udp->initial = init; pform_primitives[name] = udp; } delete parms; delete table; delete init_expr; } /* * This function attaches a range to a given name. The function is * only called by the parser within the scope of the net declaration, * and the name that I receive only has the tail component. */ static void pform_set_net_range(perm_string name, NetNet::Type net_type, const list*range, bool signed_flag, ivl_variable_type_t dt, PWSRType rt, std::list*attr) { PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { VLerror("error: name is not a valid net."); return; } // If this is not implicit ("implicit" meaning we don't // know what the type is yet) then set the type now. if (net_type != NetNet::IMPLICIT && net_type != NetNet::NONE) { bool rc = cur->set_wire_type(net_type); if (rc == false) { ostringstream msg; msg << name << " " << net_type << " definition conflicts with " << cur->get_wire_type() << " definition at " << cur->get_fileline() << "."; VLerror(msg.str().c_str()); } } if (range == 0) { /* This is the special case that we really mean a scalar. Set a fake range. */ cur->set_range_scalar(rt); } else { cur->set_range(*range, rt); } cur->set_signed(signed_flag); if (dt != IVL_VT_NO_TYPE) cur->set_data_type(dt); pform_bind_attributes(cur->attributes, attr, true); } static void pform_set_net_range(list*names, list*range, bool signed_flag, ivl_variable_type_t dt, NetNet::Type net_type, std::list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; pform_set_net_range(txt, net_type, range, signed_flag, dt, SR_NET, attr); } } /* * This is invoked to make a named event. This is the declaration of * the event, and not necessarily the use of it. */ static void pform_make_event(perm_string name, const char*fn, unsigned ln) { // Check if the named event is already in the dictionary. if (lexical_scope->events.find(name) != lexical_scope->events.end()) { LineInfo tloc; FILE_NAME(&tloc, fn, ln); cerr << tloc.get_fileline() << ": error: duplicate definition " "for named event '" << name << "' in '" << pform_cur_module.front()->mod_name() << "'." << endl; error_count += 1; } PEvent*event = new PEvent(name); FILE_NAME(event, fn, ln); lexical_scope->events[name] = event; } void pform_make_events(list*names, const char*fn, unsigned ln) { list::iterator cur; for (cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; pform_make_event(txt, fn, ln); } delete names; } /* * pform_makegates is called when a list of gates (with the same type) * are ready to be instantiated. The function runs through the list of * gates and calls the pform_makegate function to make the individual gate. */ static void pform_makegate(PGBuiltin::Type type, struct str_pair_t str, list* delay, const lgate&info, list*attr) { if (info.parms_by_name) { cerr << info.file << ":" << info.lineno << ": Gates do not " "have port names." << endl; error_count += 1; return; } for (list::iterator cur = info.parms->begin() ; cur != info.parms->end() ; ++cur) { pform_declare_implicit_nets(*cur); } perm_string dev_name = lex_strings.make(info.name); PGBuiltin*cur = new PGBuiltin(type, dev_name, info.parms, delay); if (info.range.first) cur->set_range(info.range.first, info.range.second); // The pform_makegates() that calls me will take care of // deleting the attr pointer, so tell the // pform_bind_attributes function to keep the attr object. pform_bind_attributes(cur->attributes, attr, true); cur->strength0(str.str0); cur->strength1(str.str1); FILE_NAME(cur, info.file, info.lineno); if (pform_cur_generate) pform_cur_generate->add_gate(cur); else pform_cur_module.front()->add_gate(cur); } void pform_makegates(const struct vlltype&loc, PGBuiltin::Type type, struct str_pair_t str, list*delay, svector*gates, list*attr) { assert(! pform_cur_module.empty()); if (pform_cur_module.front()->program_block) { cerr << loc << ": error: Gates and switches may not be instantiated in " << "program blocks." << endl; error_count += 1; } if (pform_cur_module.front()->is_interface) { cerr << loc << ": error: Gates and switches may not be instantiated in " << "interfaces." << endl; error_count += 1; } for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) { pform_makegate(type, str, delay, (*gates)[idx], attr); } if (attr) delete attr; delete gates; } /* * A module is different from a gate in that there are different * constraints, and sometimes different syntax. The X_modgate * functions handle the instantiations of modules (and UDP objects) by * making PGModule objects. * * The first pform_make_modgate handles the case of a module * instantiated with ports passed by position. The "wires" is an * ordered array of port expressions. * * The second pform_make_modgate handles the case of a module * instantiated with ports passed by name. The "bind" argument is the * ports matched with names. */ static void pform_make_modgate(perm_string type, perm_string name, struct parmvalue_t*overrides, list*wires, PExpr*msb, PExpr*lsb, const char*fn, unsigned ln) { for (list::iterator idx = wires->begin() ; idx != wires->end() ; ++idx) { pform_declare_implicit_nets(*idx); } PGModule*cur = new PGModule(type, name, wires); FILE_NAME(cur, fn, ln); cur->set_range(msb,lsb); if (overrides && overrides->by_name) { unsigned cnt = overrides->by_name->size(); named*byname = new named[cnt]; list::iterator by_name_cur = overrides->by_name->begin(); for (unsigned idx = 0 ; idx < cnt ; idx += 1, ++ by_name_cur) { byname[idx].name = by_name_cur->name; byname[idx].parm = by_name_cur->parm; } cur->set_parameters(byname, cnt); } else if (overrides && overrides->by_order) { cur->set_parameters(overrides->by_order); } if (pform_cur_generate) pform_cur_generate->add_gate(cur); else pform_cur_module.front()->add_gate(cur); } static void pform_make_modgate(perm_string type, perm_string name, struct parmvalue_t*overrides, list*bind, PExpr*msb, PExpr*lsb, const char*fn, unsigned ln) { unsigned npins = bind->size(); named*pins = new named[npins]; list::iterator bind_cur = bind->begin(); for (unsigned idx = 0 ; idx < npins ; idx += 1, ++bind_cur) { pins[idx].name = bind_cur->name; pins[idx].parm = bind_cur->parm; pform_declare_implicit_nets(bind_cur->parm); } PGModule*cur = new PGModule(type, name, pins, npins); FILE_NAME(cur, fn, ln); cur->set_range(msb,lsb); if (overrides && overrides->by_name) { unsigned cnt = overrides->by_name->size(); named*byname = new named[cnt]; list::iterator by_name_cur = overrides->by_name->begin(); for (unsigned idx = 0 ; idx < cnt ; idx += 1, ++by_name_cur) { byname[idx].name = by_name_cur->name; byname[idx].parm = by_name_cur->parm; } cur->set_parameters(byname, cnt); } else if (overrides && overrides->by_order) { cur->set_parameters(overrides->by_order); } if (pform_cur_generate) pform_cur_generate->add_gate(cur); else pform_cur_module.front()->add_gate(cur); } void pform_make_modgates(const struct vlltype&loc, perm_string type, struct parmvalue_t*overrides, svector*gates) { assert(! pform_cur_module.empty()); if (pform_cur_module.front()->program_block) { cerr << loc << ": error: Module instantiations are not allowed in " << "program blocks." << endl; error_count += 1; } if (pform_cur_module.front()->is_interface) { cerr << loc << ": error: Module instantiations are not allowed in " << "interfaces." << endl; error_count += 1; } for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) { lgate cur = (*gates)[idx]; perm_string cur_name = lex_strings.make(cur.name); if (cur.parms_by_name) { pform_make_modgate(type, cur_name, overrides, cur.parms_by_name, cur.range.first, cur.range.second, cur.file, cur.lineno); } else if (cur.parms) { /* If there are no parameters, the parser will be tricked into thinking it is one empty parameter. This fixes that. */ if ((cur.parms->size() == 1) && (cur.parms->front() == 0)) { delete cur.parms; cur.parms = new list; } pform_make_modgate(type, cur_name, overrides, cur.parms, cur.range.first, cur.range.second, cur.file, cur.lineno); } else { list*wires = new list; pform_make_modgate(type, cur_name, overrides, wires, cur.range.first, cur.range.second, cur.file, cur.lineno); } } delete gates; } static PGAssign* pform_make_pgassign(PExpr*lval, PExpr*rval, list*del, struct str_pair_t str) { /* Implicit declaration of nets on the LHS of a continuous assignment was introduced in IEEE1364-2001. */ if (generation_flag != GN_VER1995) pform_declare_implicit_nets(lval); list*wires = new list; wires->push_back(lval); wires->push_back(rval); PGAssign*cur; if (del == 0) cur = new PGAssign(wires); else cur = new PGAssign(wires, del); cur->strength0(str.str0); cur->strength1(str.str1); if (pform_cur_generate) pform_cur_generate->add_gate(cur); else pform_cur_module.front()->add_gate(cur); return cur; } void pform_make_pgassign_list(list*alist, list*del, struct str_pair_t str, const char* fn, unsigned lineno) { assert(alist->size() % 2 == 0); while (! alist->empty()) { PExpr*lval = alist->front(); alist->pop_front(); PExpr*rval = alist->front(); alist->pop_front(); PGAssign*tmp = pform_make_pgassign(lval, rval, del, str); FILE_NAME(tmp, fn, lineno); } } /* * this function makes the initial assignment to a register as given * in the source. It handles the case where a reg variable is assigned * where it it declared: * * reg foo = ; * * This is equivalent to the combination of statements: * * reg foo; * initial foo = ; * * and that is how it is parsed. This syntax is not part of the * IEEE1364-1995 standard, but is approved by OVI as enhancement * BTF-B14. */ void pform_make_reginit(const struct vlltype&li, perm_string name, PExpr*expr) { if (! pform_at_module_level() && !gn_system_verilog()) { VLerror(li, "error: variable declaration assignments are only " "allowed at the module level."); delete expr; return; } PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { VLerror(li, "internal error: reginit to non-register?"); delete expr; return; } PEIdent*lval = new PEIdent(name); FILE_NAME(lval, li); PAssign*ass = new PAssign(lval, expr, true); FILE_NAME(ass, li); PProcess*top = new PProcess(IVL_PR_INITIAL, ass); FILE_NAME(top, li); pform_put_behavior_in_scope(top); } /* * This function is used by the parser when I have port definition of * the form like this: * * input wire signed [7:0] nm; * * The port_type, type, signed_flag and range are known all at once, * so we can create the PWire object all at once instead of piecemeal * as is done for the old method. */ void pform_module_define_port(const struct vlltype&li, perm_string name, NetNet::PortType port_kind, NetNet::Type type, data_type_t*vtype, list*attr) { struct_type_t*struct_type = 0; ivl_variable_type_t data_type = IVL_VT_NO_TYPE; bool signed_flag = false; PWire*cur = pform_get_wire_in_scope(name); if (cur) { ostringstream msg; msg << name << " definition conflicts with " << "definition at " << cur->get_fileline() << "."; VLerror(msg.str().c_str()); return; } // Packed ranges list*prange = 0; // Unpacked dimensions list*urange = 0; // If this is an unpacked array, then split out the parts that // we can send to the PWire object that we create. if (uarray_type_t*uarr_type = dynamic_cast (vtype)) { urange = uarr_type->dims.get(); vtype = uarr_type->base_type; } if (vector_type_t*vec_type = dynamic_cast (vtype)) { data_type = vec_type->base_type; signed_flag = vec_type->signed_flag; prange = vec_type->pdims.get(); if (vec_type->reg_flag) type = NetNet::REG; } else if (atom2_type_t*atype = dynamic_cast(vtype)) { data_type = IVL_VT_BOOL; signed_flag = atype->signed_flag; prange = make_range_from_width(atype->type_code); } else if (real_type_t*rtype = dynamic_cast(vtype)) { data_type = IVL_VT_REAL; signed_flag = true; prange = 0; if (rtype->type_code != real_type_t::REAL) { VLerror(li, "sorry: Only real (not shortreal) supported here (%s:%d).", __FILE__, __LINE__); } } else if ((struct_type = dynamic_cast(vtype))) { data_type = struct_type->figure_packed_base_type(); signed_flag = false; prange = 0; } else if (enum_type_t*enum_type = dynamic_cast(vtype)) { data_type = enum_type->base_type; signed_flag = enum_type->signed_flag; prange = enum_type->range.get(); } else if (vtype) { VLerror(li, "sorry: Given type %s not supported here (%s:%d).", typeid(*vtype).name(), __FILE__, __LINE__); } // The default type for all flavor of ports is LOGIC. if (data_type == IVL_VT_NO_TYPE) data_type = IVL_VT_LOGIC; cur = new PWire(name, type, port_kind, data_type); FILE_NAME(cur, li); cur->set_signed(signed_flag); if (struct_type) { cur->set_data_type(struct_type); } else if (prange == 0) { cur->set_range_scalar((type == NetNet::IMPLICIT) ? SR_PORT : SR_BOTH); } else { cur->set_range(*prange, (type == NetNet::IMPLICIT) ? SR_PORT : SR_BOTH); } if (urange) { cur->set_unpacked_idx(*urange); } pform_bind_attributes(cur->attributes, attr); pform_put_wire_in_scope(name, cur); } /* * This function makes a single signal (a wire, a reg, etc) as * requested by the parser. The name is unscoped, so I attach the * current scope to it (with the scoped_name function) and I try to * resolve it with an existing PWire in the scope. * * The wire might already exist because of an implicit declaration in * a module port, i.e.: * * module foo (bar... * * reg bar; * * The output (or other port direction indicator) may or may not have * been seen already, so I do not do any checking with it yet. But I * do check to see if the name has already been declared, as this * function is called for every declaration. */ static PWire* pform_get_or_make_wire(const vlltype&li, perm_string name, NetNet::Type type, NetNet::PortType ptype, ivl_variable_type_t dtype) { PWire*cur = pform_get_wire_in_scope(name); // If the wire already exists but isn't yet fully defined, // carry on adding details. if (cur && (cur->get_data_type() == IVL_VT_NO_TYPE || cur->get_wire_type() == NetNet::IMPLICIT) ) { // If this is not implicit ("implicit" meaning we don't // know what the type is yet) then set the type now. if (type != NetNet::IMPLICIT) { bool rc = cur->set_wire_type(type); if (rc == false) { ostringstream msg; msg << name << " " << type << " definition conflicts with " << cur->get_wire_type() << " definition at " << cur->get_fileline() << "."; VLerror(msg.str().c_str()); } FILE_NAME(cur, li.text, li.first_line); } return cur; } // If the wire already exists and is fully defined, this // must be a redeclaration. Start again with a new wire. if (cur) { LineInfo tloc; FILE_NAME(&tloc, li); cerr << tloc.get_fileline() << ": error: duplicate declaration " "for net or variable '" << name << "' in '" << pform_cur_module.front()->mod_name() << "'." << endl; error_count += 1; delete cur; } cur = new PWire(name, type, ptype, dtype); FILE_NAME(cur, li.text, li.first_line); pform_put_wire_in_scope(name, cur); return cur; } /* * this is the basic form of pform_makewire. This takes a single simple * name, port type, net type, data type, and attributes, and creates * the variable/net. Other forms of pform_makewire ultimately call * this one to create the wire and stash it. */ void pform_makewire(const vlltype&li, perm_string name, NetNet::Type type, NetNet::PortType pt, ivl_variable_type_t dt, list*attr) { PWire*cur = pform_get_or_make_wire(li, name, type, pt, dt); if (! cur) { cur = new PWire(name, type, pt, dt); FILE_NAME(cur, li.text, li.first_line); } bool flag; switch (dt) { case IVL_VT_REAL: flag = cur->set_data_type(dt); if (flag == false) { cerr << cur->get_fileline() << ": internal error: " << " wire data type handling mismatch. Cannot change " << cur->get_data_type() << " to " << dt << "." << endl; } ivl_assert(*cur, flag); cur->set_range_scalar(SR_NET); cur->set_signed(true); break; default: break; } if (attr) { for (list::iterator attr_cur = attr->begin() ; attr_cur != attr->end() ; ++attr_cur) { cur->attributes[attr_cur->name] = attr_cur->parm; } } } /* * This form takes a list of names and some type information, and * generates a bunch of variables/nets. We use the basic * pform_makewire above. */ void pform_makewire(const vlltype&li, list*range, bool signed_flag, list*names, NetNet::Type type, NetNet::PortType pt, ivl_variable_type_t dt, list*attr, PWSRType rt) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; pform_makewire(li, txt, type, pt, dt, attr); /* This has already been done for real variables. */ if (dt != IVL_VT_REAL) { pform_set_net_range(txt, type, range, signed_flag, dt, rt, 0); } } delete names; delete range; delete attr; } /* * This form makes nets with delays and continuous assignments. */ void pform_makewire(const vlltype&li, list*delay, str_pair_t str, net_decl_assign_t*decls, NetNet::Type type, data_type_t*data_type) { // The decls pointer is a circularly linked list. net_decl_assign_t*first = decls->next; list*names = new list; // Go through the circularly linked list non-destructively. do { pform_makewire(li, first->name, type, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); names->push_back(first->name); first = first->next; } while (first != decls->next); // The pform_set_data_type function will delete the names list. pform_set_data_type(li, data_type, names, type, 0); // This time, go through the list, deleting cells as I'm done. first = decls->next; decls->next = 0; while (first) { net_decl_assign_t*next = first->next; PWire*cur = pform_get_wire_in_scope(first->name); if (cur != 0) { PEIdent*lval = new PEIdent(first->name); FILE_NAME(lval, li.text, li.first_line); PGAssign*ass = pform_make_pgassign(lval, first->expr, delay, str); FILE_NAME(ass, li.text, li.first_line); } delete first; first = next; } } /* * This should eventually replace the form above that takes a * net_decl_assign_t argument. */ void pform_makewire(const struct vlltype&li, std::list*, str_pair_t , std::list*assign_list, NetNet::Type type, data_type_t*data_type) { if ((lexical_scope == 0) && (generation_flag < GN_VER2005_SV)) { VLerror(li, "error: variable declarations must be contained within a module."); return; } if (lexical_scope == 0) { VLerror(li, "sorry: variable declarations in the $root scope are not yet supported."); return; } list*names = new list; for (list::iterator cur = assign_list->begin() ; cur != assign_list->end() ; ++ cur) { decl_assignment_t* curp = *cur; names->push_back(curp->name); } pform_set_data_type(li, data_type, names, type, 0); while (! assign_list->empty()) { decl_assignment_t*first = assign_list->front(); assign_list->pop_front(); // For now, do not handle assignment expressions. assert(! first->expr.get()); delete first; } } /* * This function is called by the parser to create task ports. The * resulting wire (which should be a register) is put into a list to * be packed into the task parameter list. * * It is possible that the wire (er, register) was already created, * but we know that if the name matches it is a part of the current * task, so in that case I just assign direction to it. * * The following example demonstrates some of the issues: * * task foo; * input a; * reg a, b; * input b; * [...] * endtask * * This function is called when the parser matches the "input a" and * the "input b" statements. For ``a'', this function is called before * the wire is declared as a register, so I create the foo.a * wire. For ``b'', I will find that there is already a foo.b and I * just set the port direction. In either case, the ``reg a, b'' * statement is caught by the block_item non-terminal and processed * there. * * Ports are implicitly type reg, because it must be possible for the * port to act as an l-value in a procedural assignment. It is obvious * for output and inout ports that the type is reg, because the task * only contains behavior (no structure) to a procedural assignment is * the *only* way to affect the output. It is less obvious for input * ports, but in practice an input port receives its value as if by a * procedural assignment from the calling behavior. * * This function also handles the input ports of function * definitions. Input ports to function definitions have the same * constraints as those of tasks, so this works fine. Functions have * no output or inout ports. */ vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, ivl_variable_type_t vtype, bool signed_flag, list*range, list*names, bool isint) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); assert(names); vector*res = new vector(0); for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string name = *cur; /* Look for a preexisting wire. If it exists, set the port direction. If not, create it. */ PWire*curw = pform_get_wire_in_scope(name); if (curw) { curw->set_port_type(pt); } else { curw = new PWire(name, NetNet::IMPLICIT_REG, pt, vtype); FILE_NAME(curw, loc); pform_put_wire_in_scope(name, curw); } curw->set_signed(signed_flag); if (isint) { bool flag = curw->set_wire_type(NetNet::INTEGER); assert(flag); } /* If there is a range involved, it needs to be set. */ if (range) { curw->set_range(*range, SR_PORT); } res->push_back(pform_tf_port_t(curw)); } delete range; return res; } static vector*do_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, ivl_variable_type_t var_type, data_type_t*data_type, list*names) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); assert(names); vector*res = new vector(0); for (list::iterator cur = names->begin() ; cur != names->end() ; ++cur) { perm_string name = *cur; PWire*curw = pform_get_wire_in_scope(name); if (curw) { curw->set_port_type(pt); } else { curw = new PWire(name, NetNet::IMPLICIT_REG, pt, var_type); FILE_NAME(curw, loc); curw->set_data_type(data_type); pform_put_wire_in_scope(name, curw); } res->push_back(pform_tf_port_t(curw)); } return res; } vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, data_type_t*vtype, list*names) { vector*ret = NULL; std::list*unpacked_dims = NULL; if (uarray_type_t*uarray = dynamic_cast (vtype)) { unpacked_dims = uarray->dims.get(); vtype = uarray->base_type; } if (atom2_type_t*atype = dynamic_cast (vtype)) { list*range_tmp = make_range_from_width(atype->type_code); ret = pform_make_task_ports(loc, pt, IVL_VT_BOOL, atype->signed_flag, range_tmp, names); } if (vector_type_t*vec_type = dynamic_cast (vtype)) { ret = pform_make_task_ports(loc, pt, vec_type->base_type, vec_type->signed_flag, copy_range(vec_type->pdims.get()), names, vec_type->integer_flag); } if (/*real_type_t*real_type = */ dynamic_cast (vtype)) { ret = pform_make_task_ports(loc, pt, IVL_VT_REAL, true, 0, names); } if (dynamic_cast (vtype)) { ret = pform_make_task_ports(loc, pt, IVL_VT_STRING, false, 0, names); } if (class_type_t*class_type = dynamic_cast (vtype)) { ret = do_make_task_ports(loc, pt, IVL_VT_CLASS, class_type, names); } ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, names); if (unpacked_dims) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { PWire*wire = pform_get_wire_in_scope(*cur); wire->set_unpacked_idx(*unpacked_dims); } } delete names; return ret; } /* * The parser calls this in the rule that matches increment/decrement * statements. The rule that does the matching creates a PEUnary with * all the information we need, but here we convert that expression to * a compressed assignment statement. */ PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc, PExpr*exp) { PEUnary*expu = dynamic_cast (exp); ivl_assert(*exp, expu != 0); char use_op = 0; switch (expu->get_op()) { case 'i': case 'I': use_op = '+'; break; case 'd': case 'D': use_op = '-'; break; default: ivl_assert(*exp, 0); break; } PExpr*lval = expu->get_expr(); PExpr*rval = new PENumber(new verinum((uint64_t)1, 1)); FILE_NAME(rval, loc); PAssign*tmp = new PAssign(lval, use_op, rval); FILE_NAME(tmp, loc); delete exp; return tmp; } void pform_set_attrib(perm_string name, perm_string key, char*value) { if (PWire*cur = lexical_scope->wires_find(name)) { cur->attributes[key] = new PEString(value); } else if (PGate*curg = pform_cur_module.front()->get_gate(name)) { curg->attributes[key] = new PEString(value); } else { delete[] value; VLerror("Unable to match name for setting attribute."); } } /* * Set the attribute of a TYPE. This is different from an object in * that this applies to every instantiation of the given type. */ void pform_set_type_attrib(perm_string name, const string&key, char*value) { map::const_iterator udp = pform_primitives.find(name); if (udp == pform_primitives.end()) { VLerror("type name is not (yet) defined."); delete[] value; return; } (*udp).second ->attributes[key] = new PEString(value); } /* * This function attaches a memory index range to an existing * register. (The named wire must be a register. */ void pform_set_reg_idx(perm_string name, list*indices) { PWire*cur = lexical_scope->wires_find(name); if (cur == 0) { VLerror("internal error: name is not a valid memory for index."); return; } if (indices && !indices->empty()) cur->set_unpacked_idx(*indices); } LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag, bool low_open, PExpr*low_expr, bool hig_open, PExpr*hig_expr) { // Detect +-inf and make the the *_open flags false to force // the range interpretation as inf. if (low_expr == 0) low_open = false; if (hig_expr == 0) hig_open = false; LexicalScope::range_t*tmp = new LexicalScope::range_t; tmp->exclude_flag = exclude_flag; tmp->low_open_flag = low_open; tmp->low_expr = low_expr; tmp->high_open_flag = hig_open; tmp->high_expr = hig_expr; tmp->next = 0; return tmp; } void pform_set_parameter(const struct vlltype&loc, perm_string name, ivl_variable_type_t type, bool signed_flag, list*range, PExpr*expr, LexicalScope::range_t*value_range) { LexicalScope*scope = lexical_scope; if ((scope == 0) && (generation_flag < GN_VER2005_SV)) { VLerror(loc, "error: parameter declarations must be contained within a module."); return; } if (scope == 0) { VLerror(loc, "sorry: parameter declarations in the $root scope are not yet supported."); return; } if (scope == pform_cur_generate) { VLerror("parameter declarations are not permitted in generate blocks"); return; } // Check if the parameter name is already in the dictionary. if (scope->parameters.find(name) != scope->parameters.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for parameter '" << name << "' in '" << pform_cur_module.front()->mod_name() << "'." << endl; error_count += 1; } if (scope->localparams.find(name) != scope->localparams.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: localparam and " << "parameter in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } // Only a Module scope has specparams. if ((dynamic_cast (scope)) && (scope == pform_cur_module.front()) && (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: specparam and " "parameter in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } assert(expr); Module::param_expr_t&parm = scope->parameters[name]; FILE_NAME(&parm, loc); parm.expr = expr; parm.type = type; if (range) { assert(range->size() == 1); pform_range_t&rng = range->front(); assert(rng.first); assert(rng.second); parm.msb = rng.first; parm.lsb = rng.second; } else { parm.msb = 0; parm.lsb = 0; } parm.signed_flag = signed_flag; parm.range = value_range; // Only a Module keeps the position of the parameter. if ((dynamic_cast (scope)) && (scope == pform_cur_module.front())) pform_cur_module.front()->param_names.push_back(name); } void pform_set_localparam(const struct vlltype&loc, perm_string name, ivl_variable_type_t type, bool signed_flag, list*range, PExpr*expr) { LexicalScope*scope = lexical_scope; if ((scope == 0) && (generation_flag < GN_VER2005_SV)) { VLerror(loc, "error: localparam declarations must be contained within a module."); return; } if (scope == 0) { VLerror(loc, "sorry: localparam declarations in the $root scope are not yet supported."); return; } // Check if the localparam name is already in the dictionary. if (scope->localparams.find(name) != scope->localparams.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for localparam '" << name << "' in '" << pform_cur_module.front()->mod_name() << "'." << endl; error_count += 1; } if (scope->parameters.find(name) != scope->parameters.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: parameter and " << "localparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } if ((! pform_cur_module.empty()) && (scope == pform_cur_module.front()) && (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: specparam and " "localparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } assert(expr); Module::param_expr_t&parm = scope->localparams[name]; FILE_NAME(&parm, loc); parm.expr = expr; parm.type = type; if (range) { assert(range->size() == 1); pform_range_t&rng = range->front(); assert(rng.first); assert(rng.second); parm.msb = rng.first; parm.lsb = rng.second; } else { parm.msb = 0; parm.lsb = 0; } parm.signed_flag = signed_flag; parm.range = 0; } void pform_set_specparam(const struct vlltype&loc, perm_string name, list*range, PExpr*expr) { assert(! pform_cur_module.empty()); Module*scope = pform_cur_module.front(); assert(scope == lexical_scope); // Check if the specparam name is already in the dictionary. if (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for specparam '" << name << "' in '" << pform_cur_module.front()->mod_name() << "'." << endl; error_count += 1; } if (scope->parameters.find(name) != scope->parameters.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: parameter and " "specparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } if (scope->localparams.find(name) != scope->localparams.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: localparam and " "specparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } assert(expr); Module::param_expr_t&parm = pform_cur_module.front()->specparams[name]; FILE_NAME(&parm, loc); parm.expr = expr; if (range) { assert(range->size() == 1); pform_range_t&rng = range->front(); assert(rng.first); assert(rng.second); parm.type = IVL_VT_LOGIC; parm.msb = rng.first; parm.lsb = rng.second; } else { parm.type = IVL_VT_NO_TYPE; parm.msb = 0; parm.lsb = 0; } parm.signed_flag = false; parm.range = 0; } void pform_set_defparam(const pform_name_t&name, PExpr*expr) { assert(expr); if (pform_cur_generate) pform_cur_generate->defparms.push_back(make_pair(name,expr)); else pform_cur_module.front()->defparms.push_back(make_pair(name,expr)); } /* * Specify paths. */ extern PSpecPath* pform_make_specify_path(const struct vlltype&li, list*src, char pol, bool full_flag, list*dst) { PSpecPath*path = new PSpecPath(src->size(), dst->size(), pol, full_flag); FILE_NAME(path, li.text, li.first_line); unsigned idx; list::const_iterator cur; idx = 0; for (idx = 0, cur = src->begin() ; cur != src->end() ; ++ idx, ++ cur) { path->src[idx] = *cur; } assert(idx == path->src.size()); delete src; for (idx = 0, cur = dst->begin() ; cur != dst->end() ; ++ idx, ++ cur) { path->dst[idx] = *cur; } assert(idx == path->dst.size()); delete dst; return path; } extern PSpecPath*pform_make_specify_edge_path(const struct vlltype&li, int edge_flag, /*posedge==true */ list*src, char pol, bool full_flag, list*dst, PExpr*data_source_expression) { PSpecPath*tmp = pform_make_specify_path(li, src, pol, full_flag, dst); tmp->edge = edge_flag; tmp->data_source_expression = data_source_expression; return tmp; } extern PSpecPath* pform_assign_path_delay(PSpecPath*path, list*del) { if (path == 0) return 0; assert(path->delays.empty()); path->delays.resize(del->size()); for (unsigned idx = 0 ; idx < path->delays.size() ; idx += 1) { path->delays[idx] = del->front(); del->pop_front(); } delete del; return path; } extern void pform_module_specify_path(PSpecPath*obj) { if (obj == 0) return; pform_cur_module.front()->specify_paths.push_back(obj); } static void pform_set_port_type(perm_string name, NetNet::PortType pt, const char*file, unsigned lineno) { PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { cur = new PWire(name, NetNet::IMPLICIT, NetNet::PIMPLICIT, IVL_VT_NO_TYPE); FILE_NAME(cur, file, lineno); pform_put_wire_in_scope(name, cur); } switch (cur->get_port_type()) { case NetNet::PIMPLICIT: if (! cur->set_port_type(pt)) VLerror("error setting port direction."); break; case NetNet::NOT_A_PORT: cerr << file << ":" << lineno << ": error: " << "port " << name << " is not in the port list." << endl; error_count += 1; break; default: cerr << file << ":" << lineno << ": error: " << "port " << name << " already has a port declaration." << endl; error_count += 1; break; } } void pform_set_port_type(const struct vlltype&li, list*names, list*range, bool signed_flag, NetNet::PortType pt, list*attr) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; pform_set_port_type(txt, pt, li.text, li.first_line); pform_set_net_range(txt, NetNet::NONE, range, signed_flag, IVL_VT_NO_TYPE, SR_PORT, attr); } delete names; delete range; delete attr; } static void pform_set_integer_2atom(uint64_t width, bool signed_flag, perm_string name, NetNet::Type net_type, list*attr) { PWire*cur = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_BOOL); assert(cur); cur->set_signed(signed_flag); pform_range_t rng; rng.first = new PENumber(new verinum(width-1, integer_width)); rng.second = new PENumber(new verinum((uint64_t)0, integer_width)); listrlist; rlist.push_back(rng); cur->set_range(rlist, SR_NET); pform_bind_attributes(cur->attributes, attr, true); } static void pform_set_integer_2atom(uint64_t width, bool signed_flag, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; pform_set_integer_2atom(width, signed_flag, txt, net_type, attr); } } template static void pform_set2_data_type(const struct vlltype&li, T*data_type, perm_string name, NetNet::Type net_type, list*attr) { ivl_variable_type_t base_type = data_type->figure_packed_base_type(); if (base_type == IVL_VT_NO_TYPE) { VLerror(li, "Compound type is not PACKED in this context."); } PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, base_type); assert(net); net->set_data_type(data_type); pform_bind_attributes(net->attributes, attr, true); } template static void pform_set2_data_type(const struct vlltype&li, T*data_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { pform_set2_data_type(li, data_type, *cur, net_type, attr); } } static void pform_set_enum(enum_type_t*enum_type, perm_string name, NetNet::Type net_type, std::list*attr) { PWire*cur = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, enum_type->base_type); assert(cur); cur->set_signed(enum_type->signed_flag); assert(enum_type->range.get() != 0); assert(enum_type->range->size() == 1); //XXXXcur->set_range(*enum_type->range, SR_NET); cur->set_data_type(enum_type); // If this is an integer enumeration switch the wire to an integer. if (enum_type->integer_flag) { bool res = cur->set_wire_type(NetNet::INTEGER); assert(res); } pform_bind_attributes(cur->attributes, attr, true); } static void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, list*names, NetNet::Type net_type, std::list*attr) { // By definition, the base type can only be IVL_VT_LOGIC or // IVL_VT_BOOL. assert(enum_type->base_type==IVL_VT_LOGIC || enum_type->base_type==IVL_VT_BOOL); assert(enum_type->range.get() != 0); assert(enum_type->range->size() == 1); // Add the file and line information to the enumeration type. FILE_NAME(&(enum_type->li), li); // Attach the enumeration to the current scope. pform_put_enum_type_in_scope(enum_type); // Now apply the checked enumeration type to the variables // that are being declared with this type. for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { perm_string txt = *cur; pform_set_enum(enum_type, txt, net_type, attr); } } /* * This function detects the derived class for the given type and * dispatches the type to the proper subtype function. */ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list*names, NetNet::Type net_type, list*attr) { const std::list*unpacked_dims = NULL; if (data_type == 0) { VLerror(li, "internal error: data_type==0."); assert(0); } if(uarray_type_t*uarray_type = dynamic_cast (data_type)) { unpacked_dims = uarray_type->dims.get(); data_type = uarray_type->base_type; } if (atom2_type_t*atom2_type = dynamic_cast (data_type)) { pform_set_integer_2atom(atom2_type->type_code, atom2_type->signed_flag, names, net_type, attr); } else if (struct_type_t*struct_type = dynamic_cast (data_type)) { pform_set_struct_type(struct_type, names, net_type, attr); } else if (enum_type_t*enum_type = dynamic_cast (data_type)) { pform_set_enum(li, enum_type, names, net_type, attr); } else if (vector_type_t*vec_type = dynamic_cast (data_type)) { if (net_type==NetNet::REG && vec_type->integer_flag) net_type=NetNet::INTEGER; pform_set_net_range(names, vec_type->pdims.get(), vec_type->signed_flag, vec_type->base_type, net_type, attr); } else if (/*real_type_t*real_type =*/ dynamic_cast (data_type)) { pform_set_net_range(names, 0, true, IVL_VT_REAL, net_type, attr); } else if (class_type_t*class_type = dynamic_cast (data_type)) { pform_set_class_type(class_type, names, net_type, attr); } else if (parray_type_t*array_type = dynamic_cast (data_type)) { pform_set2_data_type(li, array_type, names, net_type, attr); } else if (string_type_t*string_type = dynamic_cast (data_type)) { pform_set_string_type(string_type, names, net_type, attr); } else { VLerror(li, "internal error: Unexpected data_type."); assert(0); } if(unpacked_dims) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { PWire*wire = pform_get_wire_in_scope(*cur); wire->set_unpacked_idx(*unpacked_dims); } } delete names; } vector* pform_make_udp_input_ports(list*names) { vector*out = new vector(names->size()); unsigned idx = 0; for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; PWire*pp = new PWire(txt, NetNet::IMPLICIT, NetNet::PINPUT, IVL_VT_LOGIC); (*out)[idx] = pp; idx += 1; } delete names; return out; } PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, list*attr) { PProcess*pp = new PProcess(type, st); // If we are in a part of the code where the meta-comment // synthesis translate_off is in effect, then implicitly add // the ivl_synthesis_off attribute to any behavioral code that // we run into. if (pform_mc_translate_flag == false) { if (attr == 0) attr = new list; named_pexpr_t tmp; tmp.name = perm_string::literal("ivl_synthesis_off"); tmp.parm = 0; attr->push_back(tmp); } pform_bind_attributes(pp->attributes, attr); pform_put_behavior_in_scope(pp); ivl_assert(*st, ! pform_cur_module.empty()); if (pform_cur_module.front()->program_block && type == IVL_PR_ALWAYS) { cerr << st->get_fileline() << ": error: Always statements not allowed" << " in program blocks." << endl; error_count += 1; } return pp; } void pform_start_modport_item(const struct vlltype&loc, const char*name) { Module*scope = pform_cur_module.front(); ivl_assert(loc, scope && scope->is_interface); ivl_assert(loc, pform_cur_modport == 0); perm_string use_name = lex_strings.make(name); pform_cur_modport = new PModport(use_name); FILE_NAME(pform_cur_modport, loc); if (scope->modports.find(use_name) != scope->modports.end()) { cerr << loc << ": error: duplicate declaration for modport '" << name << "' in '" << scope->mod_name() << "'." << endl; error_count += 1; } scope->modports[use_name] = pform_cur_modport; delete[] name; } void pform_end_modport_item(const struct vlltype&loc) { ivl_assert(loc, pform_cur_modport); pform_cur_modport = 0; } void pform_add_modport_port(const struct vlltype&loc, NetNet::PortType port_type, perm_string name, PExpr*expr) { ivl_assert(loc, pform_cur_modport); if (pform_cur_modport->simple_ports.find(name) != pform_cur_modport->simple_ports.end()) { cerr << loc << ": error: duplicate declaration of port '" << name << "' in modport list '" << pform_cur_modport->name() << "'." << endl; error_count += 1; } pform_cur_modport->simple_ports[name] = make_pair(port_type, expr); } FILE*vl_input = 0; extern void reset_lexor(); int pform_parse(const char*path, FILE*file) { vl_file = path; if (file == 0) { if (strcmp(path, "-") == 0) vl_input = stdin; else vl_input = fopen(path, "r"); if (vl_input == 0) { cerr << "Unable to open " < # include # include # include # include /* * These classes implement the parsed form (P-form for short) of the * original Verilog source. the parser generates the pform for the * convenience of later processing steps. */ /* * Wire objects represent the named wires (of various flavor) declared * in the source. * * Gate objects are the functional modules that are connected together * by wires. * * Wires and gates, connected by joints, represent a netlist. The * netlist is therefore a representation of the desired circuit. */ class PGate; class PExpr; class PPackage; class PSpecPath; class PClass; class PPackage; struct vlltype; /* * The min:typ:max expression s selected at parse time using the * enumeration. When the compiler makes a choice, it also prints a * warning if min_typ_max_warn > 0. */ extern enum MIN_TYP_MAX { MIN, TYP, MAX } min_typ_max_flag; extern unsigned min_typ_max_warn; PExpr* pform_select_mtm_expr(PExpr*min, PExpr*typ, PExpr*max); /* * This flag is true if the lexor thinks we are in a library source * file. */ extern bool pform_library_flag; /* * These type are lexical types -- that is, types that are used as * lexical values to decorate the parse tree during parsing. They are * not in any way preserved once parsing is done. */ /* This is information about port name information for named port connections. */ struct parmvalue_t { list*by_order; list*by_name; }; struct str_pair_t { ivl_drive_t str0, str1; }; struct net_decl_assign_t { perm_string name; PExpr*expr; struct net_decl_assign_t*next; }; /* The lgate is gate instantiation information. */ struct lgate { inline lgate(int =0) : parms(0), parms_by_name(0), file(NULL), lineno(0) { } string name; list*parms; list*parms_by_name; pform_range_t range; const char* file; unsigned lineno; }; extern std::list* make_range_from_width(uint64_t wid); extern std::list* copy_range(std::list* orig); /* Use this function to transform the parted form of the attribute list to the attribute map that is used later. */ extern void pform_bind_attributes(map&attributes, list*attr, bool keep_attr =false); /* The lexor calls this function to change the default nettype. */ extern void pform_set_default_nettype(NetNet::Type net, const char*file, unsigned lineno); /* Return true if currently processing a program block. This can be used to reject statements that cannot exist in program blocks. */ extern bool pform_in_program_block(void); /* Return true if currently processing an interface. This can be used to reject statements that cannot exist in interfaces. */ extern bool pform_in_interface(void); /* * Look for the given wire in the current lexical scope. If the wire * (including variables of any type) cannot be found in the current * scope, then return 0. */ extern PWire* pform_get_wire_in_scope(perm_string name); extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type); /* * The parser uses startmodule and endmodule together to build up a * module as it parses it. The startmodule tells the pform code that a * module has been noticed in the source file and the following events * are to apply to the scope of that module. The endmodule causes the * pform to close up and finish the named module. * * The program_block flag indicates that the module is actually a program * block. The is_interface flag indicates that the module is actually * an interface. These flags have implications during parse and during * elaboration/code generation. */ extern void pform_startmodule(const struct vlltype&loc, const char*name, bool program_block, bool is_interface, list*attr); extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*); /* This function is used to support the port definition in a port_definition_list. In this case, we have everything needed to define the port, all in one place. */ extern void pform_module_define_port(const struct vlltype&li, perm_string name, NetNet::PortType, NetNet::Type type, data_type_t*vtype, list*attr); extern Module::port_t* pform_module_port_reference(perm_string name, const char*file, unsigned lineno); extern void pform_endmodule(const char*, bool inside_celldefine, Module::UCDriveType uc_drive_def); extern void pform_start_class_declaration(const struct vlltype&loc, class_type_t*type, data_type_t*base_type, std::list*base_exprs); extern void pform_class_property(const struct vlltype&loc, property_qualifier_t pq, data_type_t*data_type, std::list*decls); extern void pform_set_this_class(const struct vlltype&loc, PTaskFunc*net); extern void pform_set_constructor_return(PFunction*net); extern void pform_end_class_declaration(const struct vlltype&loc); extern void pform_make_udp(perm_string name, list*parms, std::vector*decl, list*table, Statement*init, const char*file, unsigned lineno); extern void pform_make_udp(perm_string name, bool sync_flag, perm_string out_name, PExpr*sync_init, list*parms, list*table, const char*file, unsigned lineno); /* * Package related functions. */ extern void pform_start_package_declaration(const struct vlltype&loc, const char*type); extern void pform_end_package_declaration(const struct vlltype&loc); extern void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ident); extern PExpr* pform_package_ident(const struct vlltype&loc, PPackage*pkg, pform_name_t*ident); /* * Interface related functions. */ extern void pform_start_modport_item(const struct vlltype&loc, const char*name); extern void pform_end_modport_item(const struct vlltype&loc); extern void pform_add_modport_port(const struct vlltype&loc, NetNet::PortType port_type, perm_string name, PExpr*expr); /* * This creates an identifier aware of names that may have been * imported from other packages. */ extern PEIdent* pform_new_ident(const pform_name_t&name); /* * Enter/exit name scopes. The push_scope function pushes the scope * name string onto the scope hierarchy. The pop pulls it off and * deletes it. Thus, the string pushed must be allocated. */ extern void pform_pop_scope(); /* * Peek at the current (most recently active) scope. */ extern LexicalScope* pform_peek_scope(); extern PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name); extern PFunction*pform_push_constructor_scope(const struct vlltype&loc); extern PPackage* pform_push_package_scope(const struct vlltype&loc, perm_string name); extern PTask*pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto); extern PFunction*pform_push_function_scope(const struct vlltype&loc, const char*name, bool is_auto); extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt); extern void pform_put_behavior_in_scope(AProcess*proc); extern verinum* pform_verinum_with_size(verinum*s, verinum*val, const char*file, unsigned lineno); /* * This function takes the list of names as new genvars to declare in * the current module or generate scope. */ extern void pform_genvars(const struct vlltype&li, list*names); extern void pform_start_generate_for(const struct vlltype&li, char*ident1, PExpr*init, PExpr*test, char*ident2, PExpr*next); extern void pform_start_generate_if(const struct vlltype&li, PExpr*test); extern void pform_start_generate_else(const struct vlltype&li); extern void pform_start_generate_case(const struct vlltype&lp, PExpr*test); extern void pform_start_generate_nblock(const struct vlltype&lp, char*name); extern void pform_generate_case_item(const struct vlltype&lp, list*test); extern void pform_generate_block_name(char*name); extern void pform_endgenerate(); /* * This function returns the lexically containing generate scheme, if * there is one. The parser may use this to check if we are within a * generate scheme. */ extern PGenerate* pform_parent_generate(void); extern void pform_set_typedef(perm_string name, data_type_t*data_type, std::list*unp_ranges); /* * This function makes a PECallFunction of the named function. Decide * if this function is in the scope or is imported from a package. */ extern PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, const std::list&parms); extern PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const std::list&parms); extern void pform_make_foreach_declarations(const struct vlltype&loc, std::list*loop_vars); extern PForeach* pform_make_foreach(const struct vlltype&loc, char*ident, std::list*loop_vars, Statement*stmt); /* * The makewire functions announce to the pform code new wires. These * go into a module that is currently opened. */ extern void pform_makewire(const struct vlltype&li, perm_string name, NetNet::Type type, NetNet::PortType pt, ivl_variable_type_t, list*attr); /* This form handles simple declarations */ extern void pform_makewire(const struct vlltype&li, list*range, bool signed_flag, list*names, NetNet::Type type, NetNet::PortType, ivl_variable_type_t, list*attr, PWSRType rt = SR_NET); /* This form handles assignment declarations. */ extern void pform_makewire(const struct vlltype&li, list*delay, str_pair_t str, net_decl_assign_t*assign_list, NetNet::Type type, data_type_t*data_type); extern void pform_makewire(const struct vlltype&li, std::list*delay, str_pair_t str, std::list*assign_list, NetNet::Type type, data_type_t*data_type); /* This form handles nets declared as structures. (See pform_struct_type.cc) */ extern void pform_makewire(const struct vlltype&li, struct_type_t*struct_type, NetNet::PortType, list*names, list*attr); extern void pform_make_reginit(const struct vlltype&li, perm_string name, PExpr*expr); /* Look up the names of the wires, and set the port type, i.e. input, output or inout. If the wire does not exist, create it. The second form takes a single name. */ extern void pform_set_port_type(const struct vlltype&li, list*names, list*range, bool signed_flag, NetNet::PortType, list*attr); extern void pform_set_reg_idx(perm_string name, std::list*indices); extern void pform_set_data_type(const struct vlltype&li, data_type_t*, list*names, NetNet::Type net_type, list*attr); extern void pform_set_struct_type(struct_type_t*struct_type, std::list*names, NetNet::Type net_type, std::list*attr); extern void pform_set_string_type(const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); extern void pform_set_class_type(class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); /* pform_set_attrib and pform_set_type_attrib exist to support the $attribute syntax, which can only set string values to attributes. The functions keep the value strings that are passed in. */ extern void pform_set_attrib(perm_string name, perm_string key, char*value); extern void pform_set_type_attrib(perm_string name, const string&key, char*value); extern LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag, bool low_open, PExpr*low_expr, bool hig_open, PExpr*hig_expr); extern void pform_set_parameter(const struct vlltype&loc, perm_string name, ivl_variable_type_t type, bool signed_flag, list*range, PExpr*expr, LexicalScope::range_t*value_range); extern void pform_set_localparam(const struct vlltype&loc, perm_string name, ivl_variable_type_t type, bool signed_flag, list*range, PExpr*expr); extern void pform_set_specparam(const struct vlltype&loc, perm_string name, list*range, PExpr*expr); extern void pform_set_defparam(const pform_name_t&name, PExpr*expr); /* * Functions related to specify blocks. */ extern PSpecPath*pform_make_specify_path(const struct vlltype&li, list*src, char pol, bool full_flag, list*dst); extern PSpecPath*pform_make_specify_edge_path(const struct vlltype&li, int edge_flag, /*posedge==true */ list*src, char pol, bool full_flag, list*dst, PExpr*data_source_expression); extern PSpecPath*pform_assign_path_delay(PSpecPath*obj, list*delays); extern void pform_module_specify_path(PSpecPath*obj); /* * pform_make_behavior creates processes that are declared with always * or initial items. */ extern PProcess* pform_make_behavior(ivl_process_type_t, Statement*, list*attr); extern void pform_mc_translate_on(bool flag); extern std::vector* pform_make_udp_input_ports(list*); extern void pform_make_events(list*names, const char*file, unsigned lineno); /* * Make real datum objects. */ extern void pform_make_reals(list*names, const char*file, unsigned lineno); /* * The makegate function creates a new gate (which need not have a * name) and connects it to the specified wires. */ extern void pform_makegates(const struct vlltype&loc, PGBuiltin::Type type, struct str_pair_t str, list*delay, svector*gates, list*attr); extern void pform_make_modgates(const struct vlltype&loc, perm_string type, struct parmvalue_t*overrides, svector*gates); /* Make a continuous assignment node, with optional bit- or part- select. */ extern void pform_make_pgassign_list(list*alist, list*del, struct str_pair_t str, const char* fn, unsigned lineno); /* Given a port type and a list of names, make a list of wires that can be used as task port information. */ extern std::vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, ivl_variable_type_t vtype, bool signed_flag, list*range, list*names, bool isint = false); extern std::vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, data_type_t*vtype, list*names); /* * The parser uses this function to convert a unary * increment/decrement expression to the equivalent compressed * assignment statement. */ extern PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc, PExpr*exp); /* * These are functions that the outside-the-parser code uses the do * interesting things to the Verilog. The parse function reads and * parses the source file and places all the modules it finds into the * mod list. The dump function dumps a module to the output stream. */ extern void pform_dump(ostream&out, Module*mod); /* ** pform_discipline.cc * Functions for handling the parse of natures and disciplines. These * functions are in pform_disciplines.cc */ extern void pform_start_nature(const char*name); extern void pform_end_nature(const struct vlltype&loc); extern void pform_nature_access(const struct vlltype&loc, const char*name); extern void pform_start_discipline(const char*name); extern void pform_end_discipline(const struct vlltype&loc); extern void pform_discipline_domain(const struct vlltype&loc, ivl_dis_domain_t use_domain); extern void pform_discipline_potential(const struct vlltype&loc, const char*name); extern void pform_discipline_flow(const struct vlltype&loc, const char*name); extern void pform_attach_discipline(const struct vlltype&loc, ivl_discipline_t discipline, list*names); extern void pform_dump(ostream&out, const ivl_nature_s*); extern void pform_dump(ostream&out, const ivl_discipline_s*); /* ** pform_analog.cc */ extern void pform_make_analog_behavior(const struct vlltype&loc, ivl_process_type_t type, Statement*st); extern AContrib*pform_contribution_statement(const struct vlltype&loc, PExpr*lval, PExpr*rval); extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*n1, char*n2); extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*branch); /* * Parse configuration file with format =, where key * is the hierarchical name of a valid parameter name and value * is the value user wants to assign to. The value should be constant. */ extern void parm_to_defparam_list(const string¶m); /* * Tasks to set the timeunit or timeprecision for SystemVerilog. */ extern bool get_time_unit(const char*cp, int &unit); extern int pform_get_timeunit(); extern void pform_set_timeunit(const char*txt, bool in_module, bool only_check); extern void pform_set_timeprecision(const char*txt, bool in_module, bool only_check); #endif /* IVL_pform_H */ iverilog-10_1/pform_analog.cc000066400000000000000000000042121265551621300163140ustar00rootroot00000000000000/* * Copyright (c) 2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "pform.h" # include "parse_misc.h" # include "AStatement.h" AContrib* pform_contribution_statement(const struct vlltype&loc, PExpr*lval, PExpr*rval) { AContrib*tmp = new AContrib(lval, rval); FILE_NAME(tmp, loc); return tmp; } void pform_make_analog_behavior(const struct vlltype&loc, ivl_process_type_t pt, Statement*statement) { AProcess*proc = new AProcess(pt, statement); FILE_NAME(proc, loc); pform_put_behavior_in_scope(proc); } PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*n1, char*n2) { vector parms (2); parms[0] = new PEIdent(lex_strings.make(n1)); FILE_NAME(parms[0], loc); parms[1] = new PEIdent(lex_strings.make(n2)); FILE_NAME(parms[1], loc); PECallFunction*res = new PECallFunction(lex_strings.make(name), parms); FILE_NAME(res, loc); return res; } PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*branch_name) { vector parms (1); parms[0] = new PEIdent(lex_strings.make(branch_name)); FILE_NAME(parms[0], loc); PECallFunction*res = new PECallFunction(lex_strings.make(name), parms); FILE_NAME(res, loc); return res; } iverilog-10_1/pform_class_type.cc000066400000000000000000000030741265551621300172260ustar00rootroot00000000000000/* * Copyright (c) 2012-2014 Picture Elements, Inc. * Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform.h" # include "parse_misc.h" # include "ivl_assert.h" static void pform_set_class_type(class_type_t*class_type, perm_string name, NetNet::Type net_type, list*attr) { PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_CLASS); assert(net); net->set_data_type(class_type); pform_bind_attributes(net->attributes, attr, true); } void pform_set_class_type(class_type_t*class_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { pform_set_class_type(class_type, *cur, net_type, attr); } } iverilog-10_1/pform_disciplines.cc000066400000000000000000000152271265551621300173710ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "pform.h" # include "parse_misc.h" # include "discipline.h" map natures; map disciplines; map access_function_nature; static perm_string nature_name = perm_string(); static perm_string nature_access = perm_string(); void pform_start_nature(const char*name) { nature_name = lex_strings.make(name); } void pform_nature_access(const struct vlltype&loc, const char*name) { if (nature_access) { cerr << loc.text << ":" << loc.first_line << ": error: " << "Too many access names for nature " << nature_name << "." << endl; error_count += 1; return; } nature_access = lex_strings.make(name); } void pform_end_nature(const struct vlltype&loc) { // The nature access function is required. If it is missing, // then signal an error. For a temporary expedient, we can set // the nature name as the access function, but don't expect it // to work. if (! nature_access) { cerr << loc.text << ":" << loc.first_line << ": error: " << "Missing access name for nature " << nature_name << "." << endl; error_count += 1; nature_access = nature_name; } ivl_nature_s*tmp = new ivl_nature_s(nature_name, nature_access); FILE_NAME(tmp, loc); natures[nature_name] = tmp; // Make sure the access function is not used by multiple // different natures. if (ivl_nature_t dup_access_nat = access_function_nature[nature_access]) { cerr << tmp->get_fileline() << ": error: " << "Access function name " << nature_access << " is already used by nature " << dup_access_nat->name() << " declared at " << dup_access_nat->get_fileline() << "." << endl; error_count += 1; } // Map the access function back to the nature so that // expressions that use the access function can find it. access_function_nature[nature_access] = tmp; nature_name = perm_string(); nature_access = perm_string(); } static perm_string discipline_name; static ivl_dis_domain_t discipline_domain = IVL_DIS_NONE; static ivl_nature_t discipline_potential = 0; static ivl_nature_t discipline_flow = 0; void pform_start_discipline(const char*name) { discipline_name = lex_strings.make(name); discipline_domain = IVL_DIS_NONE; } void pform_discipline_domain(const struct vlltype&loc, ivl_dis_domain_t use_domain) { assert(use_domain != IVL_DIS_NONE); if (discipline_domain != IVL_DIS_NONE) { cerr << loc.text << ":" << loc.first_line << ": error: " << "Too many domain attributes for discipline " << discipline_name << "." << endl; error_count += 1; return; } discipline_domain = use_domain; } void pform_discipline_potential(const struct vlltype&loc, const char*name) { if (discipline_potential) { cerr << loc.text << ":" << loc.first_line << ": error: " << "Too many potential natures for discipline " << discipline_name << "." << endl; error_count += 1; return; } perm_string key = lex_strings.make(name); discipline_potential = natures[key]; if (discipline_potential == 0) { cerr << loc.text << ":" << loc.first_line << ": error: " << "nature " << key << " is not declared." << endl; error_count += 1; return; } } void pform_discipline_flow(const struct vlltype&loc, const char*name) { if (discipline_flow) { cerr << loc.text << ":" << loc.first_line << ": error: " << "Too many flow natures for discipline " << discipline_name << "." << endl; error_count += 1; return; } perm_string key = lex_strings.make(name); discipline_flow = natures[key]; if (discipline_flow == 0) { cerr << loc.text << ":" << loc.first_line << ": error: " << "nature " << key << " is not declared." << endl; error_count += 1; return; } } void pform_end_discipline(const struct vlltype&loc) { // If the domain is not otherwise specified, then take it to // be continuous if potential or flow natures are given. if (discipline_domain == IVL_DIS_NONE && (discipline_potential||discipline_flow)) discipline_domain = IVL_DIS_CONTINUOUS; ivl_discipline_t tmp = new ivl_discipline_s(discipline_name, discipline_domain, discipline_potential, discipline_flow); disciplines[discipline_name] = tmp; FILE_NAME(tmp, loc); /* Clear the static variables for the next item. */ discipline_name = perm_string(); discipline_domain = IVL_DIS_NONE; discipline_potential = 0; discipline_flow = 0; } /* * The parser uses this function to attach a discipline to a wire. The * wire may be declared by now, or will be declared further later. If * it is already declared, we just attach the discipline. If it is not * declared yet, then this is the declaration and we create the signal * in the current lexical scope. */ void pform_attach_discipline(const struct vlltype&loc, ivl_discipline_t discipline, list*names) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { PWire* cur_net = pform_get_wire_in_scope(*cur); if (cur_net == 0) { /* Not declared yet, declare it now. */ pform_makewire(loc, *cur, NetNet::WIRE, NetNet::NOT_A_PORT, IVL_VT_REAL, 0); cur_net = pform_get_wire_in_scope(*cur); assert(cur_net); } if (ivl_discipline_t tmp = cur_net->get_discipline()) { cerr << loc.text << ":" << loc.first_line << ": error: " << "discipline " << discipline->name() << " cannot override existing discipline " << tmp->name() << " on net " << cur_net->basename() << endl; error_count += 1; } else { cur_net->set_data_type(IVL_VT_REAL); cur_net->set_discipline(discipline); } } } iverilog-10_1/pform_dump.cc000066400000000000000000001205021265551621300160210ustar00rootroot00000000000000/* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * This file provides the pform_dump function, that dumps the module * passed as a parameter. The dump is as much as possible in Verilog * syntax, so that a human can tell that it really does describe the * module in question. */ # include "pform.h" # include "PClass.h" # include "PEvent.h" # include "PGenerate.h" # include "PPackage.h" # include "PSpec.h" # include "PTask.h" # include "discipline.h" # include "ivl_target_priv.h" # include # include # include ostream& operator << (ostream&out, const PExpr&obj) { obj.dump(out); return out; } ostream& operator << (ostream&out, const PEventStatement&obj) { obj.dump_inline(out); return out; } ostream& operator << (ostream&o, const PDelays&d) { d.dump_delays(o); return o; } ostream& operator<< (ostream&out, const index_component_t&that) { out << "["; switch (that.sel) { case index_component_t::SEL_BIT: out << *that.msb; break; case index_component_t::SEL_BIT_LAST: out << "$"; break; case index_component_t::SEL_PART: out << *that.msb << ":" << *that.lsb; break; case index_component_t::SEL_IDX_UP: out << *that.msb << "+:" << *that.lsb; break; case index_component_t::SEL_IDX_DO: out << *that.msb << "-:" << *that.lsb; break; default: out << "???"; break; } out << "]"; return out; } ostream& operator<< (ostream&out, const name_component_t&that) { out << that.name.str(); typedef std::list::const_iterator index_it_t; for (index_it_t idx = that.index.begin() ; idx != that.index.end() ; ++ idx ) { out << *idx; } return out; } ostream& operator<< (ostream&o, const pform_name_t&that) { pform_name_t::const_iterator cur; cur = that.begin(); o << *cur; ++ cur; while (cur != that.end()) { o << "." << *cur; ++ cur; } return o; } std::ostream& operator << (std::ostream&out, ivl_process_type_t pt) { switch (pt) { case IVL_PR_INITIAL: out << "initial"; break; case IVL_PR_ALWAYS: out << "always"; break; case IVL_PR_FINAL: out << "final"; break; } return out; } std::ostream& operator << (std::ostream&out, ivl_dis_domain_t dom) { switch (dom) { case IVL_DIS_NONE: out << "no-domain"; break; case IVL_DIS_DISCRETE: out << "discrete"; break; case IVL_DIS_CONTINUOUS: out << "continuous"; break; default: assert(0); break; } return out; } void data_type_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << typeid(*this).name() << endl; } void void_type_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "void" << endl; } void parray_type_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "Packed array " << "[...]" << " of:" << endl; base_type->pform_dump(out, indent+4); } void struct_type_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "Struct " << (packed_flag?"packed":"unpacked") << " with " << (members.get()==0? 0 : members->size()) << " members" << endl; if (members.get()==0) return; for (list::iterator cur = members->begin() ; cur != members->end() ; ++ cur) { struct_member_t*curp = *cur; curp->pform_dump(out, indent+4); } } void uarray_type_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "Unpacked array " << "[...]" << " of:" << endl; base_type->pform_dump(out, indent+4); } void vector_type_t::pform_dump(ostream&fd, unsigned indent) const { fd << setw(indent) << "" << "vector of " << base_type; if (pdims.get()) { for (list::iterator cur = pdims->begin() ; cur != pdims->end() ; ++cur) { fd << "[" << *(cur->first) << ":" << *(cur->second) << "]"; } } fd << endl; } void class_type_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "class " << name; if (base_type) out << " extends "; if (! base_args.empty()) { out << " ("; for (list::const_iterator cur = base_args.begin() ; cur != base_args.end() ; ++cur) { const PExpr*curp = *cur; if (cur != base_args.begin()) out << ", "; curp->dump(out); } out << ")"; } out << " {"; for (map::const_iterator cur = properties.begin() ; cur != properties.end() ; ++cur) { out << " " << cur->first; } out << " }" << endl; if (base_type) base_type->pform_dump(out, indent+4); } void class_type_t::pform_dump_init(ostream&out, unsigned indent) const { for (vector::const_iterator cur = initialize.begin() ; cur != initialize.end() ; ++cur) { Statement*curp = *cur; curp->dump(out,indent+4); } } void struct_member_t::pform_dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << (type.get()? typeid(*type).name() : ""); for (list::iterator cur = names->begin() ; cur != names->end() ; ++cur) { decl_assignment_t*curp = *cur; out << " " << curp->name; } out << ";" << endl; } static void dump_attributes_map(ostream&out, const map&attributes, int ind) { for (map::const_iterator idx = attributes.begin() ; idx != attributes.end() ; ++ idx ) { out << setw(ind) << "" << "(* " << (*idx).first; if ((*idx).second) { out << " = " << *(*idx).second; } out << " *)" << endl; } } void PExpr::dump(ostream&out) const { out << typeid(*this).name(); } void PEAssignPattern::dump(ostream&out) const { out << "'{"; if (parms_.size() > 0) { parms_[0]->dump(out); for (size_t idx = 1 ; idx < parms_.size() ; idx += 1) { out << ", "; parms_[idx]->dump(out); } } out << "}"; } void PEConcat::dump(ostream&out) const { if (repeat_) out << "{" << *repeat_; if (parms_.empty()) { out << "{}"; return; } out << "{"; if (parms_[0]) out << *parms_[0]; for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { out << ", "; if (parms_[idx]) out << *parms_[idx]; } out << "}"; if (repeat_) out << "}"; } void PECallFunction::dump(ostream &out) const { if (package_) out << package_->pscope_name() << "::"; out << path_ << "("; if (! parms_.empty()) { if (parms_[0]) parms_[0]->dump(out); for (unsigned idx = 1; idx < parms_.size(); ++idx) { out << ", "; if (parms_[idx]) parms_[idx]->dump(out); } } out << ")"; } void PECastSize::dump(ostream &out) const { out << size_ << "'("; base_->dump(out); out << ")"; } void PECastType::dump(ostream &out) const { target_->pform_dump(out, 0); out << "'("; base_->dump(out); out << ")"; } void PEEvent::dump(ostream&out) const { switch (type_) { case PEEvent::ANYEDGE: break; case PEEvent::POSEDGE: out << "posedge "; break; case PEEvent::NEGEDGE: out << "negedge "; break; case PEEvent::POSITIVE: out << "positive "; break; } out << *expr_; } void PEFNumber::dump(ostream &out) const { out << value(); } void PENewArray::dump(ostream&out) const { out << "new [" << *size_ << "]"; if (init_) out << "(" << *init_ << ")"; } void PENewClass::dump(ostream&out) const { out << "class_new("; if (parms_.size() > 0) { parms_[0]->dump(out); for (size_t idx = 1 ; idx < parms_.size() ; idx += 1) { out << ", "; if (parms_[idx]) parms_[idx]->dump(out); } } out << ")"; } void PENewCopy::dump(ostream&out) const { out << "copy_new("; src_->dump(out); out << ")"; } void PENull::dump(ostream&out) const { out << "null"; } void PENumber::dump(ostream&out) const { out << value(); } void PEIdent::dump(ostream&out) const { if (package_) out << package_->pscope_name() << "::"; out << path_; } void PEString::dump(ostream&out) const { out << "\"" << text_ << "\""; } void PETernary::dump(ostream&out) const { out << "(" << *expr_ << ")?(" << *tru_ << "):(" << *fal_ << ")"; } void PETypename::dump(ostream&fd) const { fd << ""; } void PEUnary::dump(ostream&out) const { switch (op_) { case 'm': out << "abs"; break; default: out << op_; break; } out << "(" << *expr_ << ")"; } void PEBinary::dump(ostream&out) const { /* Handle some special cases, where the operators are written in function notation. */ if (op_ == 'm') { out << "min(" << *left_ << "," << *right_ << ")"; return; } if (op_ == 'M') { out << "min(" << *left_ << "," << *right_ << ")"; return; } if (left_) out << "(" << *left_ << ")"; else out << "()"; switch (op_) { case 'a': out << "&&"; break; case 'e': out << "=="; break; case 'E': out << "==="; break; case 'l': out << "<<"; break; case 'L': out << "<="; break; case 'n': out << "!="; break; case 'N': out << "!=="; break; case 'p': out << "**"; break; case 'R': out << ">>>"; break; case 'r': out << ">>"; break; default: out << op_; break; } if (right_) out << "(" << *right_ << ")"; else out << "()"; } void PWire::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << type_; switch (port_type_) { case NetNet::PIMPLICIT: out << " implicit input"; break; case NetNet::PINPUT: out << " input"; break; case NetNet::POUTPUT: out << " output"; break; case NetNet::PINOUT: out << " inout"; break; case NetNet::PREF: out << " ref"; break; case NetNet::NOT_A_PORT: break; } out << " " << data_type_; if (signed_) { out << " signed"; } if (get_isint()) { out << " integer"; } if (set_data_type_) { out << " set_data_type_=" << typeid(*set_data_type_).name(); } if (discipline_) { out << " discipline<" << discipline_->name() << ">"; } if (port_set_) { if (port_.empty()) { out << " port"; } else { out << " port"; for (list::const_iterator cur = port_.begin() ; cur != port_.end() ; ++cur) out << "[" << *cur->first << ":" << *cur->second << "]"; } } if (net_set_) { if (net_.empty()) { out << " net"; } else { out << " net"; for (list::const_iterator cur = net_.begin() ; cur != net_.end() ; ++cur) out << "[" << *cur->first << ":" << *cur->second << "]"; } } out << " " << name_; // If the wire has unpacked indices, dump them. for (list::const_iterator cur = unpacked_.begin() ; cur != unpacked_.end() ; ++cur) { out << "["; if (cur->first) out << *cur->first; if (cur->second) out << ":" << *cur->second; out << "]"; } out << ";" << endl; if (set_data_type_) { set_data_type_->pform_dump(out, 8); } dump_attributes_map(out, attributes, 8); } void PGate::dump_pins(ostream&out) const { if (pin_count()) { if (pin(0)) out << *pin(0); for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) { out << ", "; if (pin(idx)) out << *pin(idx); } } } void PDelays::dump_delays(ostream&out) const { if (delay_[0] && delay_[1] && delay_[2]) out << "#(" << *delay_[0] << "," << *delay_[1] << "," << *delay_[2] << ")"; else if (delay_[0] && delay_[1]) out << "#(" << *delay_[0] << "," << *delay_[1] << ")"; else if (delay_[0]) out << "#" << *delay_[0]; else out << "#0"; } void PGate::dump_delays(ostream&out) const { delay_.dump_delays(out); } void PGate::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << typeid(*this).name() << " "; delay_.dump_delays(out); out << " " << get_name() << "("; dump_pins(out); out << ");" << endl; } void PGAssign::dump(ostream&out, unsigned ind) const { out << setw(ind) << ""; out << "assign (" << strength0() << "0 " << strength1() << "1) "; dump_delays(out); out << " " << *pin(0) << " = " << *pin(1) << ";" << endl; } void PGBuiltin::dump(ostream&out, unsigned ind) const { out << setw(ind) << ""; switch (type()) { case PGBuiltin::BUFIF0: out << "bufif0 "; break; case PGBuiltin::BUFIF1: out << "bufif1 "; break; case PGBuiltin::NOTIF0: out << "bufif0 "; break; case PGBuiltin::NOTIF1: out << "bufif1 "; break; case PGBuiltin::NAND: out << "nand "; break; case PGBuiltin::NMOS: out << "nmos "; break; case PGBuiltin::RNMOS: out << "rnmos "; break; case PGBuiltin::RPMOS: out << "rpmos "; break; case PGBuiltin::PMOS: out << "pmos "; break; case PGBuiltin::RCMOS: out << "rcmos "; break; case PGBuiltin::CMOS: out << "cmos "; break; default: out << "builtin gate "; } out << "(" << strength0() << "0 " << strength1() << "1) "; dump_delays(out); out << " " << get_name(); if (msb_) { out << " [" << *msb_ << ":" << *lsb_ << "]"; } out << "("; dump_pins(out); out << ");" << endl; } void PGModule::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << type_ << " "; // If parameters are overridden by order, dump them. if (overrides_ && (! overrides_->empty())) { assert(parms_ == 0); out << "#("; list::const_iterator idx = overrides_->begin(); if (*idx == 0) out << ""; else out << *idx; for ( ; idx != overrides_->end() ; ++ idx) { out << "," << *idx; } out << ") "; } // If parameters are overridden by name, dump them. if (parms_) { assert(overrides_ == 0); out << "#("; out << "." << parms_[0].name << "(" << *parms_[0].parm << ")"; for (unsigned idx = 1 ; idx < nparms_ ; idx += 1) { out << ", ." << parms_[idx].name << "(" << *parms_[idx].parm << ")"; } out << ") "; } out << get_name(); // If the module is arrayed, print the index expressions. if (msb_ || lsb_) { out << "["; if (msb_) out << *msb_; out << ":"; if (lsb_) out << *lsb_; out << "]"; } out << "("; if (pins_) { out << "." << pins_[0].name << "("; if (pins_[0].parm) out << *pins_[0].parm; out << ")"; for (unsigned idx = 1 ; idx < npins_ ; idx += 1) { out << ", ." << pins_[idx].name << "("; if (pins_[idx].parm) out << *pins_[idx].parm; out << ")"; } } else { dump_pins(out); } out << ");" << endl; } void Statement::dump(ostream&out, unsigned ind) const { /* I give up. I don't know what type this statement is, so just print the C++ typeid and let the user figure it out. */ out << setw(ind) << ""; out << "/* " << get_fileline() << ": " << typeid(*this).name() << " */ ;" << endl; dump_attributes_map(out, attributes, ind+2); } void AContrib::dump(ostream&out, unsigned ind) const { out << setw(ind) << ""; out << *lval_ << " <+ " << *rval_ << "; /* " << get_fileline() << " */" << endl; } void PAssign::dump(ostream&out, unsigned ind) const { out << setw(ind) << ""; if (lval()) out << *lval(); else out << ""; out << " = "; if (delay_) out << "#" << *delay_ << " "; if (count_) out << "repeat(" << *count_ << ") "; if (event_) out << *event_ << " "; PExpr*rexpr = rval(); if (rexpr) out << *rval() << ";"; else out << ";"; out << " /* " << get_fileline() << " */" << endl; } void PAssignNB::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << *lval() << " <= "; if (delay_) out << "#" << *delay_ << " "; if (count_) out << "repeat(" << *count_ << ") "; if (event_) out << *event_ << " "; out << *rval() << ";" << " /* " << get_fileline() << " */" << endl; } void PBlock::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "begin"; if (pscope_name() != 0) out << " : " << pscope_name(); out << endl; if (pscope_name() != 0) { dump_parameters_(out, ind+2); dump_localparams_(out, ind+2); dump_events_(out, ind+2); dump_wires_(out, ind+2); } for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) { if (list_[idx]) list_[idx]->dump(out, ind+2); else out << setw(ind+2) << "" << "/* NOOP */ ;" << endl; } out << setw(ind) << "" << "end" << endl; } void PCallTask::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << path_; if (! parms_.empty()) { out << "("; if (parms_[0]) out << *parms_[0]; for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { out << ", "; if (parms_[idx]) out << *parms_[idx]; } out << ")"; } out << "; /* " << get_fileline() << " */" << endl; } void PCase::dump(ostream&out, unsigned ind) const { out << setw(ind) << ""; switch (type_) { case NetCase::EQ: out << "case"; break; case NetCase::EQX: out << "casex"; break; case NetCase::EQZ: out << "casez"; break; } out << " (" << *expr_ << ") /* " << get_fileline() << " */" << endl; dump_attributes_map(out, attributes, ind+2); for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { PCase::Item*cur = (*items_)[idx]; if (cur->expr.size()) { out << setw(ind+2) << "" << "default:"; } else { list::iterator idx_exp = cur->expr.begin(); out << setw(ind+2) << "" << *idx_exp; for( ; idx_exp != cur->expr.end() ; ++idx_exp) out << ", " << *idx_exp; out << ":"; } if (cur->stat) { out << endl; cur->stat->dump(out, ind+6); } else { out << " ;" << endl; } } out << setw(ind) << "" << "endcase" << endl; } void PChainConstructor::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "super.new("; if (parms_.size() > 0) { if (parms_[0]) out << *parms_[0]; } for (size_t idx = 1 ; idx < parms_.size() ; idx += 1) { out << ", "; if (parms_[idx]) out << *parms_[idx]; } out << ");" << endl; } void PCondit::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "if (" << *expr_ << ")" << endl; if (if_) if_->dump(out, ind+3); else out << setw(ind) << ";" << endl; if (else_) { out << setw(ind) << "" << "else" << endl; else_->dump(out, ind+3); } } void PCAssign::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "assign " << *lval_ << " = " << *expr_ << "; /* " << get_fileline() << " */" << endl; } void PDeassign::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "deassign " << *lval_ << "; /* " << get_fileline() << " */" << endl; } void PDelayStatement::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "#" << *delay_ << " /* " << get_fileline() << " */"; if (statement_) { out << endl; statement_->dump(out, ind+2); } else { out << " /* noop */;" << endl; } } void PDisable::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "disable "; if (scope_.empty()) out << scope_; else out << "fork"; out << "; /* " << get_fileline() << " */" << endl; } void PDoWhile::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "do" << endl; statement_->dump(out, ind+3); out << setw(ind) << "" << "while (" << *cond_ << ");" << endl; } void PEventStatement::dump(ostream&out, unsigned ind) const { if (expr_.count() == 0) { out << setw(ind) << "" << "@* "; } else if ((expr_.count() == 1) && (expr_[0] == 0)) { out << setw(ind) << "" << "wait fork "; } else { out << setw(ind) << "" << "@(" << *(expr_[0]); if (expr_.count() > 1) for (unsigned idx = 1 ; idx < expr_.count() ; idx += 1) out << " or " << *(expr_[idx]); out << ")"; } if (statement_) { out << endl; statement_->dump(out, ind+2); } else { out << " ;" << endl; } } void PEventStatement::dump_inline(ostream&out) const { assert(statement_ == 0); if (expr_.count() == 0) { out << "@* "; } else { out << "@(" << *(expr_[0]); if (expr_.count() > 1) for (unsigned idx = 1 ; idx < expr_.count() ; idx += 1) out << " or " << *(expr_[idx]); out << ")"; } } void PForce::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "force " << *lval_ << " = " << *expr_ << "; /* " << get_fileline() << " */" << endl; } void PForeach::dump(ostream&fd, unsigned ind) const { fd << setw(ind) << "" << "foreach " << "variable=" << array_var_ << ", indices=["; for (size_t idx = 0 ; idx < index_vars_.size() ; idx += 1) { if (idx > 0) fd << ","; fd << index_vars_[idx]; } fd << "] /* " << get_fileline() << " */" << endl; statement_->dump(fd, ind+3); } void PForever::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "forever /* " << get_fileline() << " */" << endl; statement_->dump(out, ind+3); } void PForStatement::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "for (" << *name1_ << " = " << *expr1_ << "; " << *cond_ << "; )" << endl; step_->dump(out, ind+6); statement_->dump(out, ind+3); } void PFunction::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "function "; if (is_auto_) out << "automatic "; out << pscope_name() << ";" << endl; if (method_of()) out << setw(ind) << "" << "method of " << method_of()->name << ";" << endl; if (return_type_) return_type_->pform_dump(out, ind+8); else out << setw(ind+8) << "" << "" << endl; dump_ports_(out, ind+2); dump_parameters_(out, ind+2); dump_localparams_(out, ind+2); dump_events_(out, ind+2); dump_wires_(out, ind+2); if (statement_) statement_->dump(out, ind+2); else out << setw(ind+2) << "" << "/* NOOP */" << endl; } void PRelease::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "release " << *lval_ << "; /* " << get_fileline() << " */" << endl; } void PRepeat::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "repeat (" << *expr_ << ")" << endl; statement_->dump(out, ind+3); } void PReturn::dump(ostream&fd, unsigned ind) const { fd << setw(ind) << "" << "return ("; if (expr_) fd << *expr_; fd << ")" << endl; } void PTask::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "task "; if (is_auto_) out << "automatic "; out << pscope_name() << ";" << endl; if (method_of()) out << setw(ind) << "" << "method of " << method_of()->name << ";" << endl; dump_ports_(out, ind+2); dump_parameters_(out, ind+2); dump_localparams_(out, ind+2); dump_events_(out, ind+2); dump_wires_(out, ind+2); if (statement_) statement_->dump(out, ind+2); else out << setw(ind+2) << "" << "/* NOOP */" << endl; } void PTaskFunc::dump_ports_(std::ostream&out, unsigned ind) const { if (ports_ == 0) return; for (unsigned idx = 0 ; idx < ports_->size() ; idx += 1) { if (ports_->at(idx).port == 0) { out << setw(ind) << "" << "ERROR PORT" << endl; continue; } out << setw(ind) << ""; switch (ports_->at(idx).port->get_port_type()) { case NetNet::PINPUT: out << "input "; break; case NetNet::POUTPUT: out << "output "; break; case NetNet::PINOUT: out << "inout "; break; case NetNet::PIMPLICIT: out << "PIMPLICIT"; break; case NetNet::NOT_A_PORT: out << "NOT_A_PORT"; break; default: assert(0); break; } out << ports_->at(idx).port->basename(); if (ports_->at(idx).defe) { out << " = " << *ports_->at(idx).defe; } out << ";" << endl; } } void PTrigger::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "-> " << event_ << ";" << endl; } void PWhile::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "while (" << *cond_ << ")" << endl; statement_->dump(out, ind+3); } void PProcess::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << type_ << " /* " << get_fileline() << " */" << endl; dump_attributes_map(out, attributes, ind+2); statement_->dump(out, ind+2); } void AProcess::dump(ostream&out, unsigned ind) const { switch (type_) { case IVL_PR_INITIAL: out << setw(ind) << "" << "analog initial"; break; case IVL_PR_ALWAYS: out << setw(ind) << "" << "analog"; break; case IVL_PR_FINAL: out << setw(ind) << "" << "analog final"; break; } out << " /* " << get_fileline() << " */" << endl; dump_attributes_map(out, attributes, ind+2); statement_->dump(out, ind+2); } void PSpecPath::dump(std::ostream&out, unsigned ind) const { out << setw(ind) << "" << "specify path "; if (condition) out << "if (" << *condition << ") "; out << "("; if (edge) { if (edge > 0) out << "posedge "; else out << "negedge "; } for (unsigned idx = 0 ; idx < src.size() ; idx += 1) { if (idx > 0) out << ", "; assert(src[idx]); out << src[idx]; } out << " "; if (polarity_) out << polarity_; if (full_flag_) out << "*> "; else out << "=> "; if (data_source_expression) out << "("; for (unsigned idx = 0 ; idx < dst.size() ; idx += 1) { if (idx > 0) out << ", "; assert(dst[idx]); out << dst[idx]; } if (data_source_expression) out << " : " << *data_source_expression << ")"; out << ") = ("; for (unsigned idx = 0 ; idx < delays.size() ; idx += 1) { if (idx > 0) out << ", "; assert(delays[idx]); out << *delays[idx]; } out << ");" << endl; } void PGenerate::dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "generate(" << id_number << ")"; PGenerate*parent = dynamic_cast(parent_scope()); switch (scheme_type) { case GS_NONE: break; case GS_LOOP: out << " for (" << loop_index << "=" << *loop_init << "; " << *loop_test << "; " << loop_index << "=" << *loop_step << ")"; break; case GS_CONDIT: out << " if (" << *loop_test << ")"; break; case GS_ELSE: out << " else !(" << *loop_test << ")"; break; case GS_CASE: out << " case (" << *loop_test << ")"; break; case GS_CASE_ITEM: assert(parent); if (loop_test) out << " (" << *loop_test << ") == (" << *parent->loop_test << ")"; else out << " default:"; break; case GS_NBLOCK: out << " begin"; } if (scope_name) out << " : " << scope_name; out << endl; dump_localparams_(out, indent+2); typedef list::const_iterator parm_hiter_t; for (parm_hiter_t cur = defparms.begin() ; cur != defparms.end() ; ++ cur ) { out << setw(indent+2) << "" << "defparam " << (*cur).first << " = "; if ((*cur).second) out << *(*cur).second << ";" << endl; else out << "/* ERROR */;" << endl; } dump_events_(out, indent+2); dump_wires_(out, indent+2); for (list::const_iterator idx = gates.begin() ; idx != gates.end() ; ++ idx ) { (*idx)->dump(out, indent+2); } for (list::const_iterator idx = behaviors.begin() ; idx != behaviors.end() ; ++ idx ) { (*idx)->dump(out, indent+2); } for (list::const_iterator idx = analog_behaviors.begin() ; idx != analog_behaviors.end() ; ++ idx ) { (*idx)->dump(out, indent+2); } typedef map::const_iterator genvar_iter_t; for (genvar_iter_t cur = genvars.begin() ; cur != genvars.end() ; ++ cur ) { out << setw(indent+2) << "" << "genvar " << ((*cur).first) << ";" << endl; } for (list::const_iterator idx = generate_schemes.begin() ; idx != generate_schemes.end() ; ++ idx ) { (*idx)->dump(out, indent+2); } if (scheme_type == GS_NBLOCK) { out << setw(indent) << "" << "end endgenerate" << endl; } else { out << setw(indent) << "" << "endgenerate" << endl; } } void LexicalScope::dump_typedefs_(ostream&out, unsigned indent) const { typedef map::const_iterator iter_t; for (iter_t cur = typedefs.begin() ; cur != typedefs.end() ; ++ cur) { out << setw(indent) << "" << "typedef of " << cur->first << ":" << endl; cur->second->pform_dump(out, indent+4); } } void LexicalScope::dump_parameters_(ostream&out, unsigned indent) const { typedef map::const_iterator parm_iter_t; for (parm_iter_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur ) { out << setw(indent) << "" << "parameter " << (*cur).second.type << " "; if ((*cur).second.signed_flag) out << "signed "; if ((*cur).second.msb) out << "[" << *(*cur).second.msb << ":" << *(*cur).second.lsb << "] "; out << (*cur).first << " = "; if ((*cur).second.expr) out << *(*cur).second.expr; else out << "/* ERROR */"; for (LexicalScope::range_t*tmp = (*cur).second.range ; tmp ; tmp = tmp->next) { if (tmp->exclude_flag) out << " exclude "; else out << " from "; if (tmp->low_open_flag) out << "("; else out << "["; if (tmp->low_expr) out << *(tmp->low_expr); else if (tmp->low_open_flag==false) out << "-inf"; else out << ""; out << ":"; if (tmp->high_expr) out << *(tmp->high_expr); else if (tmp->high_open_flag==false) out << "inf"; else out << ""; if (tmp->high_open_flag) out << ")"; else out << "]"; } out << ";" << endl; } } void LexicalScope::dump_localparams_(ostream&out, unsigned indent) const { typedef map::const_iterator parm_iter_t; for (parm_iter_t cur = localparams.begin() ; cur != localparams.end() ; ++ cur ) { out << setw(indent) << "" << "localparam "; if ((*cur).second.msb) out << "[" << *(*cur).second.msb << ":" << *(*cur).second.lsb << "] "; out << (*cur).first << " = "; if ((*cur).second.expr) out << *(*cur).second.expr << ";" << endl; else out << "/* ERROR */;" << endl; } } void LexicalScope::dump_enumerations_(ostream&out, unsigned indent) const { for (set::const_iterator cur = enum_sets.begin() ; cur != enum_sets.end() ; ++ cur) { out << setw(indent) << "" << "enum {" << endl; for (list::const_iterator idx = (*cur)->names->begin() ; idx != (*cur)->names->end() ; ++ idx) { out << setw(indent+4) << "" << idx->name << " = " << idx->parm << endl; } out << setw(indent) << "" << "}" << endl; } } void LexicalScope::dump_events_(ostream&out, unsigned indent) const { for (map::const_iterator cur = events.begin() ; cur != events.end() ; ++ cur ) { PEvent*ev = (*cur).second; out << setw(indent) << "" << "event " << ev->name() << "; // " << ev->get_fileline() << endl; } } void LexicalScope::dump_wires_(ostream&out, unsigned indent) const { // Iterate through and display all the wires. for (map::const_iterator wire = wires.begin() ; wire != wires.end() ; ++ wire ) { (*wire).second->dump(out, indent); } } void PScopeExtra::dump_classes_(ostream&out, unsigned indent) const { // Dump the task definitions. typedef map::const_iterator class_iter_t; for (class_iter_t cur = classes.begin() ; cur != classes.end() ; ++ cur ) { cur->second->dump(out, indent); } } void PScopeExtra::dump_tasks_(ostream&out, unsigned indent) const { // Dump the task definitions. typedef map::const_iterator task_iter_t; for (task_iter_t cur = tasks.begin() ; cur != tasks.end() ; ++ cur ) { out << setw(indent) << "" << "task " << (*cur).first << ";" << endl; (*cur).second->dump(out, indent+2); out << setw(indent) << "" << "endtask;" << endl; } } void PScopeExtra::dump_funcs_(ostream&out, unsigned indent) const { // Dump the task definitions. typedef map::const_iterator task_iter_t; for (task_iter_t cur = funcs.begin() ; cur != funcs.end() ; ++ cur ) { out << setw(indent) << "" << "function " << (*cur).first << ";" << endl; (*cur).second->dump(out, indent+2); out << setw(indent) << "" << "endfunction;" << endl; } } void PClass::dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "class " << type->name << ";" << endl; type->pform_dump(out, indent+2); type->pform_dump_init(out, indent+2); dump_tasks_(out, indent+2); dump_funcs_(out, indent+2); out << setw(indent) << "" << "endclass" << endl; } void Module::dump_specparams_(ostream&out, unsigned indent) const { typedef map::const_iterator parm_iter_t; for (parm_iter_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { out << setw(indent) << "" << "specparam "; if ((*cur).second.msb) out << "[" << *(*cur).second.msb << ":" << *(*cur).second.lsb << "] "; out << (*cur).first << " = "; if ((*cur).second.expr) out << *(*cur).second.expr << ";" << endl; else out << "/* ERROR */;" << endl; } } void Module::dump(ostream&out) const { if (attributes.begin() != attributes.end()) { out << "(* "; for (map::const_iterator idx = attributes.begin() ; idx != attributes.end() ; ++ idx ) { if (idx != attributes.begin()) { out << " , "; } out << (*idx).first; if ((*idx).second) { out << " = " << *(*idx).second; } } out << " *) "; } out << "module " << mod_name() << ";"; if (is_cell) out << " // Is in `celldefine."; out << endl; for (unsigned idx = 0 ; idx < ports.size() ; idx += 1) { port_t*cur = ports[idx]; if (cur == 0) { out << " unconnected" << endl; continue; } out << " ." << cur->name << "(" << *cur->expr[0]; for (unsigned wdx = 1 ; wdx < cur->expr.size() ; wdx += 1) { out << ", " << *cur->expr[wdx]; } out << ")" << endl; } for (map::const_iterator cur = nested_modules.begin() ; cur != nested_modules.end() ; ++cur) { out << setw(4) << "" << "Nested module " << cur->first << ";" << endl; } dump_typedefs_(out, 4); dump_parameters_(out, 4); dump_localparams_(out, 4); dump_specparams_(out, 4); dump_enumerations_(out, 4); dump_classes_(out, 4); typedef map::const_iterator genvar_iter_t; for (genvar_iter_t cur = genvars.begin() ; cur != genvars.end() ; ++ cur ) { out << " genvar " << ((*cur).first) << ";" << endl; } typedef list::const_iterator genscheme_iter_t; for (genscheme_iter_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur)->dump(out, 4); } typedef list::const_iterator parm_hiter_t; for (parm_hiter_t cur = defparms.begin() ; cur != defparms.end() ; ++ cur ) { out << " defparam " << (*cur).first << " = "; if ((*cur).second) out << *(*cur).second << ";" << endl; else out << "/* ERROR */;" << endl; } dump_events_(out, 4); // Iterate through and display all the wires. dump_wires_(out, 4); // Dump the task definitions. dump_tasks_(out, 4); // Dump the function definitions. dump_funcs_(out, 4); // Iterate through and display all the gates for (list::const_iterator gate = gates_.begin() ; gate != gates_.end() ; ++ gate ) { (*gate)->dump(out); } for (list::const_iterator behav = behaviors.begin() ; behav != behaviors.end() ; ++ behav ) { (*behav)->dump(out, 4); } for (list::const_iterator idx = analog_behaviors.begin() ; idx != analog_behaviors.end() ; ++ idx) { (*idx)->dump(out, 4); } for (list::const_iterator spec = specify_paths.begin() ; spec != specify_paths.end() ; ++ spec ) { (*spec)->dump(out, 4); } out << "endmodule" << endl; } void pform_dump(ostream&out, Module*mod) { mod->dump(out); } void PUdp::dump(ostream&out) const { out << "primitive " << name_ << "(" << ports[0]; for (unsigned idx = 1 ; idx < ports.count() ; idx += 1) out << ", " << ports[idx]; out << ");" << endl; if (sequential) out << " reg " << ports[0] << ";" << endl; out << " table" << endl; for (unsigned idx = 0 ; idx < tinput.count() ; idx += 1) { out << " "; for (unsigned chr = 0 ; chr < tinput[idx].length() ; chr += 1) out << " " << tinput[idx][chr]; if (sequential) out << " : " << tcurrent[idx]; out << " : " << toutput[idx] << " ;" << endl; } out << " endtable" << endl; if (sequential) out << " initial " << ports[0] << " = 1'b" << initial << ";" << endl; // Dump the attributes for the primitive as attribute // statements. for (map::const_iterator idx = attributes.begin() ; idx != attributes.end() ; ++ idx ) { out << " attribute " << (*idx).first; if ((*idx).second) out << " = " << *(*idx).second; out << endl; } out << "endprimitive" << endl; } void pform_dump(std::ostream&out, const ivl_nature_s*nat) { out << "nature " << nat->name() << endl; out << " access " << nat->access() << ";" << endl; out << "endnature" << endl; } void pform_dump(std::ostream&out, const ivl_discipline_s*dis) { out << "discipline " << dis->name() << endl; out << " domain " << dis->domain() << ";" << endl; if (const ivl_nature_s*tmp = dis->potential()) out << " potential " << tmp->name() << ";" << endl; if (const ivl_nature_s*tmp = dis->flow()) out << " flow " << tmp->name() << ";" << endl; out << "enddiscipline" << endl; } void pform_dump(std::ostream&fd, const PClass*cl) { cl->dump(fd, 0); } void pform_dump(std::ostream&out, const PPackage*pac) { pac->pform_dump(out); } void PPackage::pform_dump(std::ostream&out) const { out << "package " << pscope_name() << endl; dump_localparams_(out, 4); dump_parameters_(out, 4); dump_enumerations_(out, 4); dump_tasks_(out, 4); dump_funcs_(out, 4); out << "endpackage" << endl; } void pform_dump(std::ostream&fd, const PTaskFunc*obj) { obj->dump(fd, 0); } iverilog-10_1/pform_package.cc000066400000000000000000000132471265551621300164560ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform.h" # include "PPackage.h" # include "parse_misc.h" # include "parse_api.h" # include # include # include "ivl_assert.h" using namespace std; /* * This is a map of packages that have been defined. */ map pform_packages; static PPackage*pform_cur_package = 0; void pform_start_package_declaration(const struct vlltype&loc, const char*name) { ivl_assert(loc, pform_cur_package == 0); perm_string use_name = lex_strings.make(name); PPackage*pkg_scope = pform_push_package_scope(loc, use_name); FILE_NAME(pkg_scope, loc); pform_cur_package = pkg_scope; } void pform_end_package_declaration(const struct vlltype&loc) { ivl_assert(loc, pform_cur_package); perm_string use_name = pform_cur_package->pscope_name(); map::const_iterator test = pform_packages.find(use_name); if (test != pform_packages.end()) { ostringstream msg; msg << "Package " << use_name << " was already declared here: " << test->second->get_fileline() << ends; VLerror(msg.str().c_str()); } else { pform_packages[use_name] = pform_cur_package; } pform_packages[use_name] = pform_cur_package; pform_cur_package = 0; pform_pop_scope(); } /* * Do the import early, during processing. This requires that the * package is declared in pform ahead of time (it is) and that we can * simply transfer definitions to the current scope (we can). */ void pform_package_import(const struct vlltype&, PPackage*pkg, const char*ident) { LexicalScope*scope = pform_peek_scope(); if (ident) { perm_string use_ident = lex_strings.make(ident); map::const_iterator cur = pkg->parameters.find(use_ident); if (cur != pkg->parameters.end()) { scope->imports[cur->first] = pkg; return; } cur = pkg->localparams.find(use_ident); if (cur != pkg->localparams.end()) { scope->imports[cur->first] = pkg; return; } map::const_iterator tcur; tcur = pkg->typedefs.find(use_ident); if (tcur != pkg->typedefs.end()) { scope->imports[tcur->first] = pkg; return; } map::const_iterator fcur; fcur = pkg->funcs.find(use_ident); if (fcur != pkg->funcs.end()) { scope->imports[fcur->first] = pkg; return; } map::const_iterator ttcur; ttcur = pkg->tasks.find(use_ident); if (ttcur != pkg->tasks.end()) { scope->imports[ttcur->first] = pkg; return; } map::const_iterator wcur; wcur = pkg->wires.find(use_ident); if (wcur != pkg->wires.end()) { scope->imports[wcur->first] = pkg; return; } ostringstream msg; msg << "Symbol " << use_ident << " not found in package " << pkg->pscope_name() << "." << ends; VLerror(msg.str().c_str()); return; } else { // Handle the pkg::* case by importing everything from // the package. for (map::const_iterator cur = pkg->parameters.begin() ; cur != pkg->parameters.end() ; ++cur) { scope->imports[cur->first] = pkg; } for (map::const_iterator cur = pkg->localparams.begin() ; cur != pkg->localparams.end() ; ++cur) { scope->imports[cur->first] = pkg; } for (map::const_iterator cur = pkg->typedefs.begin() ; cur != pkg->typedefs.end() ; ++cur) { scope->imports[cur->first] = pkg; } for (map::const_iterator cur = pkg->funcs.begin() ; cur != pkg->funcs.end() ; ++cur) { scope->imports[cur->first] = pkg; } } } PExpr* pform_package_ident(const struct vlltype&loc, PPackage*pkg, pform_name_t*ident_name) { assert(ident_name); PEIdent*tmp = new PEIdent(pkg, *ident_name); FILE_NAME(tmp, loc); return tmp; } data_type_t* pform_test_type_identifier(PPackage*pkg, const char*txt) { perm_string use_name = lex_strings.make(txt); map::const_iterator cur = pkg->typedefs.find(use_name); if (cur != pkg->typedefs.end()) return cur->second; return 0; } /* * The lexor uses this function to know if the identifier names the * package. It will call this a PACKAGE_IDENTIFIER token in that case, * instead of a generic IDENTIFIER. */ PPackage* pform_test_package_identifier(const char*pkg_name) { perm_string use_name = lex_strings.make(pkg_name); map::const_iterator pcur = pform_packages.find(use_name); if (pcur == pform_packages.end()) return 0; assert(pcur->second); return pcur->second; } iverilog-10_1/pform_pclass.cc000066400000000000000000000115371265551621300163500ustar00rootroot00000000000000/* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform.h" # include "PClass.h" # include "parse_misc.h" /* * The functions here help the parser put together class type declarations. */ static PClass*pform_cur_class = 0; /* * The base_type is set to the base class if this declaration is * starting a derived class. For example, for the syntax: * * class foo extends bar (exprs) ... * * the base_type is the type of the class "bar", and the base_exprs, * if present, are the "exprs" that would be passed to a chained * constructor. */ void pform_start_class_declaration(const struct vlltype&loc, class_type_t*type, data_type_t*base_type, list*base_exprs) { PClass*class_scope = pform_push_class_scope(loc, type->name); class_scope->type = type; assert(pform_cur_class == 0); pform_cur_class = class_scope; assert(type->base_type == 0); type->base_type = base_type; assert(type->base_args.empty()); if (base_exprs) { for (list::iterator cur = base_exprs->begin() ; cur != base_exprs->end() ; ++ cur) { type->base_args.push_back(*cur); } delete base_exprs; } } void pform_class_property(const struct vlltype&loc, property_qualifier_t property_qual, data_type_t*data_type, list*decls) { assert(pform_cur_class); // Add the non-static properties to the class type // object. Unwind the list of names to make a map of name to // type. for (list::iterator cur = decls->begin() ; cur != decls->end() ; ++cur) { decl_assignment_t*curp = *cur; data_type_t*use_type = data_type; if (! curp->index.empty()) { list*pd = new list (curp->index); use_type = new uarray_type_t(use_type, pd); } pform_cur_class->type->properties[curp->name] = class_type_t::prop_info_t(property_qual,use_type); if (PExpr*rval = curp->expr.release()) { PExpr*lval = new PEIdent(curp->name); FILE_NAME(lval, loc); PAssign*tmp = new PAssign(lval, rval); FILE_NAME(tmp, loc); if (property_qual.test_static()) pform_cur_class->type->initialize_static.push_back(tmp); else pform_cur_class->type->initialize.push_back(tmp); } } } void pform_set_this_class(const struct vlltype&loc, PTaskFunc*net) { if (pform_cur_class == 0) return; list*this_name = new list; this_name->push_back(perm_string::literal("@")); vector*this_port = pform_make_task_ports(loc, NetNet::PINPUT, pform_cur_class->type, this_name); // The pform_make_task_ports() function deletes the this_name // object. assert(this_port->at(0).defe == 0); PWire*this_wire = this_port->at(0).port; delete this_port; net->set_this(pform_cur_class->type, this_wire); } void pform_set_constructor_return(PFunction*net) { assert(pform_cur_class); net->set_return(pform_cur_class->type); } /* * A constructor is basically a function with special implications. */ PFunction*pform_push_constructor_scope(const struct vlltype&loc) { assert(pform_cur_class); PFunction*func = pform_push_function_scope(loc, "new", true); return func; } void pform_end_class_declaration(const struct vlltype&loc) { assert(pform_cur_class); // If there were initializer statements, then collect them // into an implicit constructor function. if (! pform_cur_class->type->initialize.empty()) { PFunction*func = pform_push_function_scope(loc, "new@", true); func->set_ports(0); pform_set_constructor_return(func); pform_set_this_class(loc, func); class_type_t*use_class = pform_cur_class->type; if (use_class->initialize.size() == 1) { func->set_statement(use_class->initialize.front()); } else { PBlock*tmp = new PBlock(PBlock::BL_SEQ); tmp->set_statement(use_class->initialize); func->set_statement(tmp); } pform_pop_scope(); } pform_cur_class = 0; pform_pop_scope(); } iverilog-10_1/pform_string_type.cc000066400000000000000000000030041265551621300174200ustar00rootroot00000000000000/* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform.h" # include "parse_misc.h" # include "ivl_assert.h" static void pform_set_string_type(const string_type_t*, perm_string name, NetNet::Type net_type, list*attr) { PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING); assert(net); pform_bind_attributes(net->attributes, attr, true); } void pform_set_string_type(const string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { pform_set_string_type(string_type, *cur, net_type, attr); } } iverilog-10_1/pform_struct_type.cc000066400000000000000000000073621265551621300174510ustar00rootroot00000000000000/* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform.h" # include "parse_misc.h" # include "ivl_assert.h" ivl_variable_type_t struct_type_t::figure_packed_base_type(void) const { if (! packed_flag) return IVL_VT_NO_TYPE; if (members.get() == 0) return IVL_VT_NO_TYPE; ivl_variable_type_t base_type = IVL_VT_BOOL; ivl_assert(*this, members.get()); for (list::iterator cur = members->begin() ; cur != members->end() ; ++ cur) { struct_member_t*tmp = *cur; ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; if (tmp->type.get()) tmp_type = tmp->type->figure_packed_base_type(); if (tmp_type == IVL_VT_BOOL) { continue; } if (tmp_type == IVL_VT_LOGIC) { base_type = IVL_VT_LOGIC; continue; } // Oh no! Member is not a packable type! return IVL_VT_NO_TYPE; } return base_type; } /* * When we parse a packed struct, we can early on (right here) figure * out the base type of the packed variable. Elaboration, later on, * well figure out the rest. */ static void pform_set_packed_struct(struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) { ivl_variable_type_t base_type = struct_type->figure_packed_base_type(); PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, base_type); assert(net); net->set_data_type(struct_type); pform_bind_attributes(net->attributes, attr, true); } static void pform_set_struct_type(struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) { if (struct_type->packed_flag) { pform_set_packed_struct(struct_type, name, net_type, attr); return; } // For now, can only handle packed structs. The parser generates // a "sorry" message, so no need to do anything here. } void pform_set_struct_type(struct_type_t*struct_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { pform_set_struct_type(struct_type, *cur, net_type, attr); } } static void pform_makewire(const struct vlltype&li, struct_type_t*struct_type, NetNet::PortType ptype, perm_string name, list*) { ivl_variable_type_t base_type = struct_type->figure_packed_base_type(); PWire*cur = pform_get_make_wire_in_scope(name, NetNet::WIRE, ptype, base_type); assert(cur); FILE_NAME(cur, li); cur->set_data_type(struct_type); } void pform_makewire(const struct vlltype&li, struct_type_t*struct_type, NetNet::PortType ptype, list*names, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; pform_makewire(li, struct_type, ptype, txt, attr); } delete names; } iverilog-10_1/pform_types.cc000066400000000000000000000024471265551621300162270ustar00rootroot00000000000000/* * Copyright (c) 2007-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pform_types.h" data_type_t::~data_type_t() { } string_type_t::~string_type_t() { } ivl_variable_type_t data_type_t::figure_packed_base_type(void) const { return IVL_VT_NO_TYPE; } ivl_variable_type_t parray_type_t::figure_packed_base_type(void) const { return base_type->figure_packed_base_type(); } ivl_variable_type_t vector_type_t::figure_packed_base_type(void) const { return base_type; } atom2_type_t size_type (32, true); iverilog-10_1/pform_types.h000066400000000000000000000274561265551621300161000ustar00rootroot00000000000000#ifndef IVL_pform_types_H #define IVL_pform_types_H /* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // This for the perm_string type. # include "StringHeap.h" # include "LineInfo.h" # include "verinum.h" # include "named.h" # include "netstruct.h" # include "property_qual.h" # include "ivl_target.h" # include # include # include # include # include /* * parse-form types. */ class Design; class NetScope; class Definitions; class PExpr; class PWire; class Statement; class ivl_type_s; class netclass_t; class netenum_t; typedef named named_number_t; typedef named named_pexpr_t; /* * The pform_range_t holds variable dimensions for type * declarations. The two expressions are interpreted as the first and * last values of the range. For example: * * [ : ] -- Normal array range * first == * second = * * [] -- SystemVerilog canonical range * first = PENumber(0) * second = - 1; * * [ ] -- Dynamic array * first = 0 * second = 0 * * [ $ ] -- Queue type * first = PENull * second = 0 */ typedef std::pair pform_range_t; /* * Semantic NOTES: * - The SEL_BIT is a single expression. This might me a bit select * of a vector, or a word select of an array. * * - The SEL_BIT_LAST index component is an array/queue [$] index, * that is the last item in the variable. */ struct index_component_t { enum ctype_t { SEL_NONE, SEL_BIT, SEL_BIT_LAST, SEL_PART, SEL_IDX_UP, SEL_IDX_DO }; index_component_t() : sel(SEL_NONE), msb(0), lsb(0) { }; ~index_component_t() { } ctype_t sel; class PExpr*msb; class PExpr*lsb; }; struct name_component_t { explicit name_component_t(perm_string n) : name(n) { } ~name_component_t() { } perm_string name; std::listindex; }; struct decl_assignment_t { perm_string name; std::listindex; std::auto_ptr expr; }; struct pform_tf_port_t { PWire*port; PExpr*defe; inline pform_tf_port_t() : port(0), defe(0) { } inline explicit pform_tf_port_t(PWire*p) : port(p), defe(0) { } }; /* * This is the base class for data types that are matched by the * "data_type" rule in the parse rule. We make the type virtual so * that dynamic types will work. */ class data_type_t : public LineInfo { public: inline explicit data_type_t() { } virtual ~data_type_t() = 0; // This method is used to figure out the base type of a packed // compound object. Return IVL_VT_NO_TYPE if the type is not packed. virtual ivl_variable_type_t figure_packed_base_type(void)const; // This method is used by the pform dumper to diagnostic dump. virtual void pform_dump(std::ostream&out, unsigned indent) const; ivl_type_s* elaborate_type(Design*des, NetScope*scope); private: // Elaborate the type to an ivl_type_s type. virtual ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; // Keep per-scope elaboration results cached. std::map cache_type_elaborate_; }; struct void_type_t : public data_type_t { virtual void pform_dump(std::ostream&out, unsigned indent) const; }; /* * The enum_type_t holds the parsed declaration to represent an * enumeration. Since this is in the pform, it represents the type * before elaboration so the range, for example, may not be complete * until it is elaborated in a scope. */ struct enum_type_t : public data_type_t { // Return the elaborated version of the type. virtual ivl_type_s*elaborate_type_raw(Design*des, NetScope*scope) const; ivl_variable_type_t base_type; bool signed_flag; bool integer_flag; // True if "integer" was used std::auto_ptr< list > range; std::auto_ptr< list > names; LineInfo li; }; struct struct_member_t : public LineInfo { std::auto_ptr type; std::auto_ptr< list > names; void pform_dump(std::ostream&out, unsigned indent) const; }; struct struct_type_t : public data_type_t { virtual ivl_variable_type_t figure_packed_base_type(void)const; virtual void pform_dump(std::ostream&out, unsigned indent) const; virtual netstruct_t* elaborate_type_raw(Design*des, NetScope*scope) const; bool packed_flag; bool union_flag; std::auto_ptr< list > members; }; struct atom2_type_t : public data_type_t { inline explicit atom2_type_t(int tc, bool flag) : type_code(tc), signed_flag(flag) { } int type_code; bool signed_flag; ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; }; extern atom2_type_t size_type; /* * The vector_type_t class represents types in the old Verilog * way. Some typical examples: * * logic signed [7:0] foo * bit unsigned foo * reg foo * * There are a few special cases: * * For the most part, Verilog treats "logic" and "reg" as synonyms, * but there are a few cases where the parser needs to know the * difference. So "reg_flag" is set to true if the IVL_VT_LOGIC type * is due to the "reg" keyword. * * If there are no reg/logic/bit/bool keywords, then Verilog will * assume the type is logic, but the context may need to know about * this case, so the implicit_flag member is set to true in that case. */ struct vector_type_t : public data_type_t { inline explicit vector_type_t(ivl_variable_type_t bt, bool sf, std::list*pd) : base_type(bt), signed_flag(sf), reg_flag(false), integer_flag(false), implicit_flag(false), pdims(pd) { } virtual ivl_variable_type_t figure_packed_base_type(void)const; virtual void pform_dump(std::ostream&out, unsigned indent) const; ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; ivl_variable_type_t base_type; bool signed_flag; bool reg_flag; // True if "reg" was used bool integer_flag; // True if "integer" was used bool implicit_flag; // True if this type is implicitly logic/reg std::auto_ptr< list > pdims; }; struct array_base_t : public data_type_t { public: inline explicit array_base_t(data_type_t*btype, std::list*pd) : base_type(btype), dims(pd) { } data_type_t*base_type; std::auto_ptr< list > dims; }; /* * The parray_type_t is a generalization of the vector_type_t in that * the base type is another general data type. Ultimately, the subtype * must also be packed (as this is a packed array) but that may be * worked out during elaboration. */ struct parray_type_t : public array_base_t { inline explicit parray_type_t(data_type_t*btype, std::list*pd) : array_base_t(btype, pd) { } virtual ivl_variable_type_t figure_packed_base_type(void)const; virtual void pform_dump(std::ostream&out, unsigned indent) const; virtual ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; }; /* * The uarray_type_t represents unpacked array types. */ struct uarray_type_t : public array_base_t { inline explicit uarray_type_t(data_type_t*btype, std::list*pd) : array_base_t(btype, pd) { } public: virtual void pform_dump(std::ostream&out, unsigned indent) const; virtual ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; }; struct real_type_t : public data_type_t { enum type_t { REAL, SHORTREAL }; inline explicit real_type_t(type_t tc) : type_code(tc) { } type_t type_code; ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; }; struct string_type_t : public data_type_t { inline explicit string_type_t() { } ~string_type_t(); ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; }; struct class_type_t : public data_type_t { inline explicit class_type_t(perm_string n) : name(n), base_type(0), save_elaborated_type(0) { } void pform_dump(std::ostream&out, unsigned indent) const; void pform_dump_init(std::ostream&out, unsigned indent) const; // This is the name of the class type. perm_string name; // This is the named type that is supposed to be the base // class that we are extending. This is nil if there is no // hierarchy. If there are arguments to the base class, then // put them in the base_args vector. data_type_t*base_type; std::listbase_args; // This is a map of the properties. Map the name to the type. struct prop_info_t { inline prop_info_t() : qual(property_qualifier_t::make_none()), type(0) { } inline prop_info_t(property_qualifier_t q, data_type_t*t) : qual(q), type(t) { } property_qualifier_t qual; data_type_t* type; }; std::map properties; // This is an ordered list of property initializers. The name // is the name of the property to be assigned, and the val is // the expression that is assigned. std::vector initialize; // This is an ordered list of property initializers for static // properties. These are run in a synthetic "initial" block // without waiting for any constructor. std::vector initialize_static; ivl_type_s* elaborate_type_raw(Design*, NetScope*) const; // The save_elaborated_type member must be set to the pointer // to the netclass_t object that is created to represent this // type. The elaborate_type_raw() method uses this pointer, // and it is used in some other situations as well. netclass_t* save_elaborated_type; }; /* * The pform_name_t is the general form for a hierarchical * identifier. It is an ordered list of name components. Each name * component is an identifier and an optional list of bit/part * selects. The simplest name component is a simple identifier: * * foo * * The bit/part selects come from the source and are made part of the * name component. A bit select is a single number that may be a bit * select of a vector or a word select of an array: * * foo[5] -- a bit select/word index * foo[6:4] -- a part select * * The index components of a name component are collected into an * ordered list, so there may be many, for example: * * foo[5][6:4] -- a part select of an array word * * The pform_name_t, then, is an ordered list of these name * components. The list of names comes from a hierarchical name in the * source, like this: * * foo[5].bar[6:4] -- a part select of a vector in sub-scope foo[5]. */ typedef std::list pform_name_t; inline perm_string peek_head_name(const pform_name_t&that) { return that.front().name; } inline perm_string peek_tail_name(const pform_name_t&that) { return that.back().name; } extern std::ostream& operator<< (std::ostream&out, const pform_name_t&); extern std::ostream& operator<< (std::ostream&out, const name_component_t&that); extern std::ostream& operator<< (std::ostream&out, const index_component_t&that); #endif /* IVL_pform_types_H */ iverilog-10_1/property_qual.h000066400000000000000000000045271265551621300164310ustar00rootroot00000000000000#ifndef IVL_property_qual_H #define IVL_property_qual_H /* * Copyright (c) 2013-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ class property_qualifier_t { public: static inline property_qualifier_t make_none() { property_qualifier_t res; res.mask_ = 0; return res; } static inline property_qualifier_t make_static() { property_qualifier_t res; res.mask_ = 1; return res; } static inline property_qualifier_t make_protected() { property_qualifier_t res; res.mask_ = 2; return res; } static inline property_qualifier_t make_local() { property_qualifier_t res; res.mask_ = 4; return res; } static inline property_qualifier_t make_rand() { property_qualifier_t res; res.mask_ = 8; return res; } static inline property_qualifier_t make_randc() { property_qualifier_t res; res.mask_ = 16; return res; } static inline property_qualifier_t make_const() { property_qualifier_t res; res.mask_ = 32; return res; } inline property_qualifier_t operator | (property_qualifier_t r) { property_qualifier_t res; res.mask_ = mask_ | r.mask_; return res; } public: inline bool test_static() const { return mask_ & 1; } inline bool test_protected() const { return mask_ & 2; } inline bool test_local() const { return mask_ & 4; } inline bool test_rand() const { return mask_ & 8; } inline bool test_randc() const { return mask_ & 16; } inline bool test_const() const { return mask_ & 32; } private: int mask_; }; #endif /* IVL_property_qual_H */ iverilog-10_1/scripts/000077500000000000000000000000001265551621300150315ustar00rootroot00000000000000iverilog-10_1/scripts/CREATE_VERSION.sh000066400000000000000000000006641265551621300175430ustar00rootroot00000000000000#!/bin/sh # This script manually creates a version.h file. # # It is used when creating a MinGW executable from a Cygwin # hosted git repository. It assumes that git is available. # # sh scripts/CREATE_VERSION.sh # echo "Building version_tag.h with git describe" tmp=`git describe | sed -e 's;\(.*\);#define VERSION_TAG "\1";'` echo "#ifndef VERSION_TAG" > version_tag.h echo "$tmp" >> version_tag.h echo "#endif" >> version_tag.h iverilog-10_1/scripts/MAKE_RELEASE.sh000066400000000000000000000036351265551621300172510ustar00rootroot00000000000000#!/bin/sh # This script makes a release from a git repository. The input is # the number for a snapshot and the path to a temporary directory. # for example: # # sh scripts/MAKE_RELEASE.sh 10.1 ~/tmp # # The above assumes that there is a tag "v10_1" at the point # to be released. (The tag has the "v", but the argument to this # script does not have the "v"). This script extracts based on the # tag, uses the temporary directory to stage intermediate results, # and finally creates a file called verilog-10.1.tar.gz that # contains the release ready to go. # # The complete steps to make a release x.y generally are: # # Edit version_base.h to suit. # # Edit verilog.spec to suit. # # git tag -a v10_1 # (Make the tag in the local git repository.) # # sh scripts/MAKE_RELEASE.sh 10.1 ~/tmp # (Make the snapshot bundle verilog-10.1.tar.gz) # # git push --tags # (Publish the tag to the repository.) # id=$1 destdir=$2 # The git tag to use. tag="v"`echo $id | tr '.' '_'` # The prefix is the directory that contains the extracted files # of the bundle. This is also the name of the bundle file itself. prefix="verilog-$id" if [ ! -d $destdir ]; then echo "ERROR: Directory $destdir does not exist." exit 1 fi if [ -e $destdir/$prefix ]; then echo "ERROR: $destdir/$prefix already exists." exit 1 fi echo "Exporting $tag to $destdir/$prefix..." git archive --prefix="$prefix/" $tag | ( cd "$destdir" && tar xf - ) versionh="$destdir/$prefix/version_tag.h" echo "Create $versionh ..." echo "#ifndef VERSION_TAG" > $versionh echo "#define VERSION_TAG \"$tag\"" >> $versionh echo "#endif" >> $versionh echo "Running autoconf.sh..." ( cd $destdir/$prefix && sh autoconf.sh ) echo "Making bundle $prefix.tar.gz..." tar czf $prefix.tar.gz --exclude=autom4te.cache -C "$destdir" $prefix echo "Removing temporary $destdir/$prefix..." rm -rf "$destdir/$prefix" echo done iverilog-10_1/scripts/MAKE_SNAPSHOT.sh000066400000000000000000000037121265551621300174240ustar00rootroot00000000000000#!/bin/sh # This script makes snapshots from a git repository. The input is # the number for a snapshot and the path to a temporary directory. # for example: # # sh scripts/MAKE_SNAPSHOT.sh 20080428 ~/tmp # # The above assumes that there is a tag "s20080428" at the point # to be snapshot. (The tag has the "s", but the argument to this # script does not have the "s"). This script extracts based on the # tag, uses the temporary directory to stage intermediate results, # and finally creates a file called verilog-20080428.tar.gz that # contains the snapshot ready to go. # # The complete steps to make a snapshot YYYYMMDD generally are: # # edit the verilog.spec to set the rev_date to YYYYMMDD # # git tag -a sYYYYMMDD # (Make the tag in the local git repository.) # # sh scripts/MAKE_SNAPSHOT.sh YYYYMMDD ~/tmp # (Make the snapshot bundle verilog-YYYYMMDD.tar.gz) # # git push --tags # (Publish the tag to the repository.) # id=$1 destdir=$2 # The git tag to use is the snapshot id with a prepended "s". tag="s$id" # The prefix is the directory that contains the extracted files # of the bundle. This is also the name of the bundle file itself. prefix="verilog-$id" if [ ! -d $destdir ]; then echo "ERROR: Directory $destdir does not exist." exit 1 fi if [ -e $destdir/$prefix ]; then echo "ERROR: $destdir/$prefix already exists." exit 1 fi echo "Exporting $tag to $destdir/$prefix..." git archive --prefix="$prefix/" $tag | ( cd "$destdir" && tar xf - ) versionh="$destdir/$prefix/version_tag.h" echo "Create $versionh ..." echo "#ifndef VERSION_TAG" > $versionh echo "#define VERSION_TAG \"$tag\"" >> $versionh echo "#endif" >> $versionh echo "Running autoconf.sh..." ( cd $destdir/$prefix && sh autoconf.sh ) echo "Making bundle $prefix.tar.gz..." tar czf $prefix.tar.gz --exclude=autom4te.cache -C "$destdir" $prefix echo "Removing temporary $destdir/$prefix..." rm -rf "$destdir/$prefix" echo done iverilog-10_1/scripts/devel-stub.conf000066400000000000000000000013121265551621300177470ustar00rootroot00000000000000# # This is a debug conf file that the scripts/devel-stub.sh script uses # to control the ivl core. The contents of this file are normally written # to a temporary file by the driver program, but for devel purposes, where # the driver program is not used, this config substitutes. # # NOTE: DO NOT INSTALL THIS FILE! # generation:2009 generation:specify generation:assertions generation:xtypes generation:verilog-ams iwidth:32 sys_func:vpi/system.sft sys_func:vpi/v2005_math.sft sys_func:vpi/va_math.sft warnings:adfilnpstv debug:eval_tree debug:elaborate debug:emit debug:elab_pexpr debug:scopes debug:synth2 debug:optimizer out:a.out ivlpp:./ivlpp/ivlpp -D__ICARUS__ -L -Pfoo.pp sys_func:scripts/devel-stub.sft iverilog-10_1/scripts/devel-stub.sft000066400000000000000000000001451265551621300176210ustar00rootroot00000000000000 # This is an example function table. $realtime vpiSysFuncReal $verywide vpiSysFuncSized 128 signed iverilog-10_1/scripts/devel-stub.sh000066400000000000000000000011371265551621300174410ustar00rootroot00000000000000 # This is a little developer convenience script to run the ivl core program # in place with the stub target. It runs the ivl core verbose, with diagnostic # output files enable, and without the driver program or preprocessor. # It is useful only for development of the ivl core program. # # Run this script in the source directory for the ivl core program so that # the patch to the other components is correct. # # NOTE: DO NOT INSTALL THIS FILE. ./ivl -v -Ctgt-stub/stub.conf -C./scripts/devel-stub.conf -Pa.pf -Na.net -fDLL=tgt-stub/stub.tgt foo.vl | tee foo.log 2>&1 echo "*** ivl command completed" iverilog-10_1/solaris/000077500000000000000000000000001265551621300150165ustar00rootroot00000000000000iverilog-10_1/solaris/README-solaris_pkg.txt000066400000000000000000000024331265551621300210310ustar00rootroot00000000000000Notes about the solaris package. I. Installing a prebuilt solaris package ----------------------------------------- To install the solaris package do the following as root on your machine: 1) uncompress the package using: cp package_name.gz /tmp cd /tmp gunzip package_name.gz where "package_name.gz" is the compressed binary package. It will be named something like "verilog-0.3-SunOS-5.6-sparc.gz" 2) install the package using: cd /tmp pkgadd -d package_name this will install the package. The package will be registered under the name "IVLver" II. Uninstalling the solaris package ------------------------------------- To uninstall an installed solaris package do the following as root on your machine: pkgrm IVLver III. Notes on building a solaris package from sources ------------------------------------------------------ 1) build and install verilog. Be sure and pick where the package should install with the "--prefix=" argument to 'configure' 2) edit the 'pkginfo' file to update the version number and also set BASEDIR to the same as the argument to "--prefix=" 3) edit the 'prototype' file to add/removed files/directories from the list of installed components. 4) run the 'mksolpkg' script to create the solaris package iverilog-10_1/solaris/mksolpkg000077500000000000000000000020521265551621300165720ustar00rootroot00000000000000#!/bin/sh # # mksolpkg # a script to generate a native solaris package # if [ `whoami` != "root" ]; then echo "you must be root to run this script" exit 1 fi # an ugly hack to get various bits of info ver=`grep VERSION pkginfo | sed 's/"/ /g' | awk '{print $2}'` basedir=`grep BASEDIR pkginfo | sed 's/"/ /g' | awk '{print $2}'` name=`grep NAME pkginfo | sed 's/"/ /g' | awk '{print $2}'` pkg=`grep PKG pkginfo | sed 's/"/ /g' | awk '{print $2}'` arch=`grep ARCH pkginfo | sed 's/"/ /g' | awk '{print $2}'` march=`uname -p` if [ "$arch" != "$march" ]; then echo "Warning: you have listed \"$arch\" in the pkginfo file but this machine" echo " has a \"$march\" processor" exit 1 fi oslabel=`uname -s`-`uname -r`-$march fname=$name-$ver-$oslabel cp -f prototype $basedir cp -f pkginfo $basedir cd $basedir pkgmk -o -r `pwd` cd /var/spool/pkg pkgtrans -s `pwd` /tmp/$fname all cd /tmp gzip -f $fname echo "Your $oslabel package is left in /tmp/$fname.gz" # cleanup rm -f $basedir/prototype $basedir/pkginfo rm -fr /var/spool/pkg/$pkg iverilog-10_1/solaris/pkginfo000066400000000000000000000002711265551621300163760ustar00rootroot00000000000000PKG="IVLver" NAME="verilog" ARCH="sparc" VERSION="0.7" CATEGORY="application" VENDOR="Icarus.com" EMAIL="steve@icarus.com" PSTAMP="Stephen Williams" BASEDIR="/usr/local" CLASSES="none" iverilog-10_1/solaris/prototype000066400000000000000000000015601265551621300170100ustar00rootroot00000000000000i pkginfo=./pkginfo d none bin 0755 bin bin f none bin/iverilog 0755 bin bin f none bin/iverilog-vpi 0755 bin bin f none bin/vvp 0755 bin bin d none include 0755 bin bin f none include/acc_user.h 0644 bin bin f none include/ivl_target.h 0644 bin bin f none include/veriuser.h 0644 bin bin f none include/vpi_user.h 0644 bin bin d none lib 0755 bin bin d none lib/ivl 0755 bin bin f none lib/ivl/fpga.tgt 0644 bin bin f none lib/ivl/iverilog.conf 0644 bin bin f none lib/ivl/ivl 0755 bin bin f none lib/ivl/ivlpp 0755 bin bin f none lib/ivl/null.tgt 0644 bin bin f none lib/ivl/system.vpi 0644 bin bin f none lib/ivl/vvp.tgt 0644 bin bin f none lib/libveriuser.a 0644 bin bin f none lib/libvpi.a 0644 bin bin d none man 0755 bin bin d none man/man1 0755 bin bin f none man/man1/iverilog.1 0644 bin bin f none man/man1/iverilog-vpi.1 0644 bin bin f none man/man1/vvp.1 0644 bin bin iverilog-10_1/sv_vpi_user.h000066400000000000000000000041521265551621300160610ustar00rootroot00000000000000#ifndef SV_VPI_USER_H #define SV_VPI_USER_H /* * Copyright (c) 2010-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vpi_user.h" #if defined(__MINGW32__) || defined (__CYGWIN32__) # define DLLEXPORT __declspec(dllexport) #else # define DLLEXPORT #endif #ifdef __cplusplus # define EXTERN_C_START extern "C" { # define EXTERN_C_END } #else # define EXTERN_C_START # define EXTERN_C_END #endif #ifndef __GNUC__ # undef __attribute__ # define __attribute__(x) #endif EXTERN_C_START /********* OBJECT TYPES ***********/ #define vpiPackage 600 #define vpiArrayType 606 #define vpiStaticArray 1 #define vpiDynamicArray 2 #define vpiAssocArray 3 #define vpiQueueArray 4 #define vpiLongIntVar 610 #define vpiShortIntVar 611 #define vpiIntVar 612 #define vpiByteVar 614 #define vpiLogicVar vpiReg #define vpiClassVar 615 #define vpiStringVar 616 #define vpiBitVar 620 #define vpiArrayVar vpiRegArray /********* TYPESPECS *************/ #define vpiClassTypespec 630 #define vpiEnumTypespec 633 #define vpiEnumConst 634 #define vpiClassDefn 652 /********* One-to-One ***********/ #define vpiBaseTypespec 703 /********* Many-to-One ***********/ #define vpiMember 742 EXTERN_C_END #endif /* SV_VPI_USER_H */ iverilog-10_1/svector.h000066400000000000000000000072151265551621300152050ustar00rootroot00000000000000#ifndef IVL_svector_H #define IVL_svector_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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. In order to redistribute the software in * binary form, you will need a Picture Elements Binary Software * 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 GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include /* * This is a way simplified vector class that cannot grow or shrink, * and is really only able to handle values. It is intended to be * lighter weight than the STL list class. */ template class svector { public: explicit svector() : nitems_(0), items_(0) { } explicit svector(unsigned size) : nitems_(size), items_(new TYPE[size]) { for (unsigned idx = 0 ; idx < size ; idx += 1) items_[idx] = 0; } svector(const svector&that) : nitems_(that.nitems_), items_(new TYPE[nitems_]) { for (unsigned idx = 0 ; idx < that.nitems_ ; idx += 1) items_[idx] = that[idx]; } svector(const svector&l, const svector&r) : nitems_(l.nitems_ + r.nitems_), items_(new TYPE[nitems_]) { for (unsigned idx = 0 ; idx < l.nitems_ ; idx += 1) items_[idx] = l[idx]; for (unsigned idx = 0 ; idx < r.nitems_ ; idx += 1) items_[l.nitems_+idx] = r[idx]; } svector(const svector&l, TYPE r) : nitems_(l.nitems_ + 1), items_(new TYPE[nitems_]) { for (unsigned idx = 0 ; idx < l.nitems_ ; idx += 1) items_[idx] = l[idx]; items_[nitems_-1] = r; } ~svector() { delete[]items_; } svector& operator= (const svector&that) { if (&that == this) return *this; delete[]items_; nitems_ = that.nitems_; items_ = new TYPE[nitems_]; for (unsigned idx = 0 ; idx < nitems_ ; idx += 1) { items_[idx] = that.items_[idx]; } return *this; } unsigned count() const { return nitems_; } TYPE&operator[] (unsigned idx) { assert(idx < nitems_); return items_[idx]; } TYPE operator[] (unsigned idx) const { assert(idx < nitems_); return items_[idx]; } private: unsigned nitems_; TYPE* items_; }; /* * Override the implementation of the above template for the string * type parameter. The initialization to nil works different here. */ template <> inline svector::svector(unsigned size) : nitems_(size), items_(new std::string[size]) { } /* * This is a convenience function that converts an svector to a * vector. This is to ease the transition from svector to vector so * that the svector class can be gradually removed. */ template inline std::vector vector_from_svector(const svector&that) { std::vector res (that.count()); for (unsigned idx = 0 ; idx < that.count() ; idx += 1) res[idx] = that[idx]; return res; } #endif /* IVL_svector_H */ iverilog-10_1/swift.txt000066400000000000000000000045311265551621300152420ustar00rootroot00000000000000 SWIFT MODEL SUPPORT FOR Icarus Verilog (PRELIMINARY) Copyright 2003 Stephen Williams NOTE: SWIFT support does not work yet, these are provisional instructions, intended to show what's supposed to happen when I get it working. Icarus Verilog support for SWIFT models is based on the LMTV interface module from Synopsys. This module is normally distributed along with the SWIFT models proper. This module can be linked with Icarus Verilog via the cadpli compatibility object. (See cadpli.txt.) * Preliminaries First, you need the LMC_HOME environment variable set to point to the installed directory for your SWIFT software. This setup is documented in your SWIFT model documentation. * Compilation When compiling your Verilog design to include a SWIFT model, you need to include wrappers for the model you intend to use. You may choose to use ncverilog or verilogxl compatible wrappers, they work the same. Locate your smartmodel directory, and include it in your command file like so: +libdir+.../smartmodel/sol/wrappers/verilogxl The wrappers directory includes Verilog modules that wrap your SWIFT module, and with this +libdir+ statement in your command file, the Icarus Verilog compiler will be able to locate these wrappers. The wrappers in turn invoke the $lm_model system tasks that are the LMTV support for your model. NOTE: This example uses the solaris directory of VerilogXL support files as a source of wrappers. The files of interest, however, are written in Verilog and are identical for all supported platforms, so long as you choose the verilogxl or ncverilog files. * Execution After your simulation is compiled, run the simulation with the vvp command, like this: % vvp -mcadpli a.out -cadpli=$LMC_HOME/lib/x86_linux.lib/swiftpli.so:swift_boot What this command line means is: -mcadpli Include the cadpli compatibility module a.out This is your compiled vvp file -cadpli=$LMC_HOME/lib/x86_linux.lib/swiftpli.so:swift_boot This tells the cadpli module to load the swiftpli.so shared object, and boot it. This is code that comes with your SWIFT modules, and provides the generic SWIFT capabilities (lm_* system tasks) needed by the module itself. Once you start the vvp command, the SWIFT infrastructure will be initialized as part of the simulation setup, and all should work normally from here. iverilog-10_1/symbol_search.cc000066400000000000000000000130651265551621300165100ustar00rootroot00000000000000/* * Copyright (c) 2003-2012 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netlist.h" # include "netmisc.h" # include "ivl_assert.h" /* * Search for the hierarchical name. */ struct symbol_search_results { inline symbol_search_results() { scope = 0; net = 0; par_val = 0; par_msb = 0; par_lsb = 0; eve = 0; } inline bool is_scope() const { if (net) return false; if (eve) return false; if (par_val) return false; if (scope) return true; return false; } // Scope where symbol was located. This is set in all cases, // assuming the search succeeded. NetScope*scope; // If this was a net, the signal itself. NetNet*net; // If this was a parameter, the value expression and the // optional value dimensions. const NetExpr*par_val; const NetExpr*par_msb; const NetExpr*par_lsb; // If this is a named event, ... NetEvent*eve; }; static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, pform_name_t path, struct symbol_search_results*res, NetScope*start_scope = 0) { assert(scope); bool prefix_scope = false; bool recurse_flag = false; ivl_assert(*li, ! path.empty()); name_component_t path_tail = path.back(); path.pop_back(); // If this is a recursive call, then we need to know that so // that we can enable the search for scopes. Set the // recurse_flag to true if this is a recurse. if (start_scope==0) start_scope = scope; else recurse_flag = true; // If there are components ahead of the tail, symbol_search // recursively. Ideally, the result is a scope that we search // for the tail key, but there are other special cases as well. if (! path.empty()) { symbol_search_results recurse; bool flag = symbol_search(li, des, scope, path, &recurse, start_scope); if (! flag) return false; // The prefix is found to be a scope, so switch to that // scope, set the hier_path to turn off upwards searches, // and continue our search for the tail. if (recurse.is_scope()) { scope = recurse.scope; prefix_scope = true; if (scope->is_auto() && li) { cerr << li->get_fileline() << ": error: Hierarchical " "reference to automatically allocated item " "`" << path_tail.name << "' in path `" << path << "'" << endl; des->errors += 1; } } else { // Prefix is present, but is NOT a scope. Fail! return false; } } while (scope) { if (path_tail.name == "#") { cerr << li->get_fileline() << ": sorry: " << "Implicit class handle \"super\" not supported." << endl; return false; } if (NetNet*net = scope->find_signal(path_tail.name)) { res->scope = scope; res->net = net; return true; } if (NetEvent*eve = scope->find_event(path_tail.name)) { res->scope = scope; res->eve = eve; return true; } if (const NetExpr*par = scope->get_parameter(des, path_tail.name, res->par_msb, res->par_lsb)) { res->scope = scope; res->par_val = par; return true; } if (recurse_flag) { bool flag = false; hname_t path_item = eval_path_component(des, start_scope, path_tail, flag); if (flag) { cerr << li->get_fileline() << ": XXXXX: Errors evaluating scope index" << endl; } else if (NetScope*chld = des->find_scope(scope, path_item)) { res->scope = chld; return true; } } // Don't scan up past a module boundary. if (scope->type()==NetScope::MODULE && !scope->nested_module()) break; // Don't scan up if we are searching within a prefixed scope. if (prefix_scope) break; scope = scope->parent(); } // Last chance: this is a single name, so it might be the name // of a root scope. Ask the design if this is a root // scope. This is only possible if there is no prefix. if (prefix_scope==false) { hname_t path_item (path_tail.name); scope = des->find_scope(path_item); if (scope) { res->scope = scope; return true; } } return false; } /* * Compatibility version. Remove me! */ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope, pform_name_t path, NetNet*&net, const NetExpr*&par, NetEvent*&eve, const NetExpr*&ex1, const NetExpr*&ex2) { symbol_search_results recurse; bool flag = symbol_search(li, des, scope, path, &recurse); net = recurse.net; par = recurse.par_val; ex1 = recurse.par_msb; ex2 = recurse.par_lsb; eve = recurse.eve; if (! flag) { return 0; } if (recurse.is_scope()) return recurse.scope; return recurse.scope; } iverilog-10_1/syn-rules.y000066400000000000000000000223011265551621300154730ustar00rootroot00000000000000 %{ /* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include /* * This file implements synthesis based on matching threads and * converting them to equivalent devices. The trick here is that the * proc_match_t functor can be used to scan a process and generate a * string of tokens. That string of tokens can then be matched by the * rules to determine what kind of device is to be made. */ # include "netlist.h" # include "netmisc.h" # include "functor.h" # include struct syn_token_t { int token; NetAssignBase*assign; NetProcTop*top; NetEvWait*evwait; NetEvent*event; NetExpr*expr; syn_token_t*next_; }; #define YYSTYPE syn_token_t* static int yylex(); static void yyerror(const char*); static Design*des_; static void make_DFF_CE(Design*des, NetProcTop*top, NetEvent*eclk, NetExpr*cexp, NetAssignBase*asn); %} %token S_ALWAYS S_ASSIGN S_ASSIGN_MEM S_ASSIGN_MUX S_ELSE S_EVENT %token S_EXPR S_IF S_INITIAL %% start /* These rules match simple DFF devices. Clocked assignments are simply implemented as DFF, and a CE is easily expressed with a conditional statement. The typical Verilog that get these are: always @(posedge CLK) Q = D always @(negedge CLK) Q = D always @(posedge CLK) if (CE) Q = D; always @(negedge CLK) if (CE) Q = D; The width of Q and D cause a wide register to be created. The code generators generally implement that as an array of flip-flops. */ : S_ALWAYS '@' '(' S_EVENT ')' S_ASSIGN ';' { make_DFF_CE(des_, $1->top, $4->event, 0, $6->assign); } | S_ALWAYS '@' '(' S_EVENT ')' S_IF S_EXPR S_ASSIGN ';' ';' { make_DFF_CE(des_, $1->top, $4->event, $7->expr, $8->assign); } /* Unconditional assignments in initial blocks should be made into initializers wherever possible. */ ; %% /* Various actions. */ static void hookup_DFF_CE(NetFF*ff, NetESignal*d, NetEvProbe*pclk, NetNet*ce, NetAssign_*a, unsigned rval_pinoffset) { if (rval_pinoffset != 0) { cerr << a->get_fileline() << ": sorry: " << "unable to hook up an R-value with offset " << rval_pinoffset << " to signal " << a->name() << "." << endl; return; } // a->sig() is a *NetNet, which doesn't have the loff_ and // lwid_ context. Add the correction for loff_ ourselves. // This extra calculation allows for assignments like: // lval[7:1] <= foo; // where lval is really a "reg [7:0]". In other words, part // selects in the l-value are handled by loff and the lwidth(). connect(ff->pin_Data(), d->sig()->pin(0)); connect(ff->pin_Q(), a->sig()->pin(0)); connect(ff->pin_Clock(), pclk->pin(0)); if (ce) connect(ff->pin_Enable(), ce->pin(0)); /* This lval_ represents a reg that is a WIRE in the synthesized results. This function signals the destructor to change the REG that this l-value refers to into a WIRE. It is done then, at the last minute, so that pending synthesis can continue to work with it as a WIRE. */ a->turn_sig_to_wire_on_release(); } static void make_DFF_CE(Design*des, NetProcTop*top, NetEvent*eclk, NetExpr*cexp, NetAssignBase*asn) { assert(asn); NetEvProbe*pclk = eclk->probe(0); NetESignal*d = dynamic_cast (asn->rval()); NetNet*ce = cexp? cexp->synthesize(des, top->scope(), cexp) : 0; if (d == 0) { cerr << asn->get_fileline() << ": internal error: " << " not a simple signal? " << *asn->rval() << endl; } assert(d); NetAssign_*a; unsigned rval_pinoffset=0; for (unsigned i=0; (a=asn->l_val(i)); i++) { // asn->l_val(i) are the set of *NetAssign_'s that form the list // of lval expressions. Treat each one independently, keeping // track of which bits of rval to use for each set of DFF inputs. // For example, given: // {carry,data} <= x + y + z; // run through this loop twice, where a and rval_pinoffset are // first data and 0, then carry and 1. // FIXME: ff gets its pin names wrong when loff_ is nonzero. if (a->sig()) { // cerr << "new NetFF named " << a->name() << endl; bool negedge = pclk->edge() == NetEvProbe::NEGEDGE; NetFF*ff = new NetFF(top->scope(), a->name(), negedge, a->sig()->vector_width()); hookup_DFF_CE(ff, d, pclk, ce, a, rval_pinoffset); des->add_node(ff); } rval_pinoffset += a->lwidth(); } des->delete_process(top); } static syn_token_t*first_ = 0; static syn_token_t*last_ = 0; static syn_token_t*ptr_ = 0; /* * The match class is used to take a process and turn it into a stream * of tokens. This stream is used by the yylex function to feed tokens * to the parser. */ struct tokenize : public proc_match_t { tokenize() { } ~tokenize() { } int assign(NetAssign*dev) { syn_token_t*cur; cur = new syn_token_t; // Bit Muxes can't be synthesized (yet), but it's too much // work to detect them now. // cur->token = dev->l_val(0)->bmux() ? S_ASSIGN_MUX : S_ASSIGN; cur->token = S_ASSIGN; cur->assign = dev; cur->next_ = 0; last_->next_ = cur; last_ = cur; return 0; } int assign_nb(NetAssignNB*dev) { syn_token_t*cur; cur = new syn_token_t; // Bit Muxes can't be synthesized (yet), but it's too much // work to detect them now. // cur->token = dev->l_val(0)->bmux() ? S_ASSIGN_MUX : S_ASSIGN; cur->token = S_ASSIGN; cur->assign = dev; cur->next_ = 0; last_->next_ = cur; last_ = cur; return 0; } int condit(NetCondit*dev) { syn_token_t*cur; cur = new syn_token_t; cur->token = S_IF; cur->next_ = 0; last_->next_ = cur; last_ = cur; cur = new syn_token_t; cur->token = S_EXPR; cur->expr = dev->expr(); cur->next_ = 0; last_->next_ = cur; last_ = cur; /* Because synthesis is broken this is needed to prevent * a seg. fault. */ if (!dev->if_clause()) return 0; dev -> if_clause() -> match_proc(this); if (dev->else_clause()) { cur = new syn_token_t; cur->token = S_ELSE; cur->next_ = 0; last_->next_ = cur; last_ = cur; dev -> else_clause() -> match_proc(this); } cur = new syn_token_t; cur->token = ';'; cur->next_ = 0; last_->next_ = cur; last_ = cur; return 0; } int event_wait(NetEvWait*dev) { syn_token_t*cur; cur = new syn_token_t; cur->token = '@'; cur->evwait = dev; cur->next_ = 0; last_->next_ = cur; last_ = cur; cur = new syn_token_t; cur->token = '('; cur->next_ = 0; last_->next_ = cur; last_ = cur; for (unsigned idx = 0; idx < dev->nevents(); idx += 1) { cur = new syn_token_t; cur->token = S_EVENT; cur->event = dev->event(idx); cur->next_ = 0; last_->next_ = cur; last_ = cur; } cur = new syn_token_t; cur->token = ')'; cur->next_ = 0; last_->next_ = cur; last_ = cur; dev -> statement() -> match_proc(this); cur = new syn_token_t; cur->token = ';'; cur->next_ = 0; last_->next_ = cur; last_ = cur; return 0; } }; static void syn_start_process(NetProcTop*t) { first_ = new syn_token_t; last_ = first_; ptr_ = first_; first_->token = (t->type() == IVL_PR_ALWAYS)? S_ALWAYS : S_INITIAL; first_->top = t; first_->next_ = 0; tokenize go; t -> statement() -> match_proc(&go); } static void syn_done_process() { while (first_) { syn_token_t*cur = first_; first_ = cur->next_; delete cur; } } static int yylex() { if (ptr_ == 0) { yylval = 0; return 0; } yylval = ptr_; ptr_ = ptr_->next_; return yylval->token; } struct syn_rules_f : public functor_t { ~syn_rules_f() { } void process(class Design*, class NetProcTop*top) { /* If the scope that contains this process as a cell attribute attached to it, then skip synthesis. */ if (top->scope()->attribute(perm_string::literal("ivl_synthesis_cell")).len() > 0) return; syn_start_process(top); yyparse(); syn_done_process(); } }; void syn_rules(Design*d) { des_ = d; syn_rules_f obj; des_->functor(&obj); } static void yyerror(const char*) { } iverilog-10_1/sync.cc000066400000000000000000000033471265551621300146340ustar00rootroot00000000000000/* * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "functor.h" # include "netlist.h" # include /* * Most process statements are not roots of synchronous logic. */ bool NetProc::is_synchronous() { return false; } bool NetEvWait::is_synchronous() { for (unsigned idx = 0 ; idx < events_.size() ; idx += 1) { NetEvent*ev = events_[idx]; if (ev->nprobe() == 0) return false; for (unsigned pdx = 0 ; pdx < ev->nprobe() ; pdx += 1) { NetEvProbe*pr = ev->probe(pdx); /* No level sensitive clocks. */ if (pr->edge() == NetEvProbe::ANYEDGE) return false; } } /* So we know that there is a clock source. Check that the input to the storage is asynchronous. */ return true; //statement_->is_asynchronous(); } bool NetProcTop::is_synchronous() { if (type_ == IVL_PR_INITIAL) return false; return statement_->is_synchronous(); } iverilog-10_1/synth.cc000066400000000000000000000100351265551621300150150ustar00rootroot00000000000000/* * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "functor.h" # include "netlist.h" /* * This functor scans the behavioral code, looking for expressions to * synthesize. Although it uses the proc_match_t class, it doesn't * actually match anything, but transforms expressions into structural * netlists. The product of this should be a process where all the * expressions have been reduced to a signal ident, which references * the NetNet of the now synthesized expression. */ class do_expr : public proc_match_t { public: do_expr(Design*d, NetScope*s) : des_(d), scope_(s) { } private: Design*des_; NetScope*scope_; virtual int assign(NetAssign*); virtual int assign_nb(NetAssignNB*); virtual int event_wait(NetEvWait*); virtual int condit(NetCondit*); }; int do_expr::assign(NetAssign*stmt) { if (dynamic_cast(stmt->rval())) return 0; NetNet*tmp = stmt->rval()->synthesize(des_, scope_, stmt->rval()); if (tmp == 0) return 0; NetESignal*tmpe = new NetESignal(tmp); stmt->set_rval(tmpe); return 0; } int do_expr::assign_nb(NetAssignNB*stmt) { if (dynamic_cast(stmt->rval())) return 0; NetNet*tmp = stmt->rval()->synthesize(des_, scope_, stmt->rval()); if (tmp == 0) return 0; NetESignal*tmpe = new NetESignal(tmp); stmt->set_rval(tmpe); return 0; } int do_expr::condit(NetCondit*stmt) { /* synthesize the condition expression, if necessary. */ if (! dynamic_cast(stmt->expr())) { NetNet*tmp = stmt->expr()->synthesize(des_, scope_, stmt->expr()); if (tmp) { NetESignal*tmpe = new NetESignal(tmp); stmt->set_expr(tmpe); } } /* Now recurse through the if and else clauses. */ if (NetProc*tmp = stmt->if_clause()) tmp->match_proc(this); if (NetProc*tmp = stmt->else_clause()) tmp->match_proc(this); return 0; } int do_expr::event_wait(NetEvWait*stmt) { NetProc*tmp = stmt->statement(); if (tmp) return tmp->match_proc(this); else return 0; } class synth_f : public functor_t { public: synth_f() { top_ = NULL; } void process(Design*, NetProcTop*); private: void proc_always_(Design*); void proc_initial_(Design*); void proc_final_(Design*); NetProcTop*top_; }; /* * Look at a process, and divide the problem into always and initial * threads. */ void synth_f::process(Design*des, NetProcTop*top) { top_ = top; switch (top->type()) { case IVL_PR_ALWAYS: proc_always_(des); break; case IVL_PR_INITIAL: proc_initial_(des); break; case IVL_PR_FINAL: proc_final_(des); break; } } void synth_f::proc_always_(Design*des) { do_expr expr_pat(des, top_->scope()); top_->statement()->match_proc(&expr_pat); } void synth_f::proc_initial_(Design*des) { do_expr expr_pat(des, top_->scope()); top_->statement()->match_proc(&expr_pat); } void synth_f::proc_final_(Design*des) { do_expr expr_pat(des, top_->scope()); top_->statement()->match_proc(&expr_pat); } void synth(Design*des) { synth_f synth_obj; des->functor(&synth_obj); } iverilog-10_1/synth2.cc000066400000000000000000002063451265551621300151120ustar00rootroot00000000000000/* * Copyright (c) 2002-2016 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "functor.h" # include "netlist.h" # include "netvector.h" # include "netmisc.h" # include "compiler.h" # include "ivl_assert.h" using namespace std; bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&) { return false; } bool NetProc::synth_sync(Design*des, NetScope*scope, bool& /* ff_negedge */, NetNet* /* ff_clk */, NetBus& /* ff_ce */, NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/, vector& /*ff_aset_value*/, NexusSet&nex_map, NetBus&nex_out, const vector&events) { if (events.size() > 0) { cerr << get_fileline() << ": error: Events are unaccounted" << " for in process synthesis." << endl; des->errors += 1; } if (debug_synth2) { cerr << get_fileline() << ": NetProc::synth_sync: " << "This statement is an async input to a sync process." << endl; } /* Synthesize the input to the DFF. */ NetBus accumulated_nex_out (scope, nex_out.pin_count()); return synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); } /* * Async synthesis of assignments is done by synthesizing the rvalue * expression, then connecting the l-value directly to the output of * the r-value. * * The nex_map is the O-set for the statement, and lists the positions * of the outputs as the caller wants results linked up. The nex_out, * however, is the set of nexa that are to actually get linked to the * r-value. */ bool NetAssignBase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { /* If the lval is a concatenation, synthesise each part separately. */ if (lval_->more ) { /* Temporarily set the lval_ and rval_ fields for each part in turn and recurse. Restore them when done. */ NetAssign_*full_lval = lval_; NetExpr*full_rval = rval_; unsigned offset = 0; bool flag = true; while (lval_) { unsigned width = lval_->lwidth(); NetEConst*base = new NetEConst(verinum(offset)); base->set_line(*this); rval_ = new NetESelect(full_rval->dup_expr(), base, width); rval_->set_line(*this); eval_expr(rval_, width); NetAssign_*more = lval_->more; lval_->more = 0; if (!synth_async(des, scope, nex_map, nex_out, accumulated_nex_out)) flag = false; lval_ = lval_->more = more; offset += width; } lval_ = full_lval; rval_ = full_rval; return flag; } NetNet*rsig = rval_->synthesize(des, scope, rval_); assert(rsig); if (lval_->word() && ! dynamic_cast(lval_->word())) { cerr << get_fileline() << ": sorry: assignment to variable " "location in memory is not currently supported in " "synthesis." << endl; des->errors += 1; return false; } NetNet*lsig = lval_->sig(); if (!lsig) { cerr << get_fileline() << ": error: " << "NetAssignBase::synth_async on unsupported lval "; dump_lval(cerr); cerr << endl; des->errors += 1; return false; } if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " << "l-value signal is " << lsig->vector_width() << " bits, " << "r-value signal is " << rsig->vector_width() << " bits." << endl; cerr << get_fileline() << ": NetAssignBase::synth_async: " << "lval_->lwidth()=" << lval_->lwidth() << endl; cerr << get_fileline() << ": NetAssignBase::synth_async: " << "lsig = " << scope_path(scope) << "." << lsig->name() << endl; if (const NetExpr*base = lval_->get_base()) { cerr << get_fileline() << ": NetAssignBase::synth_async: " << "base_=" << *base << endl; } cerr << get_fileline() << ": NetAssignBase::synth_async: " << "nex_map.size()==" << nex_map.size() << ", nex_out.pin_count()==" << nex_out.pin_count() << endl; } // Here we note if the l-value is actually a bit/part // select. If so, generate a NetPartSelect to perform the select. if ((lval_->lwidth()!=lsig->vector_width()) && !scope->loop_index_tmp.empty()) { // If we are within a NetForLoop, there may be an index // value. That is collected from the scope member // loop_index_tmp, and the evaluate_function method // knows how to apply it. ivl_assert(*this, !scope->loop_index_tmp.empty()); ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); long base_off = 0; // Evaluate the index expression to a constant. const NetExpr*base_expr_raw = lval_->get_base(); ivl_assert(*this, base_expr_raw); NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); if (! eval_as_long(base_off, base_expr)) { ivl_assert(*this, 0); } ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_->lwidth(), NetPartSelect::PV); ps->set_line(*this); des->add_node(ps); connect(ps->pin(0), rsig->pin(0)); rsig = tmp; } else if (lval_->lwidth() != lsig->vector_width()) { // In this case, there is no loop_index_tmp, so we are // not within a NetForLoop. Generate a NetSubstitute // object to handle the bit/part-select in the l-value. ivl_assert(*this, scope->loop_index_tmp.empty()); ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); long base_off = 0; const NetExpr*base_expr_raw = lval_->get_base(); ivl_assert(*this, base_expr_raw); NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); if (! eval_as_long(base_off, base_expr)) { ivl_assert(*this, 0); } ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); ivl_assert(*this, accumulated_nex_out.pin_count()==1); NetNet*use_lsig = lsig; if (accumulated_nex_out.pin(0).is_linked()) { if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " << " Found a use_sig:" << endl; accumulated_nex_out.pin(0).dump_link(cerr, 8); } Nexus*tmp_nex = accumulated_nex_out.pin(0).nexus(); use_lsig = tmp_nex->pick_any_net(); ivl_assert(*this, use_lsig); } else { if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " << " Found no use_sig, resorting to lsig." << endl; } } NetSubstitute*ps = new NetSubstitute(use_lsig, rsig, tmp->vector_width(), base_off); ps->set_line(*this); des->add_node(ps); connect(ps->pin(0), tmp->pin(0)); rsig = tmp; } rsig = crop_to_width(des, rsig, lsig->vector_width()); if (nex_out.pin_count() > 1) { NexusSet tmp_set; nex_output(tmp_set); ivl_assert(*this, tmp_set.size()==1); unsigned ptr = nex_map.find_nexus(tmp_set[0]); ivl_assert(*this, rsig->pin_count()==1); ivl_assert(*this, nex_map.size()==nex_out.pin_count()); ivl_assert(*this, nex_out.pin_count() > ptr); connect(nex_out.pin(ptr), rsig->pin(0)); } else { ivl_assert(*this, nex_out.pin_count()==1); ivl_assert(*this, rsig->pin_count()==1); connect(nex_out.pin(0), rsig->pin(0)); } /* This lval_ represents a reg that is a WIRE in the synthesized results. This function signals the destructor to change the REG that this l-value refers to into a WIRE. It is done then, at the last minute, so that pending synthesis can continue to work with it as a WIRE. */ lval_->turn_sig_to_wire_on_release(); return true; } bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&accumulated_nex_out, NetProc*substmt) { // Create a temporary map of the output only from this statement. NexusSet tmp_map; substmt->nex_output(tmp_map); if (debug_synth2) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " << "tmp_map.size()==" << tmp_map.size() << " for statement at " << substmt->get_fileline() << endl; for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " << "accumulated_nex_out[" << idx << "] dump link" << endl; accumulated_nex_out.pin(idx).dump_link(cerr, 8); } } /* Create also a temporary NetBus to collect the output from the synthesis. */ NetBus tmp_out (scope, tmp_map.size()); // Map (and move) the accumulated_nex_out for this block // to the version that we can pass to the next // statement. We will move the result back later. NetBus accumulated_tmp_out (scope, tmp_map.size()); for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { unsigned ptr = tmp_map.find_nexus(nex_map[idx]); if (ptr >= tmp_map.size()) continue; connect(accumulated_tmp_out.pin(ptr), accumulated_nex_out.pin(idx)); accumulated_nex_out.pin(idx).unlink(); } if (debug_synth2) { for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: nex_map[" << idx << "] dump link, base=" << nex_map[idx].base << ", wid=" << nex_map[idx].wid << endl; nex_map[idx].lnk.dump_link(cerr, 8); } for (unsigned idx = 0 ; idx < tmp_map.size() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_map[" << idx << "] dump link, base=" << tmp_map[idx].base << ", wid=" << tmp_map[idx].wid << endl; tmp_map[idx].lnk.dump_link(cerr, 8); } for (unsigned idx = 0 ; idx < accumulated_tmp_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: accumulated_tmp_out[" << idx << "] dump link" << endl; accumulated_tmp_out.pin(idx).dump_link(cerr, 8); } } bool ok_flag = substmt->synth_async(des, scope, tmp_map, tmp_out, accumulated_tmp_out); if (debug_synth2) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " "substmt->synch_async(...) --> " << (ok_flag? "true" : "false") << " for statement at " << substmt->get_fileline() << "." << endl; } if (ok_flag == false) return false; // Now map the output from the substatement back to the // accumulated_nex_out for this block. Look for the // nex_map pin that is linked to the tmp_map.pin(idx) // pin, and link that to the tmp_out.pin(idx) output link. for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_map[idx]); ivl_assert(*this, ptr < accumulated_nex_out.pin_count()); if (debug_synth2) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " << "tmp_out.pin(" << idx << "):" << endl; tmp_out.pin(idx).dump_link(cerr, 8); } connect(accumulated_nex_out.pin(ptr), tmp_out.pin(idx)); } return true; } /* * Sequential blocks are translated to asynchronous logic by * translating each statement of the block, in order, into gates. The * nex_out for the block is the union of the nex_out for all the * substatements. */ bool NetBlock::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { if (last_ == 0) { return true; } bool flag = true; NetProc*cur = last_; do { cur = cur->next_; bool ok_flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_nex_out, cur); flag = flag && ok_flag; if (ok_flag == false) continue; } while (cur != last_); // The output from the block is now the accumulated outputs. for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) connect(nex_out.pin(idx), accumulated_nex_out.pin(idx)); return flag; } /* * This function is used to fix up a MUX selector to be no longer than * it needs to be. The general idea is that if the selector needs to * be only N bits, but is actually M bits, we translate it to this: * * osig = { |esig[M-1:N-1], esig[N-2:0] } * * This obviously implies that (N >= 2) and (M >= N). In the code * below, N is sel_need, and M is sel_got (= esig->vector_width()). */ static NetNet* mux_selector_reduce_width(Design*des, NetScope*scope, const LineInfo&loc, NetNet*esig, unsigned sel_need) { const unsigned sel_got = esig->vector_width(); ivl_assert(*esig, sel_got >= sel_need); // If the actual width matches the desired width (M==N) then // osig is esig itself. We're done. if (sel_got == sel_need) return esig; if (debug_synth2) { cerr << loc.get_fileline() << ": mux_selector_reduce_width: " << "Reduce selector width=" << sel_got << " to " << sel_need << " bits." << endl; } ivl_assert(*esig, sel_need >= 2); // This is the output signal, osig. ivl_variable_type_t osig_data_type = IVL_VT_LOGIC; netvector_t*osig_vec = new netvector_t(osig_data_type, sel_need-1, 0); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, osig_vec); osig->local_flag(true); osig->set_line(loc); // Create the concat: osig = {...,...} NetConcat*osig_cat = new NetConcat(scope, scope->local_symbol(), sel_need, 2, true); osig_cat->set_line(loc); des->add_node(osig_cat); connect(osig_cat->pin(0), osig->pin(0)); // Create the part select esig[N-2:0]... NetPartSelect*ps0 = new NetPartSelect(esig, 0, sel_need-1, NetPartSelect::VP); ps0->set_line(loc); des->add_node(ps0); connect(ps0->pin(1), esig->pin(0)); netvector_t*ps0_vec = new netvector_t(osig_data_type, sel_need-2, 0); NetNet*ps0_sig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, ps0_vec); ps0_sig->local_flag(true); ps0_sig->set_line(loc); connect(ps0_sig->pin(0), ps0->pin(0)); // osig = {..., esig[N-2:0]} connect(osig_cat->pin(1), ps0_sig->pin(0)); // Create the part select esig[M-1:N-1] NetPartSelect*ps1 = new NetPartSelect(esig, sel_need-1, sel_got-sel_need, NetPartSelect::VP); ps1->set_line(loc); des->add_node(ps1); connect(ps1->pin(1), esig->pin(0)); netvector_t*ps1_vec = new netvector_t(osig_data_type, sel_got-sel_need-1, 0); NetNet*ps1_sig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, ps1_vec); ps1_sig->local_flag(true); ps1_sig->set_line(loc); connect(ps1_sig->pin(0), ps1->pin(0)); // Create the reduction OR: | esig[M-1:N-1] NetUReduce*ered = new NetUReduce(scope, scope->local_symbol(), NetUReduce::OR, sel_got-sel_need); ered->set_line(loc); des->add_node(ered); connect(ered->pin(1), ps1_sig->pin(0)); NetNet*ered_sig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, &netvector_t::scalar_logic); ered_sig->local_flag(true); ered_sig->set_line(loc); connect(ered->pin(0), ered_sig->pin(0)); // osig = { |esig[M-1:N-1], esig[N-2:0] } connect(osig_cat->pin(2), ered_sig->pin(0)); return osig; } bool NetCase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { if (type()==NetCase::EQZ || type()==NetCase::EQX) return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out); // Special case: If the case expression is constant, then this // is a pattern where the guards are non-constant and tested // against a constant case. Handle this as chained conditions // instead. if (dynamic_cast (expr_)) return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out); if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " << "Selector expression: " << *expr_ << endl; } /* Synthesize the select expression. */ NetNet*esig = expr_->synthesize(des, scope, expr_); unsigned sel_width = esig->vector_width(); ivl_assert(*this, sel_width > 0); if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " << "selector width (sel_width) = " << sel_width << endl; } ivl_assert(*this, nex_map.size() == nex_out.pin_count()); vector mux_width (nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { mux_width[idx] = nex_map[idx].wid; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " << "idx=" << idx << ", mux_width[idx]=" << mux_width[idx] << endl; } } // The accumulated_nex_out is taken as the input for this // statement. Since there are collection of statements that // start at this same point, we save all these inputs and // reuse them for each statement. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); } /* Collect all the statements into a map of index to statement. The guard expression it evaluated to be the index of the mux value, and the statement is bound to that index. */ unsigned long max_guard_value = 0; mapstatement_map; NetProc*statement_default = 0; for (size_t item = 0 ; item < items_.size() ; item += 1) { if (items_[item].guard == 0) { statement_default = items_[item].statement; continue; } NetEConst*ge = dynamic_cast(items_[item].guard); if (ge == 0) { cerr << items_[item].guard->get_fileline() << ": sorry: " << "variable case item expressions with a variable " << "case select expression are not supported in " << "synthesis. " << endl; des->errors += 1; return false; } ivl_assert(*this, ge); verinum gval = ge->value(); unsigned long sel_idx = gval.as_ulong(); if (statement_map[sel_idx]) { cerr << ge->get_fileline() << ": warning: duplicate case " << "value '" << sel_idx << "' detected. This case is " << "unreachable." << endl; delete items_[item].statement; items_[item].statement = 0; continue; } if (sel_idx > max_guard_value) max_guard_value = sel_idx; ivl_assert(*this, items_[item].statement); statement_map[sel_idx] = items_[item].statement; } // The minimum selector width is the number of inputs that // are selected, rounded up to the nearest power of 2. unsigned sel_need = ceil(log2(max_guard_value + 1)); // If the sel_width can select more than just the explicit // guard values, and there is a default statement, then adjust // the sel_need to allow for the implicit selections. if (statement_default && (sel_width > sel_need)) sel_need += 1; // The mux size is always an exact power of 2. if (sel_need >= 8*sizeof(unsigned)) { cerr << get_fileline() << ": sorry: mux select width of " << sel_need << " bits is too large for synthesis." << endl; des->errors += 1; return false; } unsigned mux_size = 1U << sel_need; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " << "Adjusted mux_size is " << mux_size << " (max_guard_value=" << max_guard_value << ", sel_need=" << sel_need << ", sel_width=" << sel_width << ")." << endl; } if (sel_width > sel_need) { if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " << "Selector is " << sel_width << " bits, " << "need only " << sel_need << " bits." << endl; } esig = mux_selector_reduce_width(des, scope, *this, esig, sel_need); } if (!statement_default && (statement_map.size() != ((size_t)1 << sel_width))) { cerr << get_fileline() << ": sorry: Latch inferred from " << "incomplete case statement. This is not supported " << "in synthesis." << endl; des->errors += 1; return false; } /* If there is a default clause, synthesize it once and we'll link it in wherever it is needed. */ NetBus default_bus (scope, nex_map.size()); if (statement_default) { bool flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_nex_out, statement_default); if (!flag) { return false; } for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) { connect(default_bus.pin(idx), accumulated_nex_out.pin(idx)); accumulated_nex_out.pin(idx).unlink(); } if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " << "synthesize default clause at " << statement_default->get_fileline() << " is done." << endl; } } vector mux (mux_width.size()); for (size_t mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { mux[mdx] = new NetMux(scope, scope->local_symbol(), mux_width[mdx], mux_size, sel_need); des->add_node(mux[mdx]); // The select signal is already synthesized, and is // common for every mux of this case statement. Simply // hook it up. connect(mux[mdx]->pin_Sel(), esig->pin(0)); // The outputs are in the nex_out, and connected to the // mux Result pins. connect(mux[mdx]->pin_Result(), nex_out.pin(mdx)); // Make sure the output is now connected to a net. If // not, then create a fake one to carry the net-ness of // the pin. if (mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) { ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::TRI, tmp_vec); tmp->local_flag(true); ivl_assert(*this, tmp->vector_width() != 0); connect(mux[mdx]->pin_Result(), tmp->pin(0)); } } for (unsigned idx = 0 ; idx < mux_size ; idx += 1) { NetProc*stmt = statement_map[idx]; if (stmt==0 && statement_default) { ivl_assert(*this, default_bus.pin_count() == mux.size()); for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) connect(mux[mdx]->pin_Data(idx), default_bus.pin(mdx)); continue; } ivl_assert(*this, stmt); NetBus accumulated_tmp (scope, nex_map.size()); for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1) connect(accumulated_tmp.pin(pin), statement_input.pin(pin)); synth_async_block_substatement_(des, scope, nex_map, accumulated_tmp, stmt); for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) { connect(mux[mdx]->pin_Data(idx), accumulated_tmp.pin(mdx)); if (mux[mdx]->pin_Data(idx).nexus()->pick_any_net()==0) { cerr << get_fileline() << ": warning: case " << idx << " has no input for mux slice " << mdx << "." << endl; ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); NetNet*tmpn = new NetNet(scope, scope->local_symbol(), NetNet::TRI, tmp_vec); tmpn->local_flag(true); ivl_assert(*this, tmpn->vector_width() != 0); connect(mux[mdx]->pin_Data(idx), tmpn->pin(0)); } ivl_assert(*this, mux[mdx]->pin_Data(idx).nexus()->pick_any_net()); } } return true; } /* * casez statements are hard to implement as a single wide mux because * the test doesn't really map to a select input. Instead, implement * it as a chain of binary muxes. This gives the synthesizer my * flexibility, and is more typically what is desired from a casez anyhow. */ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { /* Synthesize the select expression. */ NetNet*esig = expr_->synthesize(des, scope, expr_); unsigned sel_width = esig->vector_width(); ivl_assert(*this, sel_width > 0); ivl_assert(*this, nex_map.size() == nex_out.pin_count()); if (debug_synth2) { for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetCase::synth_async_casez_: " << "nex_out.pin(" << idx << "):" << endl; nex_out.pin(idx).dump_link(cerr, 8); } for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetCase::synth_async_casez_: " << "accumulated_nex_out.pin(" << idx << "):" << endl; accumulated_nex_out.pin(idx).dump_link(cerr, 8); } } vectormux_width (nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { mux_width[idx] = nex_map[idx].wid; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async_casez_: " << "idx=" << idx << ", mux_width[idx]=" << mux_width[idx] << endl; } } // The accumulated_nex_out is taken as the input for this // statement. Since there are collection of statements that // start at this same point, we save all these inputs and // reuse them for each statement. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); } // Look for a default statement. NetProc*statement_default = 0; for (size_t item = 0 ; item < items_.size() ; item += 1) { if (items_[item].guard != 0) continue; ivl_assert(*this, statement_default==0); statement_default = items_[item].statement; } NetBus default_bus (scope, nex_out.pin_count()); if (statement_default) { bool flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_nex_out, statement_default); if (!flag) { return false; } for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) { connect(default_bus.pin(idx), accumulated_nex_out.pin(idx)); accumulated_nex_out.pin(idx).unlink(); } if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async_casez_: " << "synthesize default clause at " << statement_default->get_fileline() << " is done." << endl; } } netvector_t*condit_type = new netvector_t(IVL_VT_LOGIC, 0, 0); NetCaseCmp::kind_t case_kind = NetCaseCmp::EEQ; switch (type()) { case NetCase::EQ: case_kind = NetCaseCmp::EEQ; break; case NetCase::EQX: case_kind = NetCaseCmp::XEQ; break; case NetCase::EQZ: case_kind = NetCaseCmp::ZEQ; break; default: assert(0); } // Process the items from last to first. We generate a // true/false mux, with the select being the comparison of // the case select with the guard expression. The true input // (data1) is the current statement, and the false input is // the result of a later statement. vectormux_prev (mux_width.size()); for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { size_t item = items_.size()-idx-1; if (items_[item].guard == 0) continue; NetProc*stmt = items_[item].statement; ivl_assert(*this, stmt); NetExpr*guard_expr = items_[item].guard; NetNet*guard = guard_expr->synthesize(des, scope, guard_expr); NetCaseCmp*condit_dev = new NetCaseCmp(scope, scope->local_symbol(), sel_width, case_kind); des->add_node(condit_dev); condit_dev->set_line(*this); // Note that the expression that may have wildcards must // go in the pin(2) input. This is the definition of the // NetCaseCmp statement. connect(condit_dev->pin(1), esig->pin(0)); connect(condit_dev->pin(2), guard->pin(0)); NetNet*condit = new NetNet(scope, scope->local_symbol(), NetNet::TRI, condit_type); condit->set_line(*this); condit->local_flag(true); connect(condit_dev->pin(0), condit->pin(0)); // Synthesize the guarded statement. NetBus true_bus (scope, nex_out.pin_count()); for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1) connect(true_bus.pin(pin), statement_input.pin(pin)); synth_async_block_substatement_(des, scope, nex_map, true_bus, stmt); for (unsigned mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { NetMux*mux_cur = new NetMux(scope, scope->local_symbol(), mux_width[mdx], 2, 1); des->add_node(mux_cur); mux_cur->set_line(*this); connect(mux_cur->pin_Sel(), condit->pin(0)); connect(mux_cur->pin_Data(1), true_bus.pin(mdx)); // If there is a previous mux, then use that as the // false clause input. Otherwise, use the // default. But wait, if there is no default, then // use the accumulated input. if (mux_prev[mdx]) { connect(mux_cur->pin_Data(0), mux_prev[mdx]->pin_Result()); } else if (default_bus.pin(mdx).is_linked()) { connect(mux_cur->pin_Data(0), default_bus.pin(mdx)); } else { connect(mux_cur->pin_Data(0), statement_input.pin(mdx)); } // Make a NetNet for the result. ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::TRI, tmp_vec); tmp->local_flag(true); tmp->set_line(*this); ivl_assert(*this, tmp->vector_width() != 0); connect(mux_cur->pin_Result(), tmp->pin(0)); // This mux becomes the "false" input to the next mux. mux_prev[mdx] = mux_cur; } } // Connect the last mux to the output. for (size_t mdx = 0 ; mdx < mux_prev.size() ; mdx += 1) { connect(mux_prev[mdx]->pin_Result(), nex_out.pin(mdx)); } return true; } /* * A condit statement (if (cond) ... else ... ;) infers an A-B mux, * with the cond expression acting as a select input. If the cond * expression is true, the if_ clause is selected, and if false, the * else_ clause is selected. */ bool NetCondit::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { if (if_ == 0) { return false; } if (else_ == 0) { bool latch_flag = false; for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { if (! accumulated_nex_out.pin(idx).is_linked()) latch_flag = true; } if (latch_flag) { cerr << get_fileline() << ": error: Asynchronous if statement" << " cannot synthesize missing \"else\"" << " without generating latches." << endl; //return false; } } ivl_assert(*this, if_ != 0); // Synthesize the condition. This will act as a select signal // for a binary mux. NetNet*ssig = expr_->synthesize(des, scope, expr_); assert(ssig); if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Synthesize if clause at " << if_->get_fileline() << endl; for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { cerr << get_fileline() << ": NetCondit::synth_async: " << "accumulated_nex_out.pin(" << idx << "):" << endl; accumulated_nex_out.pin(idx).dump_link(cerr, 8); } } NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "statement_input.pin(" << idx << "):" << endl; statement_input.pin(idx).dump_link(cerr, 8); } } bool flag; NetBus asig(scope, nex_out.pin_count()); flag = if_->synth_async(des, scope, nex_map, asig, accumulated_nex_out); if (!flag) { return false; } NetBus btmp(scope, nex_out.pin_count()); NetBus bsig(scope, nex_out.pin_count()); if (else_==0) { for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { connect(bsig.pin(idx), accumulated_nex_out.pin(idx)); accumulated_nex_out.pin(idx).unlink(); } } else { if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Synthesize else clause at " << else_->get_fileline() << endl; for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { cerr << get_fileline() << ": NetCondit::synth_async: " << "accumulated_nex_out.pin(" << idx << "):" << endl; accumulated_nex_out.pin(idx).dump_link(cerr, 8); } for (unsigned idx = 0 ; idx < statement_input.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetCondit::synth_async: " << "statement_input.pin(" << idx << "):" << endl; statement_input.pin(idx).dump_link(cerr, 8); } } NetBus accumulated_btmp_out (scope, statement_input.pin_count()); for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { if (statement_input.pin(idx).is_linked()) connect(accumulated_btmp_out.pin(idx), statement_input.pin(idx)); else connect(accumulated_btmp_out.pin(idx), accumulated_nex_out.pin(idx)); } if (debug_synth2) { for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetCondit::synth_async: " << "accumulated_btmp_out.pin(" << idx << "):" << endl; accumulated_btmp_out.pin(idx).dump_link(cerr, 8); } } flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_btmp_out, else_); if (!flag) { return false; } if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "synthesize else clause at " << else_->get_fileline() << " is done." << endl; for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetCondit::synth_async: " << "accumulated_btmp_out.pin(" << idx << "):" << endl; accumulated_btmp_out.pin(idx).dump_link(cerr, 8); } } for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { connect(bsig.pin(idx), accumulated_btmp_out.pin(idx)); accumulated_btmp_out.pin(idx).unlink(); } } /* The nex_out output, asig input, and bsig input all have the same pin count (usually, but not always 1) because they are net arrays of the same dimension. The for loop below creates a NetMux for each pin of the output. (Note that pins may be, in fact usually are, vectors.) */ ivl_assert(*this, nex_out.pin_count()==asig.pin_count()); ivl_assert(*this, nex_out.pin_count()==bsig.pin_count()); bool rc_flag = true; for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { // It should not be possible for the a (true) or b // (false) signals to be missing. If either is, print a // warning and clear a flag so that the rest of this // code can find a way to cope. bool asig_is_present = true; if (! asig.pin(idx).nexus()->pick_any_net()) { cerr << get_fileline() << ": warning: " << "True clause of conditional statement might not" << " drive all expected outputs." << endl; asig_is_present = false; } bool bsig_is_present = true; if (! bsig.pin(idx).nexus()->pick_any_net()) { cerr << get_fileline() << ": warning: " << "False clause of conditional statement might not" << " drive all expected outputs." << endl; bsig_is_present = false; } // Guess the mux type from the type of the output. ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; if (NetNet*tmp = nex_out.pin(idx).nexus()->pick_any_net()) { mux_data_type = tmp->data_type(); } unsigned mux_off = 0; unsigned mux_width; if (asig_is_present) mux_width = asig.pin(idx).nexus()->vector_width(); else if (bsig_is_present) mux_width = bsig.pin(idx).nexus()->vector_width(); else mux_width = 0; if (debug_synth2) { if (asig_is_present) cerr << get_fileline() << ": NetCondit::synth_async: " << "asig_is_present," << " asig width=" << asig.pin(idx).nexus()->vector_width() << endl; if (bsig_is_present) cerr << get_fileline() << ": NetCondit::synth_async: " << "bsig_is_present," << " bsig width=" << bsig.pin(idx).nexus()->vector_width() << endl; cerr << get_fileline() << ": NetCondit::synth_async: " << "Calculated mux_width=" << mux_width << endl; } NetPartSelect*apv = detect_partselect_lval(asig.pin(idx)); if (debug_synth2 && apv) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Assign-to-part apv base=" << apv->base() << ", width=" << apv->width() << endl; } NetPartSelect*bpv = detect_partselect_lval(bsig.pin(idx)); if (debug_synth2 && bpv) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Assign-to-part bpv base=" << bpv->base() << ", width=" << bpv->width() << endl; } unsigned mux_lwidth = mux_width; ivl_assert(*this, mux_width != 0); if (apv && bpv && apv->width()==bpv->width() && apv->base()==bpv->base()) { // The a and b sides are both assigning to the // same bits of the output, so we can use that to // create a much narrower mux that only // manipulates the width of the part. mux_width = apv->width(); mux_off = apv->base(); asig.pin(idx).unlink(); bsig.pin(idx).unlink(); connect(asig.pin(idx), apv->pin(0)); connect(bsig.pin(idx), bpv->pin(0)); delete apv; delete bpv; } else { // The part selects are of no use. Forget them. apv = 0; bpv = 0; } if (bsig_is_present && mux_width != bsig.pin(idx).nexus()->vector_width()) { cerr << get_fileline() << ": internal error: " << "NetCondit::synth_async: " << "Mux input sizes do not match." << " A size=" << mux_lwidth << ", B size=" << bsig.pin(idx).nexus()->vector_width() << endl; cerr << get_fileline() << ": : " << "asig node pins:" << endl; asig.dump_node_pins(cerr, 8); cerr << get_fileline() << ": : " << "if_ statement:" << endl; if_->dump(cerr, 8); cerr << get_fileline() << ": : " << "bsig node pins:" << endl; bsig.dump_node_pins(cerr, 4); if (else_) { cerr << get_fileline() << ": : " << "else_ statement:" << endl; else_->dump(cerr, 8); } rc_flag = false; } NetMux*mux = new NetMux(scope, scope->local_symbol(), mux_width, 2, 1); mux->set_line(*this); netvector_t*tmp_type = 0; if (mux_width==1) tmp_type = new netvector_t(mux_data_type); else tmp_type = new netvector_t(mux_data_type, mux_width-1,0); // Bind some temporary signals to carry pin type. NetNet*otmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); otmp->local_flag(true); otmp->set_line(*this); connect(mux->pin_Result(),otmp->pin(0)); connect(mux->pin_Sel(), ssig->pin(0)); connect(mux->pin_Data(1), asig.pin(idx)); connect(mux->pin_Data(0), bsig.pin(idx)); if (! asig_is_present) { tmp_type = new netvector_t(mux_data_type, mux_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); connect(mux->pin_Data(1), tmp->pin(0)); } if (! bsig_is_present) { tmp_type = new netvector_t(mux_data_type, mux_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); connect(mux->pin_Data(0), tmp->pin(0)); } // We are only muxing a part of the output vector, so // make a NetPartSelect::PV to widen the vector to the // output at hand. if (mux_width < mux_lwidth) { tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); NetPartSelect*ps = new NetPartSelect(tmp, mux_off, mux_width, NetPartSelect::PV); des->add_node(ps); connect(ps->pin(0), otmp->pin(0)); otmp = tmp; } connect(nex_out.pin(idx), otmp->pin(0)); // Handle the special case that this NetMux is only // assigning to a part of the vector. If that is the // case, then we need to blend this output with the // already calculated input to this statement so that we // don't accidentally disconnect the other drivers to // other bits. // FIXME: NEED TO CHECK THAT THESE DRIVERS DON'T // OVERLAP. THIS CODE CURRENTLY DOESN'T DO THAT TEST. if (mux_width < mux_lwidth && if_ && else_) { if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "This NetMux only impacts a few bits of output," << " so combine nex_out with statement input." << endl; cerr << get_fileline() << ": NetCondit::synth_async: " << "MISSING TEST FOR CORRECTNESS OF THE BLEND!" << endl; } vectormask = statement_input.pin(idx).nexus()->driven_mask(); // If the mask is empty then there are no bits in the // nexus to check yet. if (! mask.empty()) { for (size_t bit = mux_off; bit < mux_off+mux_width; bit += 1) { ivl_assert(*this, mask[bit]==false); } } connect(nex_out.pin(idx), statement_input.pin(idx)); } des->add_node(mux); } return rc_flag; } bool NetEvWait::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { bool flag = statement_->synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); return flag; } bool NetForLoop::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, NetBus&accumulated_nex_out) { if (debug_synth2) { cerr << get_fileline() << ": NetForLoop::synth_async: " << "Index variable is " << index_->name() << endl; cerr << get_fileline() << ": NetForLoop::synth_async: " << "Initialization expression: " << *init_expr_ << endl; } // Get the step assignment statement and break it into the // l-value (should be the index) and the r-value, which is the // step expressions. NetAssign*step_assign = dynamic_cast (step_statement_); char assign_operator = step_assign->assign_operator(); ivl_assert(*this, step_assign); NetExpr*step_expr = step_assign->rval(); // Tell the scope that this index value is like a genvar. LocalVar index_var; index_var.nwords = 0; map index_args; // Calculate the initial value for the index. index_var.value = init_expr_->evaluate_function(*this, index_args); ivl_assert(*this, index_var.value); index_args[index_->name()] = index_var; for (;;) { // Evaluate the condition expression. If it is false, // then we are going to break out of this synthesis loop. NetExpr*tmp = condition_->evaluate_function(*this, index_args); ivl_assert(*this, tmp); long cond_value; bool rc = eval_as_long(cond_value, tmp); ivl_assert(*this, rc); delete tmp; if (!cond_value) break; scope->genvar_tmp = index_->name(); rc = eval_as_long(scope->genvar_tmp_val, index_var.value); ivl_assert(*this, rc); if (debug_synth2) { cerr << get_fileline() << ": NetForLoop::synth_async: " << "Synthesis iteration with " << index_->name() << "=" << *index_var.value << endl; } // Synthesize the iterated expression. Stash the loop // index value so that the substatements can see this // value and use it during its own synthesis. ivl_assert(*this, scope->loop_index_tmp.empty()); scope->loop_index_tmp = index_args; rc = synth_async_block_substatement_(des, scope, nex_map, accumulated_nex_out, statement_); scope->loop_index_tmp.clear(); // Evaluate the step_expr to generate the next index value. tmp = step_expr->evaluate_function(*this, index_args); ivl_assert(*this, tmp); // If there is an assign_operator, then replace the // index_var.value with (value tmp) and evaluate // that to get the next value. "value" is the existing // value, and "tmp" is the step value. We are replacing // (value += tmp) with (value = value + tmp) and // evaluating it. switch (assign_operator) { case 0: break; case '+': case '-': index_var.value = new NetEBAdd(assign_operator, tmp, index_var.value, 32, true); tmp = index_var.value->evaluate_function(*this, index_args); break; default: cerr << get_fileline() << ": internal error: " << "NetForLoop::synth_async: What to do with assign_operator=" << assign_operator << endl; ivl_assert(*this, 0); } delete index_var.value; index_var.value = tmp; index_args[index_->name()] = index_var; } delete index_var.value; // The output from the block is now the accumulated outputs. for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) connect(nex_out.pin(idx), accumulated_nex_out.pin(idx)); return true; } /* * This method is called when the process is shown to be * asynchronous. Figure out the nexus set of outputs from this * process, and pass that to the synth_async method for the statement * of the process. The statement will connect its output to the * nex_out set, using the nex_map as a guide. Starting from the top, * the nex_map is the same as the nex_map. */ bool NetProcTop::synth_async(Design*des) { NexusSet nex_set; statement_->nex_output(nex_set); if (debug_synth2) { cerr << get_fileline() << ": NetProcTop::synth_async: " << "Process has " << nex_set.size() << " outputs." << endl; } NetBus nex_q (scope(), nex_set.size()); for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { NexusSet::elem_t&item = nex_set[idx]; if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) { ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC; netvector_t*tmp_type = new netvector_t(tmp_data_type, item.lnk.nexus()->vector_width()-1,0); NetNet*tmp_sig = new NetNet(scope(), scope()->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp_sig->local_flag(true); tmp_sig->set_line(*this); NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base, item.wid, NetPartSelect::PV); des->add_node(tmp); tmp->set_line(*this); connect(tmp->pin(0), nex_q.pin(idx)); connect(item.lnk, tmp_sig->pin(0)); } else { connect(item.lnk, nex_q.pin(idx)); } } NetBus tmp_q (scope(), nex_set.size()); bool flag = statement_->synth_async(des, scope(), nex_set, nex_q, tmp_q); return flag; } /* * This method is called when a block is encountered near the surface * of a synchronous always statement. For example, this code will be * invoked for input like this: * * always @(posedge clk...) begin * * * ... * end * * This needs to be split into a DFF bank for each statement, because * the statements may each infer different reset and enable signals. */ bool NetBlock::synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const vector&events_in) { if (debug_synth2) { cerr << get_fileline() << ": NetBlock::synth_sync: " << "Examine this block for synchronous logic." << endl; } bool flag = true; NetProc*cur = last_; do { cur = cur->next_; /* Create a temporary nex_map for the substatement. */ NexusSet tmp_set; cur->nex_output(tmp_set); /* Create also a temporary net_out to collect the output. The tmp1 and tmp2 map and out sets together are used to collect the outputs from the substatement for the inputs of the FF bank. */ NetBus tmp_out (scope, tmp_set.size()); for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_set[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); if (nex_out.pin(ptr).is_linked()) { cerr << get_fileline() << ": sorry: multiple statements " "assigning to the same flip-flop are not yet " "supported in synthesis." << endl; return false; } } /* Create a temporary ff_ce (FF clock-enable) that accounts for the subset of outputs that this substatement drives. This allows for the possibility that the substatement has CE patterns of its own. */ NetBus tmp_ce (scope, tmp_set.size()); for (unsigned idx = 0 ; idx < tmp_ce.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_set[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); if (ff_ce.pin(ptr).is_linked()) { connect(tmp_ce.pin(idx), ff_ce.pin(ptr)); ff_ce.pin(ptr).unlink(); } } /* Now go on with the synchronous synthesis for this subset of the statement. The tmp_map is the output nexa that we expect, and the tmp_out is where we want those outputs connected. */ bool ok_flag = cur->synth_sync(des, scope, ff_negedge, ff_clk, tmp_ce, ff_aclr, ff_aset, ff_aset_value, tmp_set, tmp_out, events_in); flag = flag && ok_flag; if (ok_flag == false) continue; /* Use the nex_map to link up the output from the substatement to the output of the block as a whole. It is occasionally possible to have outputs beyond the input set, for example when the l-value of an assignment is smaller than the r-value. */ for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_set[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); connect(nex_out.pin(ptr), tmp_out.pin(idx)); connect(ff_ce.pin(ptr), tmp_ce.pin(idx)); } } while (cur != last_); if (debug_synth2) { cerr << get_fileline() << ": NetBlock::synth_sync: " << "Done Examining this block for synchronous logic." << endl; } return flag; } /* * This method handles the case where I find a conditional near the * surface of a synchronous thread. This conditional can be a CE or an * asynchronous set/reset, depending on whether the pin of the * expression is connected to an event, or not. */ bool NetCondit::synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const vector&events_in) { /* First try to turn the condition expression into an asynchronous set/reset. If the condition expression has inputs that are included in the sensitivity list, then it is likely intended as an asynchronous input. */ NexusSet*expr_input = expr_->nex_input(); assert(expr_input); for (unsigned idx = 0 ; idx < events_in.size() ; idx += 1) { NetEvProbe*ev = events_in[idx]; NexusSet pin_set; pin_set.add(ev->pin(0).nexus(), 0, 0); if (! expr_input->contains(pin_set)) continue; // Synthesize the set/reset input expression. NetNet*rst = expr_->synthesize(des, scope, expr_); ivl_assert(*this, rst->pin_count() == 1); /* XXXX I really should find a way to check that the edge used on the reset input is correct. This would involve interpreting the expression that is fed by the reset expression. */ ivl_assert(*this, ev->edge() == NetEvProbe::POSEDGE); // Synthesize the true clause to figure out what kind of // set/reset we have. This should synthesize down to a // constant. If not, we have an asynchronous LOAD, a // very different beast. ivl_assert(*this, if_); bool flag; NetBus tmp_out(scope, nex_out.pin_count()); NetBus accumulated_tmp_out(scope, nex_out.pin_count()); flag = if_->synth_async(des, scope, nex_map, tmp_out, accumulated_tmp_out); if (! flag) return false; ivl_assert(*this, tmp_out.pin_count() == ff_aclr.pin_count()); ivl_assert(*this, tmp_out.pin_count() == ff_aset.pin_count()); for (unsigned pin = 0 ; pin < tmp_out.pin_count() ; pin += 1) { Nexus*rst_nex = tmp_out.pin(pin).nexus(); if (! rst_nex->drivers_constant()) { cerr << get_fileline() << ": sorry: " << "Asynchronous LOAD not implemented." << endl; return false; } verinum rst_drv = rst_nex->driven_vector(); verinum zero (verinum::V0, rst_drv.len()); verinum ones (verinum::V1, rst_drv.len()); if (rst_drv==zero) { // Don't yet support multiple asynchronous reset inputs. ivl_assert(*this, ! ff_aclr.pin(pin).is_linked()); ivl_assert(*this, rst->pin_count()==1); connect(ff_aclr.pin(pin), rst->pin(0)); } else { // Don't yet support multiple asynchronous set inputs. ivl_assert(*this, ! ff_aset.pin(pin).is_linked()); ivl_assert(*this, rst->pin_count()==1); connect(ff_aset.pin(pin), rst->pin(0)); if (rst_drv!=ones) ff_aset_value[pin] = rst_drv; } } return else_->synth_sync(des, scope, ff_negedge, ff_clk, ff_ce, ff_aclr, ff_aset, ff_aset_value, nex_map, nex_out, vector(0)); } delete expr_input; #if 0 /* Detect the case that this is a *synchronous* set/reset. It is not asynchronous because we know the condition is not included in the sensitivity list, but if the if_ case is constant (has no inputs) then we can model this as a synchronous set/reset. This is only synchronous set/reset if there is a true and a false clause, and no inputs. The "no inputs" requirement is met if the assignments are of all constant values. */ assert(if_ != 0); NexusSet*a_set = if_->nex_input(); if ((a_set->count() == 0) && if_ && else_) { NetNet*rst = expr_->synthesize(des); assert(rst->pin_count() == 1); /* Synthesize the true clause to figure out what kind of set/reset we have. */ NetNet*asig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, nex_map->pin_count()); asig->local_flag(true); bool flag = if_->synth_async(des, scope, nex_map, asig); if (!flag) { /* This path leads nowhere */ delete asig; } else { assert(asig->pin_count() == ff->width()); /* Collect the set/reset value into a verinum. If this turns out to be entirely 0 values, then use the Sclr input. Otherwise, use the Aset input and save the set value. */ verinum tmp (verinum::V0, ff->width()); for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) { assert(asig->pin(bit).nexus()->drivers_constant()); tmp.set(bit, asig->pin(bit).nexus()->driven_value()); } assert(tmp.is_defined()); if (tmp.is_zero()) { connect(ff->pin_Sclr(), rst->pin(0)); } else { connect(ff->pin_Sset(), rst->pin(0)); ff->sset_value(tmp); } delete a_set; assert(else_ != 0); flag = else_->synth_sync(des, scope, ff, nex_map, nex_out, svector(0)) && flag; DEBUG_SYNTH2_EXIT("NetCondit",flag) return flag; } } delete a_set; #endif /* Failed to find an asynchronous set/reset, so any events input are probably in error. */ if (events_in.size() > 0) { cerr << get_fileline() << ": error: Events are unaccounted" << " for in process synthesis." << endl; des->errors += 1; } /* If this is an if/then/else, then it is likely a combinational if, and I should synthesize it that way. */ if (if_ && else_) { NetBus tmp (scope, nex_out.pin_count()); bool flag = synth_async(des, scope, nex_map, nex_out, tmp); return flag; } ivl_assert(*this, if_); ivl_assert(*this, !else_); /* Synthesize the enable expression. */ NetNet*ce = expr_->synthesize(des, scope, expr_); ivl_assert(*this, ce && ce->pin_count()==1 && ce->vector_width()==1); if (debug_synth2) { NexusSet if_set; if_->nex_output(if_set); cerr << get_fileline() << ": NetCondit::synth_sync: " << "Found ce pattern." << " ff_ce.pin_count()=" << ff_ce.pin_count() << endl; for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { cerr << get_fileline() << ": NetCondit::synth_sync: " << "nex_map[" << idx << "]: " << "base=" << nex_map[idx].base << ", wid=" << nex_map[idx].wid << endl; nex_map[idx].lnk.dump_link(cerr, 8); } for (unsigned idx = 0 ; idx < if_set.size() ; idx += 1) { cerr << get_fileline() << ": NetCondit::synth_sync: " << "if_set[" << idx << "]: " << "base=" << if_set[idx].base << ", wid=" << if_set[idx].wid << endl; if_set[idx].lnk.dump_link(cerr, 8); } } /* What's left, is a synchronous CE statement like this: if (expr_) ; The expr_ expression has already been synthesized to the ce net, so we connect it here to the FF. What's left is to synthesize the substatement as a combinational statement. Watch out for the special case that there is already a CE connected to this FF. This can be caused by code like this: if (a) if (b) ; In this case, we are working on the inner IF, so we AND the a and b expressions to make a new CE. */ for (unsigned idx = 0 ; idx < ff_ce.pin_count() ; idx += 1) { if (ff_ce.pin(idx).is_linked()) { NetLogic*ce_and = new NetLogic(scope, scope->local_symbol(), 3, NetLogic::AND, 1); des->add_node(ce_and); connect(ff_ce.pin(idx), ce_and->pin(1)); connect(ce->pin(0), ce_and->pin(2)); ff_ce.pin(idx).unlink(); connect(ff_ce.pin(idx), ce_and->pin(0)); } else { connect(ff_ce.pin(idx), ce->pin(0)); } } bool flag = if_->synth_sync(des, scope, ff_negedge, ff_clk, ff_ce, ff_aclr, ff_aset, ff_aset_value, nex_map, nex_out, events_in); return flag; } bool NetEvWait::synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const vector&events_in) { if (debug_synth2) { cerr << get_fileline() << ": NetEvWait::synth_sync: " << "Synchronous process an event statement." << endl; } if (events_in.size() > 0) { cerr << get_fileline() << ": error: Events are unaccounted" << " for in process synthesis." << endl; des->errors += 1; } assert(events_in.size() == 0); /* This can't be other than one unless there are named events, which I cannot synthesize. */ ivl_assert(*this, events_.size() == 1); NetEvent*ev = events_[0]; assert(ev->nprobe() >= 1); vectorevents (ev->nprobe() - 1); /* Get the input set from the substatement. This will be used to figure out which of the probes is the clock. */ NexusSet*statement_input = statement_ -> nex_input(); /* Search for a clock input. The clock input is the edge event that is not also an input to the substatement. */ NetEvProbe*pclk = 0; unsigned event_idx = 0; for (unsigned idx = 0 ; idx < ev->nprobe() ; idx += 1) { NetEvProbe*tmp = ev->probe(idx); assert(tmp->pin_count() == 1); NexusSet tmp_nex; tmp_nex .add( tmp->pin(0).nexus(), 0, 0 ); if (! statement_input ->contains(tmp_nex)) { if (pclk != 0) { cerr << get_fileline() << ": error: Too many " << "clocks for synchronous logic." << endl; cerr << get_fileline() << ": : Perhaps an" << " asynchronous set/reset is misused?" << endl; des->errors += 1; } pclk = tmp; } else { events[event_idx++] = tmp; } } if (pclk == 0) { cerr << get_fileline() << ": error: None of the edges" << " are valid clock inputs." << endl; cerr << get_fileline() << ": : Perhaps the clock" << " is read by a statement or expression?" << endl; return false; } if (debug_synth2) { cerr << get_fileline() << ": NetEvWait::synth_sync: " << "Found and synthesized the FF clock." << endl; } connect(ff_clk->pin(0), pclk->pin(0)); if (pclk->edge() == NetEvProbe::NEGEDGE) { ff_negedge = true; if (debug_synth2) { cerr << get_fileline() << ": debug: " << "Detected a NEGEDGE clock for the synthesized ff." << endl; } } /* Synthesize the input to the DFF. */ bool flag = statement_->synth_sync(des, scope, ff_negedge, ff_clk, ff_ce, ff_aclr, ff_aset, ff_aset_value, nex_map, nex_out, events); return flag; } /* * This method is called for a process that is determined to be * synchronous. Create a NetFF device to hold the output from the * statement, and synthesize that statement in place. */ bool NetProcTop::synth_sync(Design*des) { if (debug_synth2) { cerr << get_fileline() << ": NetProcTop::synth_sync: " << "Process is apparently synchronous. Making NetFFs." << endl; } NexusSet nex_set; statement_->nex_output(nex_set); vector aset_value(nex_set.size()); /* Make a model FF that will connect to the first item in the set, and will also take the initial connection of clocks and resets. */ // Create a net to carry the clock for the synthesized FFs. NetNet*clock = new NetNet(scope(), scope()->local_symbol(), NetNet::TRI, &netvector_t::scalar_logic); clock->local_flag(true); clock->set_line(*this); #if 0 NetNet*ce = new NetNet(scope(), scope()->local_symbol(), NetNet::TRI, &netvector_t::scalar_logic); ce->local_flag(true); #else NetBus ce (scope(), nex_set.size()); #endif NetBus nex_d (scope(), nex_set.size()); NetBus nex_q (scope(), nex_set.size()); NetBus aclr (scope(), nex_set.size()); NetBus aset (scope(), nex_set.size()); /* The Q of the NetFF devices is connected to the output that we are. The nex_q is a bundle of the outputs. We will also pass the nex_q as a map to the statement's synth_sync method to map it to the correct nex_d pin. */ for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { connect(nex_set[idx].lnk, nex_q.pin(idx)); } // Connect the input later. /* Synthesize the input to the DFF. */ bool negedge = false; bool flag = statement_->synth_sync(des, scope(), negedge, clock, ce, aclr, aset, aset_value, nex_set, nex_d, vector()); if (! flag) { delete clock; return false; } for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { //ivl_assert(*this, nex_set[idx].nex); if (debug_synth2) { cerr << get_fileline() << ": debug: " << "Top level making a " << nex_set[idx].wid << "-wide " << "NetFF device." << endl; } NetFF*ff2 = new NetFF(scope(), scope()->local_symbol(), negedge, nex_set[idx].wid); des->add_node(ff2); ff2->set_line(*this); ff2->aset_value(aset_value[idx]); NetNet*tmp = nex_d.pin(idx).nexus()->pick_any_net(); tmp->set_line(*this); assert(tmp); tmp = crop_to_width(des, tmp, ff2->width()); connect(nex_q.pin(idx), ff2->pin_Q()); connect(tmp->pin(0), ff2->pin_Data()); connect(clock->pin(0), ff2->pin_Clock()); if (ce.pin(idx).is_linked()) connect(ce.pin(idx), ff2->pin_Enable()); if (aclr.pin(idx).is_linked()) connect(aclr.pin(idx), ff2->pin_Aclr()); if (aset.pin(idx).is_linked()) connect(aset.pin(idx), ff2->pin_Aset()); #if 0 if (ff->pin_Sset().is_linked()) connect(ff->pin_Sset(), ff2->pin_Sset()); if (ff->pin_Sclr().is_linked()) connect(ff->pin_Sclr(), ff2->pin_Sclr()); #endif } // The "clock" and "ce" nets were just to carry the connection // back to the flip-flop. Delete them now. The connections // will persist. delete clock; #if 0 delete ce; #endif return true; } class synth2_f : public functor_t { public: void process(Design*, NetProcTop*); private: }; /* * Look at a process. If it is asynchronous, then synthesize it as an * asynchronous process and delete the process itself for its gates. */ void synth2_f::process(Design*des, NetProcTop*top) { if (top->attribute(perm_string::literal("ivl_synthesis_off")).as_ulong() != 0) return; /* If the scope that contains this process as a cell attribute attached to it, then skip synthesis. */ if (top->scope()->attribute(perm_string::literal("ivl_synthesis_cell")).len() > 0) return; if (top->is_synchronous()) { bool flag = top->synth_sync(des); if (! flag) { cerr << top->get_fileline() << ": error: " << "Unable to synthesize synchronous process." << endl; des->errors += 1; return; } des->delete_process(top); return; } if (! top->is_asynchronous()) { bool synth_error_flag = false; if (top->attribute(perm_string::literal("ivl_combinational")).as_ulong() != 0) { cerr << top->get_fileline() << ": error: " << "Process is marked combinational," << " but isn't really." << endl; des->errors += 1; synth_error_flag = true; } if (top->attribute(perm_string::literal("ivl_synthesis_on")).as_ulong() != 0) { cerr << top->get_fileline() << ": error: " << "Process is marked for synthesis," << " but I can't do it." << endl; des->errors += 1; synth_error_flag = true; } if (! synth_error_flag) cerr << top->get_fileline() << ": warning: " << "Process not synthesized." << endl; return; } if (! top->synth_async(des)) { cerr << top->get_fileline() << ": error: " << "failed to synthesize asynchronous " << "logic for this process." << endl; des->errors += 1; return; } des->delete_process(top); } void synth2(Design*des) { synth2_f synth_obj; des->functor(&synth_obj); } iverilog-10_1/sys_funcs.cc000066400000000000000000000127451265551621300156760ustar00rootroot00000000000000/* * Copyright (c) 2004-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include # include # include /* * Manage the information about system functions. This information is * collected from the sources before elaboration and made available * via the lookup_sys_func function. */ static const struct sfunc_return_type sfunc_table[] = { { "$realtime", IVL_VT_REAL, 1, 0 }, { "$bitstoreal", IVL_VT_REAL, 1, 0 }, { "$itor", IVL_VT_REAL, 1, 0 }, { "$realtobits", IVL_VT_LOGIC, 64, 0 }, { "$time", IVL_VT_LOGIC, 64, 0 }, { "$stime", IVL_VT_LOGIC, 32, 0 }, { "$simtime", IVL_VT_LOGIC, 64, 0 }, { 0, IVL_VT_LOGIC, 32, 0 } }; struct sfunc_return_type_cell : sfunc_return_type { struct sfunc_return_type_cell*next; }; static struct sfunc_return_type_cell*sfunc_stack = 0; void cleanup_sys_func_table() { struct sfunc_return_type_cell *next, *cur = sfunc_stack; while (cur) { next = cur->next; delete cur; cur = next; } } const struct sfunc_return_type* lookup_sys_func(const char*name) { /* First, try to find the name in the function stack. */ struct sfunc_return_type_cell*cur = sfunc_stack; while (cur) { if (strcmp(cur->name, name) == 0) return cur; cur = cur->next; } /* Next, look in the core table. */ unsigned idx = 0; while (sfunc_table[idx].name) { if (strcmp(sfunc_table[idx].name, name) == 0) return sfunc_table + idx; idx += 1; } /* No luck finding, so return the trailer, which give a default description. */ return sfunc_table + idx; } /* * This function loads a system functions descriptor file with the * format: * * [] */ int load_sys_func_table(const char*path) { struct sfunc_return_type_cell*cell; FILE*fd = fopen(path, "r"); if (fd == 0) { if (verbose_flag) { fprintf(stderr, "%s: Unable to open System Function Table file.\n", path); } return -1; } if (verbose_flag) { fprintf(stderr, "%s: Processing System Function Table file.\n", path); } char buf[256]; while (fgets(buf, sizeof buf, fd)) { char*name = buf + strspn(buf, " \t\r\n"); /* Skip empty lines. */ if (name[0] == 0) continue; /* Skip comment lines. */ if (name[0] == '#') continue; char*cp = name + strcspn(name, " \t\r\n"); if (cp[0]) *cp++ = 0; cp += strspn(cp, " \t\r\n"); char*stype = cp; if (stype[0] == 0) { fprintf(stderr, "%s:%s: No function type?\n", path, name); continue; } cp = stype + strcspn(stype, " \t\r\n"); if (cp[0]) *cp++ = 0; if (strcmp(stype,"vpiSysFuncReal") == 0) { cell = new struct sfunc_return_type_cell; cell->name = lex_strings.add(name); cell->type = IVL_VT_REAL; cell->wid = 1; cell->signed_flag = true; cell->next = sfunc_stack; sfunc_stack = cell; continue; } if (strcmp(stype,"vpiSysFuncInt") == 0) { cell = new struct sfunc_return_type_cell; cell->name = lex_strings.add(name); cell->type = IVL_VT_LOGIC; cell->wid = 32; cell->signed_flag = true; cell->next = sfunc_stack; sfunc_stack = cell; continue; } /* If this is a sized integer, then parse the additional arguments, the width (decimal) and the optional signed/unsigned flag. */ if (strcmp(stype,"vpiSysFuncSized") == 0) { cp += strspn(cp, " \t\r\n"); char*swidth = cp; unsigned width = 32; bool signed_flag = false; cp = swidth + strcspn(swidth, " \t\r\n"); if (cp[0]) *cp++ = 0; width = strtoul(swidth, 0, 10); cp += strspn(cp, " \t\r\n"); char*flag = cp; while (flag[0]) { cp = flag + strcspn(flag, " \t\r\n"); if (cp[0]) *cp++ = 0; if (strcmp(flag,"signed") == 0) { signed_flag = true; } else if (strcmp(flag,"unsigned") == 0) { signed_flag = false; } flag = cp + strspn(cp, " \t\r\n"); } cell = new struct sfunc_return_type_cell; cell->name = lex_strings.add(name); cell->type = IVL_VT_LOGIC; cell->wid = width; cell->signed_flag = signed_flag; cell->next = sfunc_stack; sfunc_stack = cell; continue; } if (strcmp(stype,"vpiSysFuncVoid") == 0) { cell = new struct sfunc_return_type_cell; cell->name = lex_strings.add(name); cell->type = IVL_VT_VOID; cell->wid = 0; cell->signed_flag = false; cell->next = sfunc_stack; sfunc_stack = cell; continue; } fprintf(stderr, "%s:%s: Unknown type: %s\n", path, name, stype); } fclose(fd); return 0; } iverilog-10_1/t-dll-analog.cc000066400000000000000000000045061265551621300161310ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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.will need a Picture Elements Binary Software * 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 GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include "target.h" # include "ivl_target.h" # include "compiler.h" # include "t-dll.h" # include # include "ivl_alloc.h" bool dll_target::process(const NetAnalogTop*net) { bool rc_flag = true; ivl_process_t obj = (struct ivl_process_s*) calloc(1, sizeof(struct ivl_process_s)); obj->type_ = net->type(); obj->analog_flag = 1; FILE_NAME(obj, net); /* Save the scope of the process. */ obj->scope_ = lookup_scope_(net->scope()); obj->nattr = net->attr_cnt(); obj->attr = fill_in_attributes(net); assert(stmt_cur_ == 0); stmt_cur_ = (struct ivl_statement_s*)calloc(1, sizeof*stmt_cur_); rc_flag = net->statement()->emit_proc(this) && rc_flag; assert(stmt_cur_); obj->stmt_ = stmt_cur_; stmt_cur_ = 0; /* Save the process in the design. */ obj->next_ = des_.threads_; des_.threads_ = obj; return rc_flag; } bool dll_target::proc_contribution(const NetContribution*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_CONTRIB; assert(expr_ == 0); net->lval()->expr_scan(this); stmt_cur_->u_.contrib_.lval = expr_; expr_ = 0; net->rval()->expr_scan(this); stmt_cur_->u_.contrib_.rval = expr_; expr_ = 0; return true; } iverilog-10_1/t-dll-api.cc000066400000000000000000001743121265551621300154440ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "StringHeap.h" # include "t-dll.h" # include "discipline.h" # include "netclass.h" # include "netdarray.h" # include "netenum.h" # include "netvector.h" # include # include # include # include "ivl_alloc.h" static StringHeap api_strings; /* THE FOLLOWING ARE FUNCTIONS THAT ARE CALLED FROM THE TARGET. */ extern "C" ivl_island_t ivl_branch_island(ivl_branch_t net) { return net->island; } extern "C" ivl_nexus_t ivl_branch_terminal(ivl_branch_t net, int idx) { assert(idx >= 0); assert( idx < 2); return net->pins[idx]; } extern "C" const char*ivl_design_delay_sel(ivl_design_t des) { return des->self->get_delay_sel(); } extern "C" const char*ivl_design_flag(ivl_design_t des, const char*key) { return des->self->get_flag(key); } extern "C" int ivl_design_process(ivl_design_t des, ivl_process_f func, void*cd) { for (ivl_process_t idx = des->threads_; idx; idx = idx->next_) { int rc = (func)(idx, cd); if (rc != 0) return rc; } return 0; } extern "C" ivl_scope_t ivl_design_root(ivl_design_t des) { cerr << "ANACHRONISM: ivl_design_root called. " "Use ivl_design_roots instead." << endl; assert (des->roots.size() > 0); return des->roots[0]; } extern "C" void ivl_design_roots(ivl_design_t des, ivl_scope_t **scopes, unsigned int *nscopes) { assert (nscopes && scopes); if (des->root_scope_list.size() == 0) { size_t fill = 0; des->root_scope_list.resize(des->root_tasks.size() + des->packages.size() + des->roots.size() + des->classes.size()); for (map::iterator idx = des->root_tasks.begin() ; idx != des->root_tasks.end() ; ++ idx) des->root_scope_list[fill++] = idx->second; for (map::iterator idx = des->classes.begin() ; idx != des->classes.end() ; ++ idx) des->root_scope_list[fill++] = idx->second; for (size_t idx = 0 ; idx < des->packages.size() ; idx += 1) des->root_scope_list[fill++] = des->packages[idx]; for (size_t idx = 0 ; idx < des->roots.size() ; idx += 1) des->root_scope_list[fill++] = des->roots[idx]; } *scopes = &des->root_scope_list[0]; *nscopes = des->root_scope_list.size(); } extern "C" int ivl_design_time_precision(ivl_design_t des) { return des->time_precision; } extern "C" unsigned ivl_design_consts(ivl_design_t des) { return des->consts.size(); } extern "C" ivl_net_const_t ivl_design_const(ivl_design_t des, unsigned idx) { assert(idx < des->consts.size()); return des->consts[idx]; } extern "C" unsigned ivl_design_disciplines(ivl_design_t des) { assert(des); return des->disciplines.size(); } extern "C" ivl_discipline_t ivl_design_discipline(ivl_design_t des, unsigned idx) { assert(des); assert(idx < des->disciplines.size()); return des->disciplines[idx]; } extern "C" ivl_dis_domain_t ivl_discipline_domain(ivl_discipline_t net) { return net->domain(); } extern "C" ivl_nature_t ivl_discipline_flow(ivl_discipline_t net) { return net->flow(); } extern "C" const char* ivl_discipline_name(ivl_discipline_t net) { return net->name(); } extern "C" ivl_nature_t ivl_discipline_potential(ivl_discipline_t net) { return net->potential(); } extern "C" ivl_expr_type_t ivl_expr_type(ivl_expr_t net) { if (net == 0) return IVL_EX_NONE; return net->type_; } extern "C" const char*ivl_expr_file(ivl_expr_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_expr_lineno(ivl_expr_t net) { assert(net); return net->lineno; } extern "C" ivl_variable_type_t ivl_const_type(ivl_net_const_t net) { assert(net); return net->type; } extern "C" const char*ivl_const_bits(ivl_net_const_t net) { assert(net); switch (net->type) { case IVL_VT_BOOL: case IVL_VT_LOGIC: case IVL_VT_STRING: if (net->width_ <= sizeof(net->b.bit_)) return net->b.bit_; else return net->b.bits_; default: return 0; } } extern "C" ivl_expr_t ivl_const_delay(ivl_net_const_t net, unsigned transition) { assert(transition < 3); return net->delay[transition]; } extern "C" const char*ivl_const_file(ivl_net_const_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_const_lineno(ivl_net_const_t net) { assert(net); return net->lineno; } extern "C" ivl_nexus_t ivl_const_nex(ivl_net_const_t net) { assert(net); return net->pin_; } extern "C" double ivl_const_real(ivl_net_const_t net) { assert(net); assert(net->type == IVL_VT_REAL); return net->b.real_value; } extern "C" ivl_scope_t ivl_const_scope(ivl_net_const_t net) { assert(net); return net->scope; } extern "C" int ivl_const_signed(ivl_net_const_t net) { assert(net); return net->signed_; } extern "C" unsigned ivl_const_width(ivl_net_const_t net) { assert(net); return net->width_; } extern "C" unsigned ivl_enum_names(ivl_enumtype_t net) { assert(net); return net->size(); } extern "C" const char* ivl_enum_name(ivl_enumtype_t net, unsigned idx) { assert(net); assert(idx < net->size()); return net->name_at(idx); } extern "C" const char* ivl_enum_bits(ivl_enumtype_t net, unsigned idx) { assert(net); assert(idx < net->size()); return net->bits_at(idx); } extern "C" ivl_variable_type_t ivl_enum_type(ivl_enumtype_t net) { assert(net); return net->base_type(); } extern "C" unsigned ivl_enum_width(ivl_enumtype_t net) { assert(net); return net->packed_width(); } extern "C" int ivl_enum_signed(ivl_enumtype_t net) { assert(net); return net->get_signed(); } extern "C" const char*ivl_enum_file(ivl_enumtype_t net) { assert(net); return net->get_file().str(); } extern "C" unsigned ivl_enum_lineno(ivl_enumtype_t net) { assert(net); return net->get_lineno(); } extern "C" const char* ivl_event_name(ivl_event_t net) { static char*name_buffer = 0; static unsigned name_size = 0; ivl_scope_t scope = net->scope; const char*sn = ivl_scope_name(scope); unsigned need = strlen(sn) + 1 + strlen(net->name) + 1; if (need > name_size) { name_buffer = (char*)realloc(name_buffer, need); name_size = need; } strcpy(name_buffer, sn); char*tmp = name_buffer + strlen(sn); *tmp++ = '.'; strcpy(tmp, net->name); cerr << "ANACHRONISM: Call to anachronistic ivl_event_name." << endl; return name_buffer; } extern "C" const char* ivl_event_basename(ivl_event_t net) { return net->name; } extern "C" const char*ivl_event_file(ivl_event_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_event_lineno(ivl_event_t net) { assert(net); return net->lineno; } extern "C" ivl_scope_t ivl_event_scope(ivl_event_t net) { return net->scope; } extern "C" unsigned ivl_event_nany(ivl_event_t net) { assert(net); return net->nany; } extern "C" ivl_nexus_t ivl_event_any(ivl_event_t net, unsigned idx) { assert(net); assert(idx < net->nany); return net->pins[idx]; } extern "C" unsigned ivl_event_nneg(ivl_event_t net) { assert(net); return net->nneg; } extern "C" ivl_nexus_t ivl_event_neg(ivl_event_t net, unsigned idx) { assert(net); assert(idx < net->nneg); return net->pins[net->nany + idx]; } extern "C" unsigned ivl_event_npos(ivl_event_t net) { assert(net); return net->npos; } extern "C" ivl_nexus_t ivl_event_pos(ivl_event_t net, unsigned idx) { assert(net); assert(idx < net->npos); return net->pins[net->nany + net->nneg + idx]; } extern "C" const char* ivl_expr_bits(ivl_expr_t net) { assert(net && (net->type_ == IVL_EX_NUMBER)); return net->u_.number_.bits_; } extern "C" ivl_branch_t ivl_expr_branch(ivl_expr_t net) { assert(net && (net->type_ == IVL_EX_BACCESS)); return net->u_.branch_.branch; } extern "C" ivl_scope_t ivl_expr_def(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_UFUNC: return net->u_.ufunc_.def; default: assert(0); } return 0; } extern "C" uint64_t ivl_expr_delay_val(ivl_expr_t net) { assert(net->type_ == IVL_EX_DELAY); return net->u_.delay_.value; } extern "C" double ivl_expr_dvalue(ivl_expr_t net) { assert(net->type_ == IVL_EX_REALNUM); return net->u_.real_.value; } extern "C" ivl_enumtype_t ivl_expr_enumtype(ivl_expr_t net) { assert(net->type_ == IVL_EX_ENUMTYPE); return net->u_.enumtype_.type; } extern "C" ivl_type_t ivl_expr_net_type(ivl_expr_t net) { return net->net_type; } extern "C" const char* ivl_expr_name(ivl_expr_t net) { switch (net->type_) { case IVL_EX_SFUNC: return net->u_.sfunc_.name_; case IVL_EX_SIGNAL: return net->u_.signal_.sig->name_; case IVL_EX_PROPERTY: { ivl_signal_t sig = ivl_expr_signal(net); ivl_type_t use_type = ivl_signal_net_type(sig); unsigned idx = ivl_expr_property_idx(net); return ivl_type_prop_name(use_type, idx); } default: assert(0); } return 0; } extern "C" ivl_nature_t ivl_expr_nature(ivl_expr_t net) { assert(net && (net->type_ == IVL_EX_BACCESS)); return net->u_.branch_.nature; } extern "C" char ivl_expr_opcode(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_BINARY: return net->u_.binary_.op_; case IVL_EX_UNARY: return net->u_.unary_.op_; default: assert(0); } return 0; } extern "C" ivl_expr_t ivl_expr_oper1(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_BINARY: return net->u_.binary_.lef_; case IVL_EX_PROPERTY: return net->u_.property_.index; case IVL_EX_SELECT: return net->u_.select_.expr_; case IVL_EX_UNARY: return net->u_.unary_.sub_; case IVL_EX_MEMORY: return net->u_.memory_.idx_; case IVL_EX_NEW: return net->u_.new_.size; case IVL_EX_SHALLOWCOPY: return net->u_.shallow_.dest; case IVL_EX_SIGNAL: return net->u_.signal_.word; case IVL_EX_TERNARY: return net->u_.ternary_.cond; default: assert(0); } return 0; } extern "C" ivl_expr_t ivl_expr_oper2(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_BINARY: return net->u_.binary_.rig_; case IVL_EX_NEW: return net->u_.new_.init_val; case IVL_EX_SELECT: return net->u_.select_.base_; case IVL_EX_SHALLOWCOPY: return net->u_.shallow_.src; case IVL_EX_TERNARY: return net->u_.ternary_.true_e; default: assert(0); } return 0; } extern "C" ivl_expr_t ivl_expr_oper3(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_TERNARY: return net->u_.ternary_.false_e; default: assert(0); } return 0; } extern "C" ivl_parameter_t ivl_expr_parameter(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_NUMBER: return net->u_.number_.parameter; case IVL_EX_STRING: return net->u_.string_.parameter; case IVL_EX_REALNUM: return net->u_.real_.parameter; default: return 0; } } extern "C" ivl_expr_t ivl_expr_parm(ivl_expr_t net, unsigned idx) { assert(net); switch (net->type_) { case IVL_EX_ARRAY_PATTERN: assert(idx < net->u_.array_pattern_.parms); return net->u_.array_pattern_.parm[idx]; case IVL_EX_CONCAT: assert(idx < net->u_.concat_.parms); return net->u_.concat_.parm[idx]; case IVL_EX_SFUNC: assert(idx < net->u_.sfunc_.parms); return net->u_.sfunc_.parm[idx]; case IVL_EX_UFUNC: assert(idx < net->u_.ufunc_.parms); return net->u_.ufunc_.parm[idx]; default: assert(0); return 0; } } extern "C" unsigned ivl_expr_parms(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_ARRAY_PATTERN: return net->u_.array_pattern_.parms; case IVL_EX_CONCAT: return net->u_.concat_.parms; case IVL_EX_SFUNC: return net->u_.sfunc_.parms; case IVL_EX_UFUNC: return net->u_.ufunc_.parms; default: assert(0); return 0; } } extern "C" unsigned ivl_expr_repeat(ivl_expr_t net) { assert(net); assert(net->type_ == IVL_EX_CONCAT); return net->u_.concat_.rept; } extern "C" ivl_event_t ivl_expr_event(ivl_expr_t net) { assert(net); assert(net->type_ == IVL_EX_EVENT); return net->u_.event_.event; } extern "C" int ivl_expr_property_idx(ivl_expr_t net) { assert(net); assert(net->type_ == IVL_EX_PROPERTY); return net->u_.property_.prop_idx; } extern "C" ivl_scope_t ivl_expr_scope(ivl_expr_t net) { assert(net); assert(net->type_ == IVL_EX_SCOPE); return net->u_.scope_.scope; } extern "C" ivl_select_type_t ivl_expr_sel_type(ivl_expr_t net) { assert(net); assert(net->type_ == IVL_EX_SELECT); return net->u_.select_.sel_type_; } extern "C" ivl_signal_t ivl_expr_signal(ivl_expr_t net) { assert(net); switch (net->type_) { case IVL_EX_SIGNAL: case IVL_EX_ARRAY: return net->u_.signal_.sig; case IVL_EX_PROPERTY: return net->u_.property_.sig; default: assert(0); return 0; } } extern "C" int ivl_expr_signed(ivl_expr_t net) { assert(net); return net->signed_; } extern "C" int ivl_expr_sized(ivl_expr_t net) { assert(net); return net->sized_; } extern "C" const char* ivl_expr_string(ivl_expr_t net) { assert(net->type_ == IVL_EX_STRING); return net->u_.string_.value_; } extern "C" unsigned long ivl_expr_uvalue(ivl_expr_t net) { switch (net->type_) { case IVL_EX_ULONG: return net->u_.ulong_.value; case IVL_EX_NUMBER: { unsigned long val = 0; for (unsigned long idx = 0 ; idx < net->width_ ; idx += 1) { if (net->u_.number_.bits_[idx] == '1') val |= 1UL << idx; } return val; } default: assert(0); } assert(0); return 0; } extern "C" ivl_variable_type_t ivl_expr_value(ivl_expr_t net) { assert(net); return net->value_; } extern "C" unsigned ivl_expr_width(ivl_expr_t net) { // assert(net); return net->width_; } /* * ivl_file_table_index puts entries in the map as needed and returns * the appropriate index. * ivl_file_table_size returns the number of entries in the table. * ivl_file_table_item returns the file name for the given index. */ struct ltstr { bool operator()(const char*s1, const char*s2) const { return strcmp(s1, s2) < 0; } }; static map fn_map; static vector fn_vector; static void ivl_file_table_init() { /* The first two index entries do not depend on a real * file name and are always available. */ fn_vector.push_back("N/A"); fn_map["N/A"] = 0; fn_vector.push_back(""); fn_map[""] = 1; } extern "C" const char* ivl_file_table_item(unsigned idx) { if (fn_vector.empty()) { ivl_file_table_init(); } assert(idx < fn_vector.size()); return fn_vector[idx]; } extern "C" unsigned ivl_file_table_index(const char*name) { if (fn_vector.empty()) { ivl_file_table_init(); } if (name == NULL) return 0; /* The new index is the current map size. This is inserted only * if the file name is not currently in the map. */ pair::iterator, bool> result; result = fn_map.insert(make_pair(name, fn_vector.size())); if (result.second) { fn_vector.push_back(name); } return result.first->second; } extern "C" unsigned ivl_file_table_size() { if (fn_vector.empty()) { ivl_file_table_init(); } return fn_vector.size(); } extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value) { if (flag >= net->flags.size()) { if (value == 0) return 0; else net->flags.resize(flag+1, false); } int old_flag = net->flags[flag]; net->flags[flag] = value != 0; return old_flag; } extern "C" int ivl_island_flag_test(ivl_island_t net, unsigned flag) { if (flag >= net->flags.size()) return 0; else return net->flags[flag]; } extern "C" const char*ivl_logic_file(ivl_net_logic_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_logic_lineno(ivl_net_logic_t net) { assert(net); return net->lineno; } extern "C" unsigned ivl_logic_is_cassign(ivl_net_logic_t net) { assert(net); return net->is_cassign; } extern "C" const char* ivl_logic_attr(ivl_net_logic_t net, const char*key) { assert(net); unsigned idx; for (idx = 0 ; idx < net->nattr ; idx += 1) { if (strcmp(net->attr[idx].key, key) == 0) return net->attr[idx].type == IVL_ATT_STR ? net->attr[idx].val.str : 0; } return 0; } extern "C" unsigned ivl_logic_attr_cnt(ivl_net_logic_t net) { return net->nattr; } extern "C" ivl_attribute_t ivl_logic_attr_val(ivl_net_logic_t net, unsigned idx) { assert(idx < net->nattr); return net->attr + idx; } extern "C" ivl_drive_t ivl_logic_drive0(ivl_net_logic_t net) { ivl_nexus_t nex = ivl_logic_pin(net, 0); for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t cur = ivl_nexus_ptr(nex, idx); if (ivl_nexus_ptr_log(cur) != net) continue; if (ivl_nexus_ptr_pin(cur) != 0) continue; return ivl_nexus_ptr_drive0(cur); } assert(0); return IVL_DR_STRONG; } extern "C" ivl_drive_t ivl_logic_drive1(ivl_net_logic_t net) { ivl_nexus_t nex = ivl_logic_pin(net, 0); for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t cur = ivl_nexus_ptr(nex, idx); if (ivl_nexus_ptr_log(cur) != net) continue; if (ivl_nexus_ptr_pin(cur) != 0) continue; return ivl_nexus_ptr_drive1(cur); } assert(0); return IVL_DR_STRONG; } extern "C" const char* ivl_logic_name(ivl_net_logic_t net) { assert(net); cerr << "ANACHRONISM: Call to anachronistic ivl_logic_name." << endl; return net->name_; } extern "C" const char* ivl_logic_basename(ivl_net_logic_t net) { assert(net); return net->name_; } extern "C" ivl_scope_t ivl_logic_scope(ivl_net_logic_t net) { assert(net); return net->scope_; } extern "C" ivl_logic_t ivl_logic_type(ivl_net_logic_t net) { return net->type_; } extern "C" unsigned ivl_logic_pins(ivl_net_logic_t net) { return net->npins_; } extern "C" ivl_nexus_t ivl_logic_pin(ivl_net_logic_t net, unsigned pin) { assert(pin < net->npins_); return net->pins_[pin]; } extern "C" ivl_udp_t ivl_logic_udp(ivl_net_logic_t net) { assert(net->type_ == IVL_LO_UDP); assert(net->udp); return net->udp; } extern "C" ivl_expr_t ivl_logic_delay(ivl_net_logic_t net, unsigned transition) { assert(transition < 3); return net->delay[transition]; } extern "C" unsigned ivl_logic_width(ivl_net_logic_t net) { assert(net); return net->width_; } extern "C" int ivl_udp_sequ(ivl_udp_t net) { return net->sequ; } extern "C" unsigned ivl_udp_nin(ivl_udp_t net) { return net->nin; } extern "C" char ivl_udp_init(ivl_udp_t net) { return net->init; } extern "C" const char* ivl_udp_port(ivl_udp_t net, unsigned idx) { assert(idx <= net->nin); assert(net->ports); assert(net->ports[idx].c_str()); return net->ports[idx].c_str(); } extern "C" const char* ivl_udp_row(ivl_udp_t net, unsigned idx) { assert(idx < net->nrows); assert(net->table); assert(net->table[idx]); return net->table[idx]; } extern "C" unsigned ivl_udp_rows(ivl_udp_t net) { return net->nrows; } extern "C" const char* ivl_udp_name(ivl_udp_t net) { assert(net->name); return net->name; } extern "C" const char* ivl_udp_file(ivl_udp_t net) { return net->file.str(); } extern "C" unsigned ivl_udp_lineno(ivl_udp_t net) { return net->lineno; } extern "C" const char* ivl_lpm_basename(ivl_lpm_t net) { return net->name; } extern "C" ivl_nexus_t ivl_lpm_async_clr(ivl_lpm_t net) { assert(net); switch(net->type) { case IVL_LPM_FF: return net->u_.ff.aclr; default: assert(0); return 0; } } extern "C" ivl_nexus_t ivl_lpm_sync_clr(ivl_lpm_t net) { assert(net); switch(net->type) { case IVL_LPM_FF: return net->u_.ff.sclr; default: assert(0); return 0; } } extern "C" ivl_expr_t ivl_lpm_delay(ivl_lpm_t net, unsigned transition) { assert(transition < 3); return net->delay[transition]; } extern "C" ivl_nexus_t ivl_lpm_async_set(ivl_lpm_t net) { assert(net); switch(net->type) { case IVL_LPM_FF: return net->u_.ff.aset; default: assert(0); return 0; } } extern "C" ivl_nexus_t ivl_lpm_sync_set(ivl_lpm_t net) { assert(net); switch(net->type) { case IVL_LPM_FF: return net->u_.ff.sset; default: assert(0); return 0; } } extern "C" ivl_signal_t ivl_lpm_array(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_ARRAY: return net->u_.array.sig; default: assert(0); return 0; } } extern "C" unsigned ivl_lpm_base(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: return net->u_.part.base; case IVL_LPM_SUBSTITUTE: return net->u_.substitute.base; default: assert(0); return 0; } } extern "C" unsigned ivl_lpm_negedge(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_FF: return net->u_.ff.negedge_flag; default: assert(0); return 0; } } extern "C" ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_FF: return net->u_.ff.clk; default: assert(0); return 0; } } extern "C" ivl_expr_t ivl_lpm_aset_value(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_FF: return net->u_.ff.aset_value; default: assert(0); return 0; } } extern "C" ivl_expr_t ivl_lpm_sset_value(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_FF: return net->u_.ff.sset_value; default: assert(0); return 0; } } extern "C" ivl_scope_t ivl_lpm_define(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_UFUNC: return net->u_.ufunc.def; default: assert(0); return 0; } } extern "C" ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_FF: return net->u_.ff.we; default: assert(0); return 0; } } extern "C" const char* ivl_lpm_file(ivl_lpm_t net) { return net->file.str(); } extern "C" unsigned ivl_lpm_lineno(ivl_lpm_t net) { return net->lineno; } extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) { assert(net); switch (net->type) { case IVL_LPM_ABS: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: assert(idx == 0); return net->u_.arith.a; case IVL_LPM_ADD: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_POW: case IVL_LPM_SUB: assert(idx <= 1); if (idx == 0) return net->u_.arith.a; else return net->u_.arith.b; case IVL_LPM_MUX: assert(idx < net->u_.mux.size); return net->u_.mux.d[idx]; case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: case IVL_LPM_SIGN_EXT: assert(idx == 0); return net->u_.reduce.a; case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: assert(idx <= 1); if (idx == 0) return net->u_.shift.d; else return net->u_.shift.s; case IVL_LPM_FF: assert(idx == 0); return net->u_.ff.d.pin; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: assert(idx < net->u_.concat.inputs); return net->u_.concat.pins[idx+1]; case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: assert(idx <= 1); if (idx == 0) return net->u_.part.a; else return net->u_.part.s; case IVL_LPM_REPEAT: assert(idx == 0); return net->u_.repeat.a; case IVL_LPM_SFUNC: // Skip the return port. assert(idx < (net->u_.sfunc.ports-1)); return net->u_.sfunc.pins[idx+1]; case IVL_LPM_SUBSTITUTE: assert(idx <= 1); if (idx == 0) return net->u_.substitute.a; else return net->u_.substitute.s; case IVL_LPM_UFUNC: // Skip the return port. assert(idx < (net->u_.ufunc.ports-1)); return net->u_.ufunc.pins[idx+1]; default: assert(0); return 0; } } extern "C" ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx) { cerr << "ANACHRONISM: Call to anachronistic ivl_lpm_datab." << endl; assert(net); switch (net->type) { case IVL_LPM_ADD: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_POW: case IVL_LPM_SUB: assert(idx == 0); return net->u_.arith.b; default: assert(0); return 0; } } /* * This function returns the hierarchical name for the LPM device. The * name needs to be built up from the scope name and the lpm base * name. * * Anachronism: This function is provided for * compatibility. Eventually, it will be removed. */ extern "C" const char* ivl_lpm_name(ivl_lpm_t net) { static char*name_buffer = 0; static unsigned name_size = 0; ivl_scope_t scope = ivl_lpm_scope(net); const char*sn = ivl_scope_name(scope); unsigned need = strlen(sn) + 1 + strlen(net->name) + 1; if (need > name_size) { name_buffer = (char*)realloc(name_buffer, need); name_size = need; } strcpy(name_buffer, sn); char*tmp = name_buffer + strlen(sn); *tmp++ = '.'; strcpy(tmp, net->name); return name_buffer; } extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_NEE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_POW: case IVL_LPM_SUB: return net->u_.arith.q; case IVL_LPM_FF: return net->u_.ff.q.pin; case IVL_LPM_MUX: return net->u_.mux.q; case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: case IVL_LPM_SIGN_EXT: return net->u_.reduce.q; case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: return net->u_.shift.q; case IVL_LPM_SFUNC: return net->u_.sfunc.pins[0]; case IVL_LPM_UFUNC: return net->u_.ufunc.pins[0]; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: return net->u_.concat.pins[0]; case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: return net->u_.part.q; case IVL_LPM_REPEAT: return net->u_.repeat.q; case IVL_LPM_SUBSTITUTE: return net->u_.substitute.q; case IVL_LPM_ARRAY: return net->u_.array.q; default: assert(0); return 0; } } extern "C" ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net) { ivl_nexus_t nex = ivl_lpm_q(net); for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t cur = ivl_nexus_ptr(nex, idx); if (ivl_nexus_ptr_lpm(cur) != net) continue; if (ivl_nexus_ptr_pin(cur) != 0) continue; return ivl_nexus_ptr_drive0(cur); } assert(0); return IVL_DR_STRONG; } extern "C" ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net) { ivl_nexus_t nex = ivl_lpm_q(net); for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t cur = ivl_nexus_ptr(nex, idx); if (ivl_nexus_ptr_lpm(cur) != net) continue; if (ivl_nexus_ptr_pin(cur) != 0) continue; return ivl_nexus_ptr_drive1(cur); } assert(0); return IVL_DR_STRONG; } extern "C" ivl_scope_t ivl_lpm_scope(ivl_lpm_t net) { assert(net); return net->scope; } extern "C" ivl_nexus_t ivl_lpm_select(ivl_lpm_t net) { switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.s; case IVL_LPM_ARRAY: return net->u_.array.a; default: assert(0); return 0; } } extern "C" unsigned ivl_lpm_selects(ivl_lpm_t net) { switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.swid; case IVL_LPM_ARRAY: return net->u_.array.swid; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: cerr << "error: ivl_lpm_selects() is no longer supported for " "IVL_LPM_CONCAT, use ivl_lpm_size() instead." << endl; default: assert(0); return 0; } } extern "C" int ivl_lpm_signed(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_FF: case IVL_LPM_MUX: return 0; case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_POW: case IVL_LPM_SUB: case IVL_LPM_CAST_INT2: return net->u_.arith.signed_flag; case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: return 0; case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: return net->u_.shift.signed_flag; case IVL_LPM_CAST_INT: case IVL_LPM_SIGN_EXT: // Sign extend is always signed. return 1; case IVL_LPM_SFUNC: return 0; case IVL_LPM_UFUNC: return 0; case IVL_LPM_CONCAT: // Concatenations are always unsigned case IVL_LPM_CONCATZ: // Concatenations are always unsigned return 0; case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: return net->u_.part.signed_flag; case IVL_LPM_REPEAT: case IVL_LPM_SUBSTITUTE: return 0; case IVL_LPM_ARRAY: // Array ports take the signedness of the array. return net->u_.array.sig->net_type->get_signed()? 1 : 0; default: assert(0); return 0; } } extern "C" unsigned ivl_lpm_size(ivl_lpm_t net) { switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.size; case IVL_LPM_SFUNC: return net->u_.sfunc.ports - 1; case IVL_LPM_UFUNC: return net->u_.ufunc.ports - 1; case IVL_LPM_REPEAT: return net->u_.repeat.count; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: return net->u_.concat.inputs; case IVL_LPM_ABS: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: case IVL_LPM_SIGN_EXT: case IVL_LPM_FF: return 1; case IVL_LPM_ADD: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_POW: case IVL_LPM_SUB: case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: return 2; default: assert(0); return 0; } } extern "C" const char* ivl_lpm_string(ivl_lpm_t net) { assert(net->type == IVL_LPM_SFUNC); return net->u_.sfunc.fun_name; } extern "C" ivl_lpm_type_t ivl_lpm_type(ivl_lpm_t net) { return net->type; } extern "C" unsigned ivl_lpm_width(ivl_lpm_t net) { assert(net); return net->width; } extern "C" ivl_event_t ivl_lpm_trigger(ivl_lpm_t net) { assert(net); switch (net->type) { case IVL_LPM_SFUNC: return net->u_.sfunc.trigger; case IVL_LPM_UFUNC: return net->u_.ufunc.trigger; default: assert(0); return 0; } } /* * Deprecated */ extern "C" ivl_expr_t ivl_lval_mux(ivl_lval_t) { return 0; } extern "C" ivl_expr_t ivl_lval_idx(ivl_lval_t net) { assert(net); if (net->type_ == IVL_LVAL_ARR) return net->idx; return 0x0; } extern "C" ivl_expr_t ivl_lval_part_off(ivl_lval_t net) { assert(net); return net->loff; } extern "C" ivl_select_type_t ivl_lval_sel_type(ivl_lval_t net) { assert(net); return net->sel_type; } extern "C" unsigned ivl_lval_width(ivl_lval_t net) { assert(net); return net->width_; } extern "C" int ivl_lval_property_idx(ivl_lval_t net) { assert(net); return net->property_idx; } extern "C" ivl_signal_t ivl_lval_sig(ivl_lval_t net) { assert(net); switch (net->type_) { case IVL_LVAL_REG: case IVL_LVAL_ARR: return net->n.sig; default: return 0; } } extern "C" ivl_lval_t ivl_lval_nest(ivl_lval_t net) { assert(net); if (net->type_ == IVL_LVAL_LVAL) return net->n.nest; return 0; } extern "C" const char* ivl_nature_name(ivl_nature_t net) { return net->name(); } /* * The nexus name is rarely needed. (Shouldn't be needed at all!) This * function will calculate the name if it is not already calculated. */ extern "C" const char* ivl_nexus_name(ivl_nexus_t net) { assert(net); if (net->name_ == 0) { char tmp[2 * sizeof(net) + 5]; snprintf(tmp, sizeof tmp, "n%p", (void *)net); net->name_ = api_strings.add(tmp); } return net->name_; } extern "C" void* ivl_nexus_get_private(ivl_nexus_t net) { assert(net); return net->private_data; } extern "C" void ivl_nexus_set_private(ivl_nexus_t net, void*data) { assert(net); net->private_data = data; } extern "C" unsigned ivl_nexus_ptrs(ivl_nexus_t net) { assert(net); return net->ptrs_.size(); } extern "C" ivl_nexus_ptr_t ivl_nexus_ptr(ivl_nexus_t net, unsigned idx) { assert(net); assert(idx < net->ptrs_.size()); return & net->ptrs_[idx]; } extern "C" ivl_drive_t ivl_nexus_ptr_drive0(ivl_nexus_ptr_t net) { assert(net); return (ivl_drive_t)(net->drive0); } extern "C" ivl_drive_t ivl_nexus_ptr_drive1(ivl_nexus_ptr_t net) { assert(net); return (ivl_drive_t)(net->drive1); } extern "C" unsigned ivl_nexus_ptr_pin(ivl_nexus_ptr_t net) { assert(net); return net->pin_; } extern "C" ivl_branch_t ivl_nexus_ptr_branch(ivl_nexus_ptr_t net) { if (net == 0) return 0; if (net->type_ != __NEXUS_PTR_BRA) return 0; return net->l.bra; } extern "C" ivl_net_const_t ivl_nexus_ptr_con(ivl_nexus_ptr_t net) { if (net == 0) return 0; if (net->type_ != __NEXUS_PTR_CON) return 0; return net->l.con; } extern "C" ivl_net_logic_t ivl_nexus_ptr_log(ivl_nexus_ptr_t net) { if (net == 0) return 0; if (net->type_ != __NEXUS_PTR_LOG) return 0; return net->l.log; } extern "C" ivl_lpm_t ivl_nexus_ptr_lpm(ivl_nexus_ptr_t net) { if (net == 0) return 0; if (net->type_ != __NEXUS_PTR_LPM) return 0; return net->l.lpm; } extern "C" ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net) { if (net == 0) return 0; if (net->type_ != __NEXUS_PTR_SIG) return 0; return net->l.sig; } extern "C" ivl_switch_t ivl_nexus_ptr_switch(ivl_nexus_ptr_t net) { if (net == 0) return 0; if (net->type_ != __NEXUS_PTR_SWI) return 0; return net->l.swi; } extern "C" const char* ivl_parameter_basename(ivl_parameter_t net) { assert(net); return net->basename; } extern "C" int ivl_parameter_local(ivl_parameter_t net) { assert(net); return net->local; } extern "C" int ivl_parameter_signed(ivl_parameter_t net) { assert(net); return net->signed_flag; } extern "C" int ivl_parameter_msb(ivl_parameter_t net) { assert(net); return net->msb; } extern "C" int ivl_parameter_lsb(ivl_parameter_t net) { assert(net); return net->lsb; } /* * No need to waste space with storing the width of the parameter since * it can easily be computed when needed. */ extern "C" unsigned ivl_parameter_width(ivl_parameter_t net) { unsigned result = 1; assert(net); if (net->msb >= net->lsb) result += net->msb - net->lsb; else result += net->lsb - net->msb; return result; } extern "C" ivl_expr_t ivl_parameter_expr(ivl_parameter_t net) { assert(net); return net->value; } extern "C" const char* ivl_parameter_file(ivl_parameter_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_parameter_lineno(ivl_parameter_t net) { assert(net); return net->lineno; } extern "C" ivl_scope_t ivl_parameter_scope(ivl_parameter_t net) { assert(net); return net->scope; } extern "C" ivl_nexus_t ivl_path_condit(ivl_delaypath_t obj) { assert(obj); return obj->condit; } extern "C" int ivl_path_is_condit(ivl_delaypath_t obj) { assert(obj); return obj->conditional ? 1 : 0; } extern uint64_t ivl_path_delay(ivl_delaypath_t obj, ivl_path_edge_t edg) { assert(obj); return obj->delay[edg]; } extern ivl_scope_t ivl_path_scope(ivl_delaypath_t obj) { assert(obj); assert(obj->scope); return obj->scope; } extern ivl_nexus_t ivl_path_source(ivl_delaypath_t net) { return net->src; } extern int ivl_path_source_posedge(ivl_delaypath_t net) { return net->posedge ? 1 : 0; } extern int ivl_path_source_negedge(ivl_delaypath_t net) { return net->negedge ? 1 : 0; } extern "C" const char*ivl_process_file(ivl_process_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_process_lineno(ivl_process_t net) { assert(net); return net->lineno; } extern "C" ivl_process_type_t ivl_process_type(ivl_process_t net) { return net->type_; } extern "C" int ivl_process_analog(ivl_process_t net) { return net->analog_flag != 0; } extern "C" ivl_scope_t ivl_process_scope(ivl_process_t net) { return net->scope_; } extern "C" ivl_statement_t ivl_process_stmt(ivl_process_t net) { return net->stmt_; } extern "C" unsigned ivl_process_attr_cnt(ivl_process_t net) { return net->nattr; } extern "C" ivl_attribute_t ivl_process_attr_val(ivl_process_t net, unsigned idx) { assert(idx < net->nattr); return net->attr + idx; } extern "C" unsigned ivl_scope_attr_cnt(ivl_scope_t net) { assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_scope_attr_val(ivl_scope_t net, unsigned idx) { assert(idx < net->nattr); return net->attr + idx; } extern "C" const char* ivl_scope_basename(ivl_scope_t net) { assert(net); return net->name_; } extern "C" int ivl_scope_children(ivl_scope_t net, ivl_scope_f func, void*cd) { for (map::iterator cur = net->children.begin() ; cur != net->children.end() ; ++ cur ) { int rc = func(cur->second, cd); if (rc != 0) return rc; } return 0; } extern "C" size_t ivl_scope_childs(ivl_scope_t net) { assert(net->child.size() == net->children.size()); return net->child.size(); } extern "C" ivl_scope_t ivl_scope_child(ivl_scope_t net, size_t idx) { assert(net && idx < net->child.size()); return net->child[idx]; } extern "C" ivl_type_t ivl_scope_class(ivl_scope_t net, unsigned idx) { assert(idx < net->classes.size()); return net->classes[idx]; } extern "C" unsigned ivl_scope_classes(ivl_scope_t net) { return net->classes.size(); } extern "C" ivl_statement_t ivl_scope_def(ivl_scope_t net) { assert(net); return net->def; } extern "C" const char*ivl_scope_def_file(ivl_scope_t net) { assert(net); return net->def_file.str(); } extern "C" unsigned ivl_scope_def_lineno(ivl_scope_t net) { assert(net); return net->def_lineno; } extern "C" unsigned ivl_scope_enumerates(ivl_scope_t net) { assert(net); return net->enumerations_.size(); } extern "C" ivl_enumtype_t ivl_scope_enumerate(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->enumerations_.size()); return net->enumerations_[idx]; } extern "C" unsigned ivl_scope_events(ivl_scope_t net) { assert(net); return net->nevent_; } extern "C" ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->nevent_); return net->event_[idx]; } extern "C" const char*ivl_scope_file(ivl_scope_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net) { assert(net); return net->is_auto; } extern "C" unsigned ivl_scope_is_cell(ivl_scope_t net) { assert(net); return net->is_cell; } extern "C" unsigned ivl_scope_lineno(ivl_scope_t net) { assert(net); return net->lineno; } extern "C" unsigned ivl_scope_logs(ivl_scope_t net) { assert(net); return net->nlog_; } extern "C" ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->nlog_); return net->log_[idx]; } extern "C" unsigned ivl_scope_lpms(ivl_scope_t net) { assert(net); return net->nlpm_; } extern "C" ivl_lpm_t ivl_scope_lpm(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->nlpm_); return net->lpm_[idx]; } static unsigned scope_name_len(ivl_scope_t net) { unsigned len = 0; for (ivl_scope_t cur = net ; cur ; cur = cur->parent) len += strlen(cur->name_) + 1; return len; } static void push_scope_basename(ivl_scope_t net, char*buf) { if (net->parent == 0) { strcpy(buf, net->name_); return; } push_scope_basename(net->parent, buf); strcat(buf, "."); strcat(buf, net->name_); } extern "C" const char* ivl_scope_name(ivl_scope_t net) { static char*name_buffer = 0; static unsigned name_size = 0; if (net->parent == 0) return net->name_; unsigned needlen = scope_name_len(net); if (name_size < needlen) { name_buffer = (char*)realloc(name_buffer, needlen); name_size = needlen; } push_scope_basename(net, name_buffer); return name_buffer; } extern "C" unsigned ivl_scope_params(ivl_scope_t net) { assert(net); return net->param.size(); } extern "C" ivl_parameter_t ivl_scope_param(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->param.size()); return & (net->param[idx]); } extern "C" ivl_scope_t ivl_scope_parent(ivl_scope_t net) { assert(net); return net->parent; } extern "C" unsigned ivl_scope_mod_module_ports(ivl_scope_t net) { assert(net); assert (net->type_ == IVL_SCT_MODULE ); return static_cast(net->module_ports_info.size()); } extern "C" const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx ) { assert(net); assert (net->type_ == IVL_SCT_MODULE ); assert( idx < net->module_ports_info.size()); return net->module_ports_info[idx].name; } extern "C" ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx ) { switch( net->module_ports_info[idx].type ) { case PortType::PINPUT : return IVL_SIP_INPUT; case PortType::POUTPUT : return IVL_SIP_OUTPUT; case PortType::PINOUT : return IVL_SIP_INOUT; default : return IVL_SIP_NONE; } } extern "C" unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx ) { return net->module_ports_info[idx].width; } extern "C" unsigned ivl_scope_ports(ivl_scope_t net) { assert(net); if (net->type_ == IVL_SCT_MODULE || net->type_ == IVL_SCT_FUNCTION || net->type_ == IVL_SCT_TASK) return net->ports; return 0; } extern "C" ivl_signal_t ivl_scope_port(ivl_scope_t net, unsigned idx) { assert(net); assert(net->type_ == IVL_SCT_FUNCTION || net->type_ == IVL_SCT_TASK); assert(idx < net->ports); return net->u_.port[idx]; } extern "C" ivl_nexus_t ivl_scope_mod_port(ivl_scope_t net, unsigned idx) { assert(net); assert(net->type_ == IVL_SCT_MODULE); assert(idx < net->ports); return net->u_.nex[idx]; } extern "C" unsigned ivl_scope_sigs(ivl_scope_t net) { assert(net); return net->sigs_.size(); } extern "C" ivl_signal_t ivl_scope_sig(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->sigs_.size()); return net->sigs_[idx]; } extern "C" unsigned ivl_scope_switches(ivl_scope_t net) { assert(net); return net->switches.size(); } extern "C" ivl_switch_t ivl_scope_switch(ivl_scope_t net, unsigned idx) { assert(net); assert(idx < net->switches.size()); return net->switches[idx]; } extern "C" int ivl_scope_time_precision(ivl_scope_t net) { assert(net); return net->time_precision; } extern "C" int ivl_scope_time_units(ivl_scope_t net) { assert(net); return net->time_units; } extern "C" ivl_scope_type_t ivl_scope_type(ivl_scope_t net) { assert(net); return net->type_; } extern "C" const char* ivl_scope_tname(ivl_scope_t net) { assert(net); return net->tname_; } extern "C" int ivl_signal_array_base(ivl_signal_t net) { return net->array_base; } extern "C" unsigned ivl_signal_array_count(ivl_signal_t net) { return net->array_words; } extern "C" unsigned ivl_signal_array_addr_swapped(ivl_signal_t net) { return net->array_addr_swapped; } extern "C" unsigned ivl_signal_dimensions(ivl_signal_t net) { return net->array_dimensions_; } extern "C" ivl_discipline_t ivl_signal_discipline(ivl_signal_t net) { return net->discipline; } extern "C" const char* ivl_signal_attr(ivl_signal_t net, const char*key) { if (net->nattr == 0) return 0; for (unsigned idx = 0 ; idx < net->nattr ; idx += 1) if (strcmp(key, net->attr[idx].key) == 0) return net->attr[idx].type == IVL_ATT_STR ? net->attr[idx].val.str : 0; return 0; } extern "C" unsigned ivl_signal_attr_cnt(ivl_signal_t net) { return net->nattr; } extern "C" ivl_attribute_t ivl_signal_attr_val(ivl_signal_t net, unsigned idx) { assert(idx < net->nattr); return net->attr + idx; } extern "C" const char* ivl_signal_basename(ivl_signal_t net) { return net->name_; } extern "C" const char* ivl_signal_name(ivl_signal_t net) { static char*name_buffer = 0; static unsigned name_size = 0; unsigned needlen = scope_name_len(net->scope_); needlen += strlen(net->name_) + 2; if (name_size < needlen) { name_buffer = (char*)realloc(name_buffer, needlen); name_size = needlen; } push_scope_basename(net->scope_, name_buffer); strcat(name_buffer, "."); strcat(name_buffer, net->name_); return name_buffer; } extern "C" ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word) { assert(word < net->array_words); if (net->array_words > 1) { if (net->pins) { return net->pins[word]; } else { // net->pins can be NULL for a virtualized reg array. assert(net->type_ == IVL_SIT_REG); return NULL; } } else { return net->pin; } } extern "C" unsigned ivl_signal_packed_dimensions(ivl_signal_t net) { return net->packed_dims.size(); } extern "C" int ivl_signal_packed_msb(ivl_signal_t net, unsigned dim) { assert(dim < net->packed_dims.size()); return net->packed_dims[dim].get_msb(); } extern "C" int ivl_signal_packed_lsb(ivl_signal_t net, unsigned dim) { assert(dim < net->packed_dims.size()); return net->packed_dims[dim].get_lsb(); } extern "C" int ivl_signal_msb(ivl_signal_t net) { if (net->packed_dims.empty()) return 0; assert(net->packed_dims.size() == 1); return net->packed_dims[0].get_msb(); } extern "C" int ivl_signal_lsb(ivl_signal_t net) { if (net->packed_dims.empty()) return 0; assert(net->packed_dims.size() == 1); return net->packed_dims[0].get_lsb(); } extern "C" ivl_scope_t ivl_signal_scope(ivl_signal_t net) { assert(net); return net->scope_; } extern "C" unsigned ivl_signal_width(ivl_signal_t net) { return net->net_type->packed_width(); } extern "C" ivl_signal_port_t ivl_signal_port(ivl_signal_t net) { return net->port_; } extern "C" int ivl_signal_module_port_index(ivl_signal_t net) { return net->module_port_index_; } extern "C" int ivl_signal_local(ivl_signal_t net) { return net->local_; } extern "C" int ivl_signal_signed(ivl_signal_t net) { return net->net_type->get_signed()? 1 : 0; } extern "C" unsigned ivl_signal_forced_net(ivl_signal_t net) { return net->forced_net_; } extern "C" const char* ivl_signal_file(ivl_signal_t net) { assert(net); return net->file.str(); } extern "C" unsigned ivl_signal_lineno(ivl_signal_t net) { assert(net); return net->lineno; } extern "C" int ivl_signal_integer(ivl_signal_t net) { if (const netvector_t*vec = dynamic_cast (net->net_type)) return vec->get_isint()? 1 : 0; else if (const netenum_t*enm = dynamic_cast (net->net_type)) return enm->get_isint()? 1 : 0; else return 0; } extern "C" ivl_variable_type_t ivl_signal_data_type(ivl_signal_t net) { return net->net_type->base_type(); } extern "C" ivl_type_t ivl_signal_net_type(ivl_signal_t net) { return net->net_type; } extern "C" unsigned ivl_signal_npath(ivl_signal_t net) { return net->npath; } extern "C" ivl_delaypath_t ivl_signal_path(ivl_signal_t net, unsigned idx) { assert(idx < net->npath); return net->path + idx; } extern "C" ivl_signal_type_t ivl_signal_type(ivl_signal_t net) { return net->type_; } extern "C" ivl_statement_type_t ivl_statement_type(ivl_statement_t net) { return net->type_; } extern "C" const char* ivl_stmt_file(ivl_statement_t net) { return net->file.str(); } extern "C" unsigned ivl_stmt_lineno(ivl_statement_t net) { return net->lineno; } extern "C" ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net) { switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: return net->u_.block_.scope; default: assert(0); return 0; } } extern "C" unsigned ivl_stmt_block_count(ivl_statement_t net) { switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: return net->u_.block_.nstmt_; default: assert(0); return 0; } } extern "C" ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, unsigned i) { switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: return net->u_.block_.stmt_ + i; default: assert(0); return 0; } } extern "C" ivl_scope_t ivl_stmt_call(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ALLOC: return net->u_.alloc_.scope; case IVL_ST_DISABLE: return net->u_.disable_.scope; case IVL_ST_FREE: return net->u_.free_.scope; case IVL_ST_UTASK: return net->u_.utask_.def; default: assert(0); return 0; } } extern "C" unsigned ivl_stmt_case_count(ivl_statement_t net) { switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: return net->u_.case_.ncase; default: assert(0); return 0; } } extern "C" ivl_expr_t ivl_stmt_case_expr(ivl_statement_t net, unsigned idx) { switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: assert(idx < net->u_.case_.ncase); return net->u_.case_.case_ex[idx]; default: assert(0); return 0; } } extern "C" ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned idx) { switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: assert(idx < net->u_.case_.ncase); return net->u_.case_.case_st + idx; default: assert(0); return 0; } } extern "C" ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ASSIGN_NB: return net->u_.assign_.count; case IVL_ST_CONDIT: return net->u_.condit_.cond_; case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: return net->u_.case_.cond; case IVL_ST_DO_WHILE: case IVL_ST_REPEAT: case IVL_ST_WHILE: return net->u_.while_.cond_; default: assert(0); return 0; } } extern "C" ivl_statement_t ivl_stmt_cond_false(ivl_statement_t net) { assert(net->type_ == IVL_ST_CONDIT); if (net->u_.condit_.stmt_[1].type_ == IVL_ST_NONE) return 0; else return net->u_.condit_.stmt_ + 1; } extern "C" ivl_statement_t ivl_stmt_cond_true(ivl_statement_t net) { assert(net->type_ == IVL_ST_CONDIT); if (net->u_.condit_.stmt_[0].type_ == IVL_ST_NONE) return 0; else return net->u_.condit_.stmt_ + 0; } extern "C" ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: return net->u_.assign_.delay; case IVL_ST_DELAYX: return net->u_.delayx_.expr; default: assert(0); return 0; } } extern "C" uint64_t ivl_stmt_delay_val(ivl_statement_t net) { assert(net->type_ == IVL_ST_DELAY); return net->u_.delay_.value; } extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ASSIGN_NB: return net->u_.assign_.nevent; case IVL_ST_WAIT: return net->u_.wait_.nevent; case IVL_ST_TRIGGER: return 1; default: assert(0); } return 0; } extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx) { switch (net->type_) { case IVL_ST_ASSIGN_NB: assert(idx < net->u_.assign_.nevent); if (net->u_.assign_.nevent == 1) return net->u_.assign_.event; else return net->u_.assign_.events[idx]; case IVL_ST_WAIT: assert(idx < net->u_.wait_.nevent); if (net->u_.wait_.nevent == 1) return net->u_.wait_.event; else return net->u_.wait_.events[idx]; case IVL_ST_TRIGGER: assert(idx == 0); return net->u_.wait_.event; default: assert(0); } return 0; } extern "C" ivl_expr_t ivl_stmt_lexp(ivl_statement_t net) { switch (net->type_) { case IVL_ST_CONTRIB: return net->u_.contrib_.lval; default: assert(0); } return 0; } extern "C" ivl_lval_t ivl_stmt_lval(ivl_statement_t net, unsigned idx) { switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: case IVL_ST_CASSIGN: case IVL_ST_DEASSIGN: case IVL_ST_FORCE: case IVL_ST_RELEASE: assert(idx < net->u_.assign_.lvals_); return net->u_.assign_.lval_ + idx; default: assert(0); } return 0; } extern "C" unsigned ivl_stmt_lvals(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: case IVL_ST_CASSIGN: case IVL_ST_DEASSIGN: case IVL_ST_FORCE: case IVL_ST_RELEASE: return net->u_.assign_.lvals_; default: assert(0); } return 0; } extern "C" unsigned ivl_stmt_lwidth(ivl_statement_t net) { assert((net->type_ == IVL_ST_ASSIGN) || (net->type_ == IVL_ST_ASSIGN_NB) || (net->type_ == IVL_ST_CASSIGN) || (net->type_ == IVL_ST_DEASSIGN) || (net->type_ == IVL_ST_FORCE) || (net->type_ == IVL_ST_RELEASE)); unsigned sum = 0; unsigned nlvals; struct ivl_lval_s*lvals; nlvals = net->u_.assign_.lvals_; lvals = net->u_.assign_.lval_; for (unsigned idx = 0 ; idx < nlvals ; idx += 1) { ivl_lval_t cur = lvals + idx; switch(cur->type_) { case IVL_LVAL_REG: case IVL_LVAL_ARR: case IVL_LVAL_LVAL: sum += ivl_lval_width(cur); break; default: assert(0); } } return sum; } extern "C" const char* ivl_stmt_name(ivl_statement_t net) { switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.name_; default: assert(0); } return 0; } extern "C" char ivl_stmt_opcode(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ASSIGN: return net->u_.assign_.oper; default: assert(0); } return 0; } extern "C" ivl_expr_t ivl_stmt_parm(ivl_statement_t net, unsigned idx) { switch (net->type_) { case IVL_ST_STASK: assert(idx < net->u_.stask_.nparm_); return net->u_.stask_.parms_[idx]; default: assert(0); } return 0; } extern "C" unsigned ivl_stmt_parm_count(ivl_statement_t net) { switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.nparm_; default: assert(0); } return 0; } extern "C" ivl_expr_t ivl_stmt_rval(ivl_statement_t net) { switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: case IVL_ST_CASSIGN: case IVL_ST_FORCE: return net->u_.assign_.rval_; case IVL_ST_CONTRIB: return net->u_.contrib_.rval; default: assert(0); } return 0; } extern "C" ivl_sfunc_as_task_t ivl_stmt_sfunc_as_task(ivl_statement_t net) { switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.sfunc_as_task_; default: assert(0); } return IVL_SFUNC_AS_TASK_ERROR; } extern "C" ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net) { switch (net->type_) { case IVL_ST_DELAY: return net->u_.delay_.stmt_; case IVL_ST_DELAYX: return net->u_.delayx_.stmt_; case IVL_ST_FOREVER: return net->u_.forever_.stmt_; case IVL_ST_WAIT: return net->u_.wait_.stmt_; case IVL_ST_DO_WHILE: case IVL_ST_REPEAT: case IVL_ST_WHILE: return net->u_.while_.stmt_; default: assert(0); } return 0; } extern "C" const char*ivl_switch_basename(ivl_switch_t net) { return net->name; } extern "C" ivl_scope_t ivl_switch_scope(ivl_switch_t net) { return net->scope; } extern "C" ivl_switch_type_t ivl_switch_type(ivl_switch_t net) { return net->type; } extern "C" ivl_nexus_t ivl_switch_a(ivl_switch_t net) { return net->pins[0]; } extern "C" ivl_nexus_t ivl_switch_b(ivl_switch_t net) { return net->pins[1]; } extern "C" ivl_nexus_t ivl_switch_enable(ivl_switch_t net) { return net->pins[2]; } extern "C" unsigned ivl_switch_width(ivl_switch_t net) { return net->width; } extern "C" unsigned ivl_switch_part(ivl_switch_t net) { return net->part; } extern "C" unsigned ivl_switch_offset(ivl_switch_t net) { return net->offset; } extern "C" ivl_expr_t ivl_switch_delay(ivl_switch_t net, unsigned transition) { assert(transition < 3); return net->delay[transition]; } extern "C" const char* ivl_switch_file(ivl_switch_t net) { return net->file; } extern "C" ivl_island_t ivl_switch_island(ivl_switch_t net) { return net->island; } extern "C" unsigned ivl_switch_lineno(ivl_switch_t net) { return net->lineno; } extern "C" ivl_variable_type_t ivl_type_base(ivl_type_t net) { if (net == 0) return IVL_VT_NO_TYPE; else return net->base_type(); } extern "C" ivl_type_t ivl_type_element(ivl_type_t net) { if (const netarray_t*da = dynamic_cast (net)) return da->element_type(); assert(0); return 0; } extern "C" unsigned ivl_type_packed_dimensions(ivl_type_t net) { vector slice = net->slice_dimensions(); return slice.size(); } extern "C" int ivl_type_packed_lsb(ivl_type_t net, unsigned dim) { vector slice = net->slice_dimensions(); assert(dim < slice.size()); return slice[dim].get_lsb(); } extern "C" int ivl_type_packed_msb(ivl_type_t net, unsigned dim) { vector slice = net->slice_dimensions(); assert(dim < slice.size()); return slice[dim].get_msb(); } extern "C" const char* ivl_type_name(ivl_type_t net) { if (const netclass_t*class_type = dynamic_cast(net)) { return class_type->get_name(); } return 0; } extern "C" int ivl_type_properties(ivl_type_t net) { const netclass_t*class_type = dynamic_cast(net); assert(class_type); return class_type->get_properties(); } extern "C" const char* ivl_type_prop_name(ivl_type_t net, int idx) { if (idx < 0) return 0; const netclass_t*class_type = dynamic_cast(net); assert(class_type); return class_type->get_prop_name(idx); } extern "C" ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx) { const netclass_t*class_type = dynamic_cast(net); assert(class_type); return class_type->get_prop_type(idx); } extern "C" int ivl_type_signed(ivl_type_t net) { return net->get_signed()? 1 : 0; } iverilog-10_1/t-dll-expr.cc000066400000000000000000000470471265551621300156550ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include "t-dll.h" # include "netlist.h" # include "netclass.h" # include # include # include "ivl_alloc.h" # include "ivl_assert.h" /* * This is a little convenience function for converting a NetExpr * expression type to the expression type used by ivl_expr_t objects. */ static ivl_variable_type_t get_expr_type(const NetExpr*net) { return net->expr_type(); } /* * These methods implement the expression scan that generates the * ivl_expr_t representing the expression. Each method leaves the * expr_ member filled with the ivl_expr_t that represents it. Each * method expects that the expr_ member empty (0) when it starts. */ /* * This function takes an expression in the expr_ member that is * already built up, and adds a subtraction of the given constant. */ void dll_target::sub_off_from_expr_(long off) { assert(expr_ != 0); char*bits; ivl_expr_t tmpc = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); tmpc->type_ = IVL_EX_NUMBER; tmpc->value_ = IVL_VT_VECTOR; tmpc->net_type= 0; tmpc->width_ = expr_->width_; tmpc->signed_ = expr_->signed_; tmpc->sized_ = 1; tmpc->u_.number_.bits_ = bits = (char*)malloc(tmpc->width_); for (unsigned idx = 0 ; idx < tmpc->width_ ; idx += 1) { bits[idx] = (off & 1)? '1' : '0'; off >>= 1; } /* Now make the subtractor (x-4 in the above example) that has as input A the index expression and input B the constant to subtract. */ ivl_expr_t tmps = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); tmps->type_ = IVL_EX_BINARY; tmps->value_ = IVL_VT_VECTOR; tmps->net_type= 0; tmps->width_ = tmpc->width_; tmps->signed_ = tmpc->signed_; tmps->sized_ = 1; tmps->u_.binary_.op_ = '-'; tmps->u_.binary_.lef_ = expr_; tmps->u_.binary_.rig_ = tmpc; /* Replace (x) with (x-off) */ expr_ = tmps; } void dll_target::mul_expr_by_const_(long val) { assert(expr_ != 0); char*bits; ivl_expr_t tmpc = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); tmpc->type_ = IVL_EX_NUMBER; tmpc->value_ = IVL_VT_VECTOR; tmpc->net_type= 0; tmpc->width_ = expr_->width_; tmpc->signed_ = expr_->signed_; tmpc->sized_ = 1; tmpc->u_.number_.bits_ = bits = (char*)malloc(tmpc->width_); for (unsigned idx = 0 ; idx < tmpc->width_ ; idx += 1) { bits[idx] = (val & 1)? '1' : '0'; val >>= 1; } /* Now make the subtractor (x-4 in the above example) that has as input A the index expression and input B the constant to subtract. */ ivl_expr_t tmps = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); tmps->type_ = IVL_EX_BINARY; tmps->value_ = IVL_VT_VECTOR; tmpc->net_type= 0; tmps->width_ = tmpc->width_; tmps->signed_ = tmpc->signed_; tmps->sized_ = 1; tmps->u_.binary_.op_ = '*'; tmps->u_.binary_.lef_ = expr_; tmps->u_.binary_.rig_ = tmpc; /* Replace (x) with (x*valf) */ expr_ = tmps; } ivl_expr_t dll_target::expr_from_value_(const verinum&val) { ivl_expr_t expr = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); unsigned idx; char*bits; expr->type_ = IVL_EX_NUMBER; expr->value_= IVL_VT_VECTOR; expr->net_type=0; expr->width_= val.len(); expr->signed_ = val.has_sign()? 1 : 0; expr->sized_= 1; expr->u_.number_.bits_ = bits = (char*)malloc(expr->width_ + 1); for (idx = 0 ; idx < expr->width_ ; idx += 1) switch (val.get(idx)) { case verinum::V0: bits[idx] = '0'; break; case verinum::V1: bits[idx] = '1'; break; case verinum::Vx: bits[idx] = 'x'; break; case verinum::Vz: bits[idx] = 'z'; break; default: assert(0); } bits[expr->width_] = 0; return expr; } void dll_target::expr_access_func(const NetEAccess*net) { assert(expr_ == 0); // Make a stub Branch Access Function expression node. expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_BACCESS; expr_->value_ = IVL_VT_REAL; expr_->net_type=0; expr_->width_ = 1; expr_->signed_= 1; expr_->sized_ = 1; FILE_NAME(expr_, net); expr_->u_.branch_.branch = net->get_branch()->target_obj(); expr_->u_.branch_.nature = net->get_nature(); } void dll_target::expr_array_pattern(const NetEArrayPattern*net) { assert(expr_ == 0); ivl_expr_t expr_tmp = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_tmp->type_ = IVL_EX_ARRAY_PATTERN; expr_tmp->value_= net->expr_type(); expr_tmp->net_type = net->net_type(); expr_tmp->width_ = 1; expr_tmp->signed_ = 0; expr_tmp->sized_ = 0; FILE_NAME(expr_tmp, net); expr_tmp->u_.array_pattern_.parms = net->item_size(); expr_tmp->u_.array_pattern_.parm = new ivl_expr_t [net->item_size()]; for (size_t idx = 0 ; idx < net->item_size() ; idx += 1) { const NetExpr*tmp = net->item(idx); tmp->expr_scan(this); expr_tmp->u_.array_pattern_.parm[idx] = expr_; expr_ = 0; } expr_ = expr_tmp; } void dll_target::expr_binary(const NetEBinary*net) { assert(expr_ == 0); net->left()->expr_scan(this); ivl_expr_t left = expr_; expr_ = 0; net->right()->expr_scan(this); ivl_expr_t rght = expr_; expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_BINARY; expr_->value_= get_expr_type(net); expr_->net_type=0; expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; expr_->sized_= 1; FILE_NAME(expr_, net); expr_->u_.binary_.op_ = net->op(); expr_->u_.binary_.lef_ = left; expr_->u_.binary_.rig_ = rght; } void dll_target::expr_concat(const NetEConcat*net) { assert(expr_ == 0); ivl_expr_t cur = new struct ivl_expr_s; assert(cur); cur->type_ = IVL_EX_CONCAT; cur->value_ = net->expr_type(); cur->net_type=0; cur->width_ = net->expr_width(); cur->signed_ = net->has_sign() ? 1 : 0; cur->sized_ = 1; FILE_NAME(cur, net); cur->u_.concat_.rept = net->repeat(); cur->u_.concat_.parms = net->nparms(); cur->u_.concat_.parm = new ivl_expr_t [net->nparms()]; for (unsigned idx = 0 ; idx < net->nparms() ; idx += 1) { expr_ = 0; net->parm(idx)->expr_scan(this); assert(expr_); cur->u_.concat_.parm[idx] = expr_; } expr_ = cur; } void dll_target::expr_const(const NetEConst*net) { assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->value_= net->expr_type(); expr_->net_type=0; FILE_NAME(expr_, net); if (net->value().is_string()) { expr_->type_ = IVL_EX_STRING; expr_->width_= net->expr_width(); expr_->u_.string_.value_ =strdup(net->value().as_string().c_str()); } else { verinum val = net->value(); unsigned idx; char*bits; expr_->type_ = IVL_EX_NUMBER; expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; expr_->sized_= net->has_width()? 1 : 0; expr_->u_.number_.bits_ = bits = (char*)malloc(expr_->width_); for (idx = 0 ; idx < expr_->width_ ; idx += 1) switch (val.get(idx)) { case verinum::V0: bits[idx] = '0'; break; case verinum::V1: bits[idx] = '1'; break; case verinum::Vx: bits[idx] = 'x'; break; case verinum::Vz: bits[idx] = 'z'; break; default: assert(0); } } } void dll_target::expr_param(const NetEConstParam*net) { ivl_scope_t scop = find_scope(des_, net->scope()); ivl_parameter_t par = scope_find_param(scop, net->name()); if (par == 0) { cerr << net->get_fileline() << ": internal error: " << "Parameter " << net->name() << " missing from " << ivl_scope_name(scop) << endl; } assert(par); expr_const(net); expr_->u_.string_.parameter = par; } void dll_target::expr_rparam(const NetECRealParam*net) { ivl_scope_t scop = find_scope(des_, net->scope()); ivl_parameter_t par = scope_find_param(scop, net->name()); if (par == 0) { cerr << net->get_fileline() << ": internal error: " << "Parameter " << net->name() << " missing from " << ivl_scope_name(scop) << endl; } assert(par); assert(par->value); expr_ = par->value; } void dll_target::expr_creal(const NetECReal*net) { assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->width_ = net->expr_width(); expr_->signed_ = 1; expr_->sized_ = 1; expr_->type_ = IVL_EX_REALNUM; FILE_NAME(expr_, net); expr_->value_= IVL_VT_REAL; expr_->net_type=0; expr_->u_.real_.value = net->value().as_double(); } void dll_target::expr_last(const NetELast*net) { assert(expr_ == 0); ivl_expr_t expr = new struct ivl_expr_s; expr->type_ = IVL_EX_SFUNC; expr->value_ = IVL_VT_LOGIC; expr->width_ = 32; expr->signed_ = 1; expr->sized_ = 1; expr->net_type = 0; FILE_NAME(expr, net); expr->u_.sfunc_.name_ = "$high"; ivl_signal_t sig = find_signal(des_, net->sig()); ivl_expr_t esig = new struct ivl_expr_s; esig->type_ = IVL_EX_SIGNAL; esig->value_ = IVL_VT_DARRAY; esig->net_type= sig->net_type; esig->width_ = 1; esig->signed_ = sig->net_type->get_signed()? 1 : 0; FILE_NAME(esig, net); esig->u_.signal_.word = 0; esig->u_.signal_.sig = sig; expr->u_.sfunc_.parms = 1; expr->u_.sfunc_.parm = new ivl_expr_t[1]; expr->u_.sfunc_.parm[0] = esig; expr_ = expr; } void dll_target::expr_new(const NetENew*net) { ivl_expr_t size = 0; ivl_expr_t init_val = 0; if (net->size_expr()) { net->size_expr()->expr_scan(this); size = expr_; expr_ = 0; } if (net->init_expr()) { net->init_expr()->expr_scan(this); init_val = expr_; expr_ = 0; } assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->width_ = net->expr_width(); expr_->signed_ = 0; expr_->sized_ = 1; expr_->type_ = IVL_EX_NEW; FILE_NAME(expr_, net); expr_->value_ = net->expr_type(); // May be IVL_VT_DARRAY or _CLASS expr_->net_type= net->get_type(); expr_->u_.new_.size = size; expr_->u_.new_.init_val = init_val; } void dll_target::expr_null(const NetENull*net) { assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->width_ = net->expr_width(); expr_->signed_ = 0; expr_->sized_ = 1; expr_->type_ = IVL_EX_NULL; FILE_NAME(expr_, net); expr_->value_ = IVL_VT_CLASS; expr_->net_type= 0; } void dll_target::expr_property(const NetEProperty*net) { ivl_expr_t index = 0; if (const NetExpr*index_expr = net->get_index()) { index_expr->expr_scan(this); index = expr_; expr_ = 0; } assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->width_ = net->expr_width(); expr_->signed_ = net->has_sign(); expr_->sized_ = 1; expr_->type_ = IVL_EX_PROPERTY; FILE_NAME(expr_, net); expr_->value_ = net->expr_type(); expr_->net_type= net->net_type(); expr_->u_.property_.sig = find_signal(des_, net->get_sig()); expr_->u_.property_.prop_idx = net->property_idx(); expr_->u_.property_.index = index; } void dll_target::expr_event(const NetEEvent*net) { assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_EVENT; FILE_NAME(expr_, net); expr_->value_= IVL_VT_VOID; expr_->net_type=0; /* Locate the event by name. Save the ivl_event_t in the expression so that the generator can find it easily. */ const NetEvent*ev = net->event(); ivl_scope_t ev_scope = lookup_scope_(ev->scope()); for (unsigned idx = 0 ; idx < ev_scope->nevent_ ; idx += 1) { const char*ename = ivl_event_basename(ev_scope->event_[idx]); if (strcmp(ev->name(), ename) == 0) { expr_->u_.event_.event = ev_scope->event_[idx]; break; } } } void dll_target::expr_scope(const NetEScope*net) { assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_SCOPE; FILE_NAME(expr_, net); expr_->value_= IVL_VT_VOID; expr_->net_type=0; expr_->u_.scope_.scope = lookup_scope_(net->scope()); } void dll_target::expr_scopy(const NetEShallowCopy*net) { assert(expr_ == 0); net->expr_scan_oper1(this); ivl_expr_t expr1 = expr_; expr_ = 0; net->expr_scan_oper2(this); ivl_expr_t expr2 = expr_; expr_ = 0; expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_SHALLOWCOPY; FILE_NAME(expr_, net); expr_->value_ = net->expr_type(); expr_->net_type = net->net_type(); expr_->u_.shallow_.dest = expr1; expr_->u_.shallow_.src = expr2; } void dll_target::expr_netenum(const NetENetenum*net) { assert(expr_ == 0); expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_ENUMTYPE; FILE_NAME(expr_, net); expr_->value_= IVL_VT_VOID; expr_->net_type=0; expr_->u_.enumtype_.type = net->netenum(); } void dll_target::expr_select(const NetESelect*net) { assert(expr_ == 0); net->sub_expr()->expr_scan(this); ivl_expr_t expr = expr_; expr_ = 0; if (net->select()) net->select()->expr_scan(this); ivl_expr_t base = expr_; expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_SELECT; expr_->value_= net->expr_type(); expr_->net_type=0; expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; expr_->sized_= 1; FILE_NAME(expr_, net); expr_->u_.select_.sel_type_ = net->select_type(); expr_->u_.select_.expr_ = expr; expr_->u_.select_.base_ = base; } void dll_target::expr_sfunc(const NetESFunc*net) { assert(expr_ == 0); ivl_expr_t expr = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr->type_ = IVL_EX_SFUNC; FILE_NAME(expr, net); expr->value_= net->expr_type(); expr->net_type=net->net_type(); expr->width_= net->expr_width(); expr->signed_ = net->has_sign()? 1 : 0; expr->sized_= 1; FILE_NAME(expr, net); /* system function names are lex_strings strings. */ expr->u_.sfunc_.name_ = net->name(); unsigned cnt = net->nparms(); expr->u_.sfunc_.parms = cnt; expr->u_.sfunc_.parm = new ivl_expr_t[cnt]; /* make up the parameter expressions. */ for (unsigned idx = 0 ; idx < cnt ; idx += 1) { net->parm(idx)->expr_scan(this); assert(expr_); expr->u_.sfunc_.parm[idx] = expr_; expr_ = 0; } expr_ = expr; } void dll_target::expr_ternary(const NetETernary*net) { assert(expr_ == 0); ivl_expr_t expr = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr->type_ = IVL_EX_TERNARY; expr->value_= net->expr_type(); expr->net_type=0; expr->width_ = net->expr_width(); expr->signed_ = net->has_sign()? 1 : 0; expr->sized_ = 1; FILE_NAME(expr, net); net->cond_expr()->expr_scan(this); assert(expr_); expr->u_.ternary_.cond = expr_; expr_ = 0; net->true_expr()->expr_scan(this); assert(expr_); expr->u_.ternary_.true_e = expr_; expr_ = 0; net->false_expr()->expr_scan(this); assert(expr_); expr->u_.ternary_.false_e = expr_; expr_ = expr; } void dll_target::expr_signal(const NetESignal*net) { ivl_signal_t sig = find_signal(des_, net->sig()); assert(expr_ == 0); /* If there is a word expression, generate it. */ ivl_expr_t word_expr = 0; if (const NetExpr*word = net->word_index()) { word->expr_scan(this); assert(expr_); word_expr = expr_; expr_ = 0; } expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_SIGNAL; expr_->value_= net->expr_type(); expr_->net_type=0; expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; expr_->sized_= 1; FILE_NAME(expr_, net); expr_->u_.signal_.word = word_expr; expr_->u_.signal_.sig = sig; /* Make account for the special case that this is a reference to an array as a whole. We detect this case by noting that this is an array (more than 0 array dimensions) and that there is no word select expression. For this case, we have an IVL_EX_ARRAY expression instead of a SIGNAL expression. */ if (sig->array_dimensions_ > 0 && word_expr == 0) { expr_->type_ = IVL_EX_ARRAY; expr_->width_ = 0; // Doesn't make much sense for arrays. } } void dll_target::expr_ufunc(const NetEUFunc*net) { assert(expr_ == 0); ivl_expr_t expr = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr->type_ = IVL_EX_UFUNC; expr->value_= net->expr_type(); expr->net_type=0; expr->width_= net->expr_width(); expr->signed_ = net->has_sign()? 1 : 0; expr->sized_= 1; FILE_NAME(expr, net); expr->u_.ufunc_.def = lookup_scope_(net->func()); if (expr->u_.ufunc_.def == 0) { cerr << net->get_fileline() << ": internal error: " << "dll_target::expr_ufunc: " << "Unable to match scope " << scope_path(net->func()) << endl; } ivl_assert(*net, expr->u_.ufunc_.def); ivl_assert(*net, expr->u_.ufunc_.def->type_ == IVL_SCT_FUNCTION); unsigned cnt = net->parm_count(); expr->u_.ufunc_.parms = cnt; expr->u_.ufunc_.parm = new ivl_expr_t[cnt]; /* make up the parameter expressions. */ for (unsigned idx = 0 ; idx < cnt ; idx += 1) { net->parm(idx)->expr_scan(this); assert(expr_); expr->u_.ufunc_.parm[idx] = expr_; expr_ = 0; } expr_ = expr; } void dll_target::expr_unary(const NetEUnary*net) { assert(expr_ == 0); net->expr()->expr_scan(this); assert(expr_); ivl_expr_t sub = expr_; expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->type_ = IVL_EX_UNARY; expr_->value_= net->expr_type(); expr_->net_type=0; expr_->width_ = net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; expr_->sized_ = 1; FILE_NAME(expr_, net); expr_->u_.unary_.op_ = net->op(); expr_->u_.unary_.sub_ = sub; } iverilog-10_1/t-dll-proc.cc000066400000000000000000000636051265551621300156400ustar00rootroot00000000000000/* * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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.will need a Picture Elements Binary Software * 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 GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include "target.h" # include "ivl_target.h" # include "compiler.h" # include "t-dll.h" # include "netclass.h" # include # include "ivl_alloc.h" # include "ivl_assert.h" bool dll_target::process(const NetProcTop*net) { bool rc_flag = true; ivl_process_t obj = (struct ivl_process_s*) calloc(1, sizeof(struct ivl_process_s)); obj->type_ = net->type(); obj->analog_flag = 0; FILE_NAME(obj, net); /* Save the scope of the process. */ obj->scope_ = lookup_scope_(net->scope()); obj->nattr = net->attr_cnt(); obj->attr = fill_in_attributes(net); /* This little bit causes the process to be completely generated so that it can be passed to the DLL. The stmt_cur_ member is used to hold a pointer to the current statement in progress, and the emit_proc() method fills in that object. We know a few things about the current statement: we are not in the middle of one, and when we are done, we have our statement back. The asserts check these conditions. */ assert(stmt_cur_ == 0); stmt_cur_ = (struct ivl_statement_s*)calloc(1, sizeof*stmt_cur_); rc_flag = net->statement()->emit_proc(this) && rc_flag; assert(stmt_cur_); obj->stmt_ = stmt_cur_; stmt_cur_ = 0; /* Save the process in the design. */ obj->next_ = des_.threads_; des_.threads_ = obj; return rc_flag; } void dll_target::task_def(const NetScope*net) { ivl_scope_t scop = lookup_scope_(net); const NetTaskDef*def = net->task_def(); assert(def); assert(def->proc()); assert(stmt_cur_ == 0); stmt_cur_ = (struct ivl_statement_s*)calloc(1, sizeof*stmt_cur_); def->proc()->emit_proc(this); assert(stmt_cur_); scop->def = stmt_cur_; stmt_cur_ = 0; scop->ports = def->port_count(); if (scop->ports > 0) { scop->u_.port = new ivl_signal_t[scop->ports]; for (unsigned idx = 0 ; idx < scop->ports ; idx += 1) scop->u_.port[idx] = find_signal(des_, def->port(idx)); } } bool dll_target::func_def(const NetScope*net) { ivl_scope_t scop = lookup_scope_(net); const NetFuncDef*def = net->func_def(); assert(def); assert(def->proc()); assert(stmt_cur_ == 0); stmt_cur_ = (struct ivl_statement_s*)calloc(1, sizeof*stmt_cur_); def->proc()->emit_proc(this); assert(stmt_cur_); scop->def = stmt_cur_; stmt_cur_ = 0; scop->ports = def->port_count() + 1; if (scop->ports > 0) { scop->u_.port = new ivl_signal_t[scop->ports]; for (unsigned idx = 1 ; idx < scop->ports ; idx += 1) scop->u_.port[idx] = find_signal(des_, def->port(idx-1)); } /* FIXME: the ivl_target API expects port-0 to be the output port. This assumes that the return value is a signal, which is *not* correct. Someday, I'm going to have to change this, but that will break code generators that use this result. */ if (const NetNet*ret_sig = def->return_sig()) scop->u_.port[0] = find_signal(des_, ret_sig); else scop->u_.port[0] = 0; /* If there is no return value, then this is a void function. */ return true; } /* * This private function makes the assignment lvals for the various * kinds of assignment statements. */ bool dll_target::make_assign_lvals_(const NetAssignBase*net) { bool flag = true; assert(stmt_cur_); unsigned cnt = net->l_val_count(); stmt_cur_->u_.assign_.lvals_ = cnt; stmt_cur_->u_.assign_.lval_ = new struct ivl_lval_s[cnt]; stmt_cur_->u_.assign_.delay = 0; for (unsigned idx = 0 ; idx < cnt ; idx += 1) { struct ivl_lval_s*cur = stmt_cur_->u_.assign_.lval_ + idx; const NetAssign_*asn = net->l_val(idx); flag &= make_single_lval_(net, cur, asn); } return flag; } bool dll_target::make_single_lval_(const LineInfo*li, struct ivl_lval_s*cur, const NetAssign_*asn) { bool flag = true; const NetExpr*loff = asn->get_base(); if (loff == 0) { cur->loff = 0; cur->sel_type = IVL_SEL_OTHER; } else { loff->expr_scan(this); cur->loff = expr_; cur->sel_type = asn->select_type(); expr_ = 0; } cur->width_ = asn->lwidth(); ivl_type_t nest_type = 0; if (asn->sig()) { cur->type_ = IVL_LVAL_REG; cur->n.sig = find_signal(des_, asn->sig()); } else { const NetAssign_*asn_nest = asn->nest(); ivl_assert(*li, asn_nest); nest_type = asn_nest->net_type(); struct ivl_lval_s*cur_nest = new struct ivl_lval_s; make_single_lval_(li, cur_nest, asn_nest); cur->type_ = IVL_LVAL_LVAL; cur->n.nest = cur_nest; } cur->idx = 0; // If there is a word select expression, it is // really an array index. Note that the word index // expression is already converted to canonical // form by elaboration. if (asn->word()) { assert(expr_ == 0); asn->word()->expr_scan(this); cur->type_ = IVL_LVAL_ARR; cur->idx = expr_; expr_ = 0; } cur->property_idx = -1; perm_string pname = asn->get_property(); if (!pname.nil()) { const netclass_t*use_type; switch (cur->type_) { case IVL_LVAL_LVAL: assert(nest_type); use_type = dynamic_cast (nest_type); break; default: use_type = dynamic_cast (cur->n.sig->net_type); break; } assert(use_type); cur->property_idx = use_type->property_idx_from_name(pname); } return flag; } void dll_target::proc_alloc(const NetAlloc*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_ALLOC; stmt_cur_->u_.alloc_.scope = lookup_scope_(net->scope()); } /* */ bool dll_target::proc_assign(const NetAssign*net) { bool flag = true; assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); stmt_cur_->type_ = IVL_ST_ASSIGN; FILE_NAME(stmt_cur_, net); stmt_cur_->u_.assign_.delay = 0; /* Make the lval fields. */ flag &= make_assign_lvals_(net); stmt_cur_->u_.assign_.oper = net->assign_operator(); assert(expr_ == 0); net->rval()->expr_scan(this); stmt_cur_->u_.assign_.rval_ = expr_; expr_ = 0; const NetExpr*del = net->get_delay(); if (del) { del->expr_scan(this); stmt_cur_->u_.assign_.delay = expr_; expr_ = 0; } return flag; } void dll_target::proc_assign_nb(const NetAssignNB*net) { const NetExpr* delay_exp = net->get_delay(); const NetExpr* cnt_exp = net->get_count(); assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); stmt_cur_->type_ = IVL_ST_ASSIGN_NB; FILE_NAME(stmt_cur_, net); stmt_cur_->u_.assign_.delay = 0; stmt_cur_->u_.assign_.count = 0; stmt_cur_->u_.assign_.nevent = 0; /* Make the lval fields. */ make_assign_lvals_(net); /* Make the rval field. */ assert(expr_ == 0); net->rval()->expr_scan(this); stmt_cur_->u_.assign_.rval_ = expr_; expr_ = 0; /* Process a delay if it exists. */ if (const NetEConst*delay_num = dynamic_cast(delay_exp)) { verinum val = delay_num->value(); ivl_expr_t de = new struct ivl_expr_s; de->type_ = IVL_EX_DELAY; de->width_ = 8 * sizeof(uint64_t); de->signed_ = 0; de->u_.delay_.value = val.as_ulong64(); stmt_cur_->u_.assign_.delay = de; } else if (delay_exp != 0) { delay_exp->expr_scan(this); stmt_cur_->u_.assign_.delay = expr_; expr_ = 0; } /* Process a count if it exists. */ if (const NetEConst*cnt_num = dynamic_cast(cnt_exp)) { verinum val = cnt_num->value(); ivl_expr_t cnt = new struct ivl_expr_s; cnt->type_ = IVL_EX_ULONG; cnt->width_ = 8 * sizeof(unsigned long); cnt->signed_ = 0; cnt->u_.ulong_.value = val.as_ulong(); stmt_cur_->u_.assign_.count = cnt; } else if (cnt_exp != 0) { cnt_exp->expr_scan(this); stmt_cur_->u_.assign_.count = expr_; expr_ = 0; } /* Process the events if they exist. This is a copy of code * from NetEvWait below. */ if (net->nevents() > 0) { stmt_cur_->u_.assign_.nevent = net->nevents(); if (net->nevents() > 1) { stmt_cur_->u_.assign_.events = (ivl_event_t*) calloc(net->nevents(), sizeof(ivl_event_t*)); } for (unsigned edx = 0 ; edx < net->nevents() ; edx += 1) { /* Locate the event by name. Save the ivl_event_t in the statement so that the generator can find it easily. */ const NetEvent*ev = net->event(edx); ivl_scope_t ev_scope = lookup_scope_(ev->scope()); ivl_event_t ev_tmp=0; assert(ev_scope); assert(ev_scope->nevent_ > 0); for (unsigned idx = 0; idx < ev_scope->nevent_; idx += 1) { const char*ename = ivl_event_basename(ev_scope->event_[idx]); if (strcmp(ev->name(), ename) == 0) { ev_tmp = ev_scope->event_[idx]; break; } } // XXX should we assert(ev_tmp)? if (net->nevents() == 1) stmt_cur_->u_.assign_.event = ev_tmp; else stmt_cur_->u_.assign_.events[edx] = ev_tmp; /* If this is an event with a probe, then connect up the pins. This wasn't done during the ::event method because the signals weren't scanned yet. */ if (ev->nprobe() >= 1) { unsigned iany = 0; unsigned ineg = ev_tmp->nany; unsigned ipos = ineg + ev_tmp->nneg; for (unsigned idx = 0; idx < ev->nprobe(); idx += 1) { const NetEvProbe*pr = ev->probe(idx); unsigned base = 0; switch (pr->edge()) { case NetEvProbe::ANYEDGE: base = iany; iany += pr->pin_count(); break; case NetEvProbe::NEGEDGE: base = ineg; ineg += pr->pin_count(); break; case NetEvProbe::POSEDGE: base = ipos; ipos += pr->pin_count(); break; } for (unsigned bit = 0; bit < pr->pin_count(); bit += 1) { ivl_nexus_t nex = (ivl_nexus_t) pr->pin(bit).nexus()->t_cookie(); assert(nex); ev_tmp->pins[base+bit] = nex; } } } } } } bool dll_target::proc_block(const NetBlock*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); /* First, count the statements in the block. */ unsigned count = 0; for (const NetProc*cur = net->proc_first() ; cur ; cur = net->proc_next(cur)) count += 1; /* If the block has no statements, then turn it into a no-op */ if (count == 0) { stmt_cur_->type_ = IVL_ST_NOOP; return true; } /* If there is exactly one statement, there is no need for the block wrapper, generate the contained statement instead. */ if ((count == 1) && (net->subscope() == 0)) { return net->proc_first()->emit_proc(this); } /* Handle the general case. The block has some statements in it, so fill in the block fields of the existing statement, and generate the contents for the statement array. */ switch (net->type()) { case NetBlock::SEQU: stmt_cur_->type_ = IVL_ST_BLOCK; break; case NetBlock::PARA: stmt_cur_->type_ = IVL_ST_FORK; break; case NetBlock::PARA_JOIN_ANY: stmt_cur_->type_ = IVL_ST_FORK_JOIN_ANY; break; case NetBlock::PARA_JOIN_NONE: stmt_cur_->type_ = IVL_ST_FORK_JOIN_NONE; break; } stmt_cur_->u_.block_.nstmt_ = count; stmt_cur_->u_.block_.stmt_ = (struct ivl_statement_s*) calloc(count, sizeof(struct ivl_statement_s)); if (net->subscope()) stmt_cur_->u_.block_.scope = lookup_scope_(net->subscope()); else stmt_cur_->u_.block_.scope = 0; struct ivl_statement_s*save_cur_ = stmt_cur_; unsigned idx = 0; bool flag = true; for (const NetProc*cur = net->proc_first() ; cur ; cur = net->proc_next(cur), idx += 1) { assert(idx < count); stmt_cur_ = save_cur_->u_.block_.stmt_ + idx; bool rc = cur->emit_proc(this); flag = flag && rc; } assert(idx == count); stmt_cur_ = save_cur_; return flag; } /* * A case statement is in turn an array of statements with gate * expressions. This builds arrays of the right size and builds the * ivl_expr_t and ivl_statement_s arrays for the substatements. */ void dll_target::proc_case(const NetCase*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); switch (net->type()) { case NetCase::EQ: stmt_cur_->type_ = IVL_ST_CASE; break; case NetCase::EQX: stmt_cur_->type_ = IVL_ST_CASEX; break; case NetCase::EQZ: stmt_cur_->type_ = IVL_ST_CASEZ; break; } assert(stmt_cur_->type_ != IVL_ST_NONE); assert(expr_ == 0); assert(net->expr()); net->expr()->expr_scan(this); stmt_cur_->u_.case_.cond = expr_; expr_ = 0; /* If the condition expression is a real valued expression, then change the case statement to a CASER statement. */ if (stmt_cur_->u_.case_.cond->value_ == IVL_VT_REAL) stmt_cur_->type_ = IVL_ST_CASER; unsigned ncase = net->nitems(); stmt_cur_->u_.case_.ncase = ncase; stmt_cur_->u_.case_.case_ex = new ivl_expr_t[ncase]; stmt_cur_->u_.case_.case_st = new struct ivl_statement_s[ncase]; ivl_statement_t save_cur = stmt_cur_; for (unsigned idx = 0 ; idx < ncase ; idx += 1) { const NetExpr*ex = net->expr(idx); if (ex) { ex->expr_scan(this); save_cur->u_.case_.case_ex[idx] = expr_; expr_ = 0; } else { save_cur->u_.case_.case_ex[idx] = 0; } stmt_cur_ = save_cur->u_.case_.case_st + idx; stmt_cur_->type_ = IVL_ST_NONE; if (net->stat(idx) == 0) { stmt_cur_->type_ = IVL_ST_NOOP; } else { net->stat(idx)->emit_proc(this); } } stmt_cur_ = save_cur; } bool dll_target::proc_cassign(const NetCAssign*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_CASSIGN; /* Make the l-value fields. */ make_assign_lvals_(net); assert(expr_ == 0); net->rval()->expr_scan(this); stmt_cur_->u_.assign_.rval_ = expr_; expr_ = 0; return true; } bool dll_target::proc_condit(const NetCondit*net) { bool rc_flag = true; assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_CONDIT; stmt_cur_->u_.condit_.stmt_ = (struct ivl_statement_s*) calloc(2, sizeof(struct ivl_statement_s)); assert(expr_ == 0); net->expr()->expr_scan(this); stmt_cur_->u_.condit_.cond_ = expr_; if (expr_ == 0) rc_flag = false; expr_ = 0; ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = save_cur_->u_.condit_.stmt_+0; rc_flag = net->emit_recurse_if(this) && rc_flag; stmt_cur_ = save_cur_->u_.condit_.stmt_+1; rc_flag = net->emit_recurse_else(this) && rc_flag; stmt_cur_ = save_cur_; return rc_flag; } bool dll_target::proc_deassign(const NetDeassign*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_DEASSIGN; /* Make the l-value fields. */ make_assign_lvals_(net); return true; } bool dll_target::proc_delay(const NetPDelay*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); ivl_statement_t tmp = (struct ivl_statement_s*) calloc(1, sizeof(struct ivl_statement_s)); if (const NetExpr*expr = net->expr()) { stmt_cur_->type_ = IVL_ST_DELAYX; assert(expr_ == 0); expr->expr_scan(this); stmt_cur_->u_.delayx_.expr = expr_; expr_ = 0; stmt_cur_->u_.delayx_.stmt_ = tmp; } else { stmt_cur_->type_ = IVL_ST_DELAY; stmt_cur_->u_.delay_.stmt_ = tmp; stmt_cur_->u_.delay_.value = net->delay(); } ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = tmp; bool flag = net->emit_proc_recurse(this); /* If the recurse doesn't turn this new item into something, then either it failed or there is no statement there. Either way, draw a no-op into the statement. */ if (stmt_cur_->type_ == IVL_ST_NONE) { stmt_cur_->type_ = IVL_ST_NOOP; } stmt_cur_ = save_cur_; return flag; } bool dll_target::proc_disable(const NetDisable*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_DISABLE; const NetScope* dis_scope = net->target(); /* A normal disable. */ if (dis_scope) stmt_cur_->u_.disable_.scope = lookup_scope_(dis_scope); /* A SystemVerilog disable fork. */ else stmt_cur_->u_.disable_.scope = 0; return true; } void dll_target::proc_do_while(const NetDoWhile*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_DO_WHILE; stmt_cur_->u_.while_.stmt_ = (struct ivl_statement_s*) calloc(1, sizeof(struct ivl_statement_s)); assert(expr_ == 0); net->expr()->expr_scan(this); stmt_cur_->u_.while_.cond_ = expr_; expr_ = 0; /* Now generate the statement of the do/while loop. We know it is a single statement, and we know that the emit_proc_recurse() will call emit_proc() for it. */ ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = save_cur_->u_.while_.stmt_; net->emit_proc_recurse(this); stmt_cur_ = save_cur_; } bool dll_target::proc_force(const NetForce*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_FORCE; /* Make the l-value fields. */ make_assign_lvals_(net); assert(expr_ == 0); net->rval()->expr_scan(this); stmt_cur_->u_.assign_.rval_ = expr_; expr_ = 0; return true; } void dll_target::proc_forever(const NetForever*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_FOREVER; ivl_statement_t tmp = (struct ivl_statement_s*) calloc(1, sizeof(struct ivl_statement_s)); ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = tmp; net->emit_recurse(this); save_cur_->u_.forever_.stmt_ = stmt_cur_; stmt_cur_ = save_cur_; } void dll_target::proc_free(const NetFree*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_FREE; stmt_cur_->u_.free_.scope = lookup_scope_(net->scope()); } bool dll_target::proc_release(const NetRelease*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_RELEASE; /* Make the l-value fields. */ make_assign_lvals_(net); return true; } void dll_target::proc_repeat(const NetRepeat*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_REPEAT; assert(expr_ == 0); net->expr()->expr_scan(this); stmt_cur_->u_.while_.cond_ = expr_; expr_ = 0; ivl_statement_t tmp = (struct ivl_statement_s*) calloc(1, sizeof(struct ivl_statement_s)); ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = tmp; net->emit_recurse(this); save_cur_->u_.while_.stmt_ = stmt_cur_; stmt_cur_ = save_cur_; } void dll_target::proc_stask(const NetSTask*net) { unsigned nparms = net->nparms(); assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_STASK; /* System task names are lex_strings strings. */ stmt_cur_->u_.stask_.name_ = net->name(); stmt_cur_->u_.stask_.sfunc_as_task_ = net->sfunc_as_task(); stmt_cur_->u_.stask_.nparm_= nparms; stmt_cur_->u_.stask_.parms_= (ivl_expr_t*) calloc(nparms, sizeof(ivl_expr_t)); for (unsigned idx = 0 ; idx < nparms ; idx += 1) { if (net->parm(idx)) net->parm(idx)->expr_scan(this); stmt_cur_->u_.stask_.parms_[idx] = expr_; expr_ = 0; } } bool dll_target::proc_trigger(const NetEvTrig*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_TRIGGER; stmt_cur_->u_.wait_.nevent = 1; /* Locate the event by name. Save the ivl_event_t in the statement so that the generator can find it easily. */ const NetEvent*ev = net->event(); ivl_scope_t ev_scope = lookup_scope_(ev->scope()); for (unsigned idx = 0 ; idx < ev_scope->nevent_ ; idx += 1) { const char*ename = ivl_event_basename(ev_scope->event_[idx]); if (strcmp(ev->name(), ename) == 0) { stmt_cur_->u_.wait_.event = ev_scope->event_[idx]; break; } } return true; } void dll_target::proc_utask(const NetUTask*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_UTASK; stmt_cur_->u_.utask_.def = lookup_scope_(net->task()); } bool dll_target::proc_wait(const NetEvWait*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_WAIT; stmt_cur_->u_.wait_.stmt_ = (struct ivl_statement_s*) calloc(1, sizeof(struct ivl_statement_s)); stmt_cur_->u_.wait_.nevent = net->nevents(); /* This is a wait fork statement. */ if ((net->nevents() == 1) && (net->event(0) == 0)) { stmt_cur_->u_.wait_.event = 0; stmt_cur_->type_ = IVL_ST_WAIT; stmt_cur_->u_.wait_.stmt_->type_ = IVL_ST_NOOP; return true; } // This event processing code is also in the NB assign above. if (net->nevents() > 1) { stmt_cur_->u_.wait_.events = (ivl_event_t*) calloc(net->nevents(), sizeof(ivl_event_t*)); } for (unsigned edx = 0 ; edx < net->nevents() ; edx += 1) { /* Locate the event by name. Save the ivl_event_t in the statement so that the generator can find it easily. */ const NetEvent*ev = net->event(edx); ivl_scope_t ev_scope = lookup_scope_(ev->scope()); ivl_event_t ev_tmp=0; assert(ev_scope); assert(ev_scope->nevent_ > 0); for (unsigned idx = 0 ; idx < ev_scope->nevent_ ; idx += 1) { const char*ename = ivl_event_basename(ev_scope->event_[idx]); if (strcmp(ev->name(), ename) == 0) { ev_tmp = ev_scope->event_[idx]; break; } } // XXX should we assert(ev_tmp)? if (net->nevents() == 1) stmt_cur_->u_.wait_.event = ev_tmp; else stmt_cur_->u_.wait_.events[edx] = ev_tmp; /* If this is an event with a probe, then connect up the pins. This wasn't done during the ::event method because the signals weren't scanned yet. */ if (ev->nprobe() >= 1) { unsigned iany = 0; unsigned ineg = ev_tmp->nany; unsigned ipos = ineg + ev_tmp->nneg; for (unsigned idx = 0 ; idx < ev->nprobe() ; idx += 1) { const NetEvProbe*pr = ev->probe(idx); unsigned base = 0; switch (pr->edge()) { case NetEvProbe::ANYEDGE: base = iany; iany += pr->pin_count(); break; case NetEvProbe::NEGEDGE: base = ineg; ineg += pr->pin_count(); break; case NetEvProbe::POSEDGE: base = ipos; ipos += pr->pin_count(); break; } for (unsigned bit = 0 ; bit < pr->pin_count() ; bit += 1) { ivl_nexus_t nex = (ivl_nexus_t) pr->pin(bit).nexus()->t_cookie(); assert(nex); ev_tmp->pins[base+bit] = nex; } } } } /* The ivl_statement_t for the wait statement is not complete until we calculate the sub-statement. */ ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = stmt_cur_->u_.wait_.stmt_; bool flag = net->emit_recurse(this); if (flag && (stmt_cur_->type_ == IVL_ST_NONE)) stmt_cur_->type_ = IVL_ST_NOOP; stmt_cur_ = save_cur_; return flag; } void dll_target::proc_while(const NetWhile*net) { assert(stmt_cur_); assert(stmt_cur_->type_ == IVL_ST_NONE); FILE_NAME(stmt_cur_, net); stmt_cur_->type_ = IVL_ST_WHILE; stmt_cur_->u_.while_.stmt_ = (struct ivl_statement_s*) calloc(1, sizeof(struct ivl_statement_s)); assert(expr_ == 0); net->expr()->expr_scan(this); stmt_cur_->u_.while_.cond_ = expr_; expr_ = 0; /* Now generate the statement of the while loop. We know it is a single statement, and we know that the emit_proc_recurse() will call emit_proc() for it. */ ivl_statement_t save_cur_ = stmt_cur_; stmt_cur_ = save_cur_->u_.while_.stmt_; net->emit_proc_recurse(this); stmt_cur_ = save_cur_; } iverilog-10_1/t-dll.cc000066400000000000000000002252601265551621300146740ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include # include // sprintf() # include "compiler.h" # include "t-dll.h" # include "netclass.h" # include "netmisc.h" # include "discipline.h" # include # include "ivl_assert.h" # include "ivl_alloc.h" struct dll_target dll_target_obj; #if defined(__WIN32__) inline ivl_dll_t ivl_dlopen(const char *name) { ivl_dll_t res = (ivl_dll_t) LoadLibrary(name); return res; } inline void * ivl_dlsym(ivl_dll_t dll, const char *nm) { return (void*)GetProcAddress((HMODULE)dll, nm); } inline void ivl_dlclose(ivl_dll_t dll) { FreeLibrary((HMODULE)dll); } const char *dlerror(void) { static char msg[256]; unsigned long err = GetLastError(); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &msg, sizeof(msg) - 1, NULL ); return msg; } #elif defined(HAVE_DLFCN_H) inline ivl_dll_t ivl_dlopen(const char*name) { return dlopen(name,RTLD_LAZY); } inline void* ivl_dlsym(ivl_dll_t dll, const char*nm) { void*sym = dlsym(dll, nm); /* Not found? try without the leading _ */ if (sym == 0 && nm[0] == '_') sym = dlsym(dll, nm+1); return sym; } inline void ivl_dlclose(ivl_dll_t dll) { dlclose(dll); } #elif defined(HAVE_DL_H) inline ivl_dll_t ivl_dlopen(const char*name) { return shl_load(name, BIND_IMMEDIATE, 0); } inline void* ivl_dlsym(ivl_dll_t dll, const char*nm) { void*sym; int rc = shl_findsym(&dll, nm, TYPE_PROCEDURE, &sym); return (rc == 0) ? sym : 0; } inline void ivl_dlclose(ivl_dll_t dll) { shl_unload(dll); } inline const char*dlerror(void) { return strerror( errno ); } #endif /* * The custom new operator for the ivl_nexus_s type allows us to * allocate nexus objects in blocks. There are generally lots of them * permanently allocated, and allocating them in blocks reduces the * allocation overhead. */ template void* pool_permalloc(size_t s) { static TYPE * pool_ptr = 0; static int pool_remaining = 0; static const size_t POOL_SIZE = 4096; assert(s == sizeof(TYPE)); if (pool_remaining <= 0) { pool_ptr = new TYPE[POOL_SIZE]; pool_remaining = POOL_SIZE; } TYPE*tmp = pool_ptr; pool_ptr += 1; pool_remaining -= 1; return tmp; } void* ivl_nexus_s::operator new(size_t s) { return pool_permalloc(s); } void ivl_nexus_s::operator delete(void*, size_t) { assert(0); } void* ivl_net_const_s::operator new(size_t s) { return pool_permalloc(s); } void ivl_net_const_s::operator delete(void*, size_t) { assert(0); } static StringHeapLex net_const_strings; static perm_string make_scope_name(const hname_t&name) { if (! name.has_numbers()) return name.peek_name(); char buf[1024]; snprintf(buf, sizeof buf, "%s", name.peek_name().str()); char*cp = buf + strlen(buf); size_t ncp = sizeof buf - (cp-buf); for (size_t idx = 0 ; idx < name.has_numbers() ; idx += 1) { int len = snprintf(cp, ncp, "[%d]", name.peek_number(idx)); cp += len; ncp -= len; } return lex_strings.make(buf); } static void drive_from_link(const Link&lnk, ivl_drive_t&drv0, ivl_drive_t&drv1) { drv0 = lnk.drive0(); drv1 = lnk.drive1(); } ivl_attribute_s* dll_target::fill_in_attributes(const Attrib*net) { ivl_attribute_s*attr; unsigned nattr = net->attr_cnt(); if (nattr == 0) return 0; attr = new struct ivl_attribute_s[nattr]; for (unsigned idx = 0 ; idx < nattr ; idx += 1) { verinum tmp = net->attr_value(idx); attr[idx].key = net->attr_key(idx); if (tmp.is_string()) { attr[idx].type = IVL_ATT_STR; attr[idx].val.str = strings_.add(tmp.as_string().c_str()); } else if (tmp == verinum()) { attr[idx].type = IVL_ATT_VOID; } else { attr[idx].type = IVL_ATT_NUM; attr[idx].val.num = tmp.as_long(); } } return attr; } /* * This function locates an ivl_scope_t object that matches the * NetScope object. The search works by looking for the parent scope, * then scanning the parent scope for the NetScope object. */ static ivl_scope_t find_scope_from_root(ivl_scope_t root, const NetScope*cur) { if (const NetScope*par = cur->parent()) { ivl_scope_t parent = find_scope_from_root(root, par); if (parent == 0) { return 0; } map::iterator idx = parent->children.find(cur->fullname()); if (idx == parent->children.end()) return 0; else return idx->second; } else { perm_string cur_name = make_scope_name(cur->fullname()); if (strcmp(root->name_, cur_name) == 0) return root; } return 0; } ivl_scope_t dll_target::find_scope(ivl_design_s &des, const NetScope*cur) { assert(cur); // If the scope is a PACKAGE, then it is a special kind of // root scope and it in the packages array instead. if (cur->type() == NetScope::PACKAGE) { perm_string cur_name = cur->module_name(); for (size_t idx = 0 ; idx < des.packages.size() ; idx += 1) { if (des.packages[idx]->name_ == cur_name) return des.packages[idx]; } return 0; } if (cur->type() == NetScope::CLASS) { ivl_scope_t tmp = des.classes[cur]; return tmp; } if (cur->type()==NetScope::TASK || cur->type()==NetScope::FUNC) { map::const_iterator idx = des.root_tasks.find(cur); if (idx != des.root_tasks.end()) return idx->second; } for (unsigned idx = 0; idx < des.roots.size(); idx += 1) { assert(des.roots[idx]); ivl_scope_t scope = find_scope_from_root(des.roots[idx], cur); if (scope) return scope; } for (size_t idx = 0; idx < des.packages.size(); idx += 1) { assert(des.packages[idx]); ivl_scope_t scope = find_scope_from_root(des.packages[idx], cur); if (scope) return scope; } for (map::iterator idx = des.classes.begin() ; idx != des.classes.end() ; ++ idx) { ivl_scope_t scope = find_scope_from_root(idx->second, cur); if (scope) return scope; } return 0; } ivl_scope_t dll_target::lookup_scope_(const NetScope*cur) { return find_scope(des_, cur); } /* * This is a convenience function to locate an ivl_signal_t object * given the NetESignal that has the signal name. */ ivl_signal_t dll_target::find_signal(ivl_design_s &des, const NetNet*net) { ivl_scope_t scope = find_scope(des, net->scope()); assert(scope); perm_string nname = net->name(); for (unsigned idx = 0 ; idx < scope->sigs_.size() ; idx += 1) { if (strcmp(scope->sigs_[idx]->name_, nname) == 0) return scope->sigs_[idx]; } assert(0); return 0; } static ivl_nexus_t nexus_sig_make(ivl_signal_t net, unsigned pin) { ivl_nexus_t tmp = new struct ivl_nexus_s; tmp->ptrs_.resize(1); tmp->ptrs_[0].pin_ = pin; tmp->ptrs_[0].type_ = __NEXUS_PTR_SIG; tmp->ptrs_[0].l.sig = net; ivl_drive_t drive = IVL_DR_HiZ; switch (ivl_signal_type(net)) { case IVL_SIT_REG: drive = IVL_DR_STRONG; break; default: break; } tmp->ptrs_[0].drive0 = drive; tmp->ptrs_[0].drive1 = drive; return tmp; } static void nexus_sig_add(ivl_nexus_t nex, ivl_signal_t net, unsigned pin) { unsigned top = nex->ptrs_.size(); nex->ptrs_.resize(top+1); ivl_drive_t drive = IVL_DR_HiZ; switch (ivl_signal_type(net)) { case IVL_SIT_REG: drive = IVL_DR_STRONG; break; default: break; } nex->ptrs_[top].type_= __NEXUS_PTR_SIG; nex->ptrs_[top].drive0 = drive; nex->ptrs_[top].drive1 = drive; nex->ptrs_[top].pin_ = pin; nex->ptrs_[top].l.sig= net; } static void nexus_bra_add(ivl_nexus_t nex, ivl_branch_t net, unsigned pin) { unsigned top = nex->ptrs_.size(); nex->ptrs_.resize(top+1); nex->ptrs_[top].type_= __NEXUS_PTR_BRA; nex->ptrs_[top].drive0 = 0; nex->ptrs_[top].drive1 = 0; nex->ptrs_[top].pin_ = pin; nex->ptrs_[top].l.bra= net; } /* * Add the pin of the logic object to the nexus, and return the nexus * pointer used for the pin. * * NOTE: This pointer is only valid until another pin is added to the * nexus. */ static ivl_nexus_ptr_t nexus_log_add(ivl_nexus_t nex, ivl_net_logic_t net, unsigned pin) { unsigned top = nex->ptrs_.size(); nex->ptrs_.resize(top+1); nex->ptrs_[top].type_= __NEXUS_PTR_LOG; nex->ptrs_[top].drive0 = (pin == 0)? IVL_DR_STRONG : IVL_DR_HiZ; nex->ptrs_[top].drive1 = (pin == 0)? IVL_DR_STRONG : IVL_DR_HiZ; nex->ptrs_[top].pin_ = pin; nex->ptrs_[top].l.log= net; return & (nex->ptrs_[top]); } static void nexus_con_add(ivl_nexus_t nex, ivl_net_const_t net, unsigned pin, ivl_drive_t drive0, ivl_drive_t drive1) { unsigned top = nex->ptrs_.size(); nex->ptrs_.resize(top+1); nex->ptrs_[top].type_= __NEXUS_PTR_CON; nex->ptrs_[top].drive0 = drive0; nex->ptrs_[top].drive1 = drive1; nex->ptrs_[top].pin_ = pin; nex->ptrs_[top].l.con= net; } static void nexus_lpm_add(ivl_nexus_t nex, ivl_lpm_t net, unsigned pin, ivl_drive_t drive0, ivl_drive_t drive1) { unsigned top = nex->ptrs_.size(); nex->ptrs_.resize(top+1); nex->ptrs_[top].type_= __NEXUS_PTR_LPM; nex->ptrs_[top].drive0 = drive0; nex->ptrs_[top].drive1 = drive1; nex->ptrs_[top].pin_ = pin; nex->ptrs_[top].l.lpm= net; } static void nexus_switch_add(ivl_nexus_t nex, ivl_switch_t net, unsigned pin) { unsigned top = nex->ptrs_.size(); nex->ptrs_.resize(top+1); nex->ptrs_[top].type_= __NEXUS_PTR_SWI; nex->ptrs_[top].drive0 = IVL_DR_HiZ; nex->ptrs_[top].drive1 = IVL_DR_HiZ; nex->ptrs_[top].pin_ = pin; nex->ptrs_[top].l.swi= net; } void scope_add_logic(ivl_scope_t scope, ivl_net_logic_t net) { if (scope->nlog_ == 0) { scope->nlog_ = 1; scope->log_ = (ivl_net_logic_t*)malloc(sizeof(ivl_net_logic_t)); scope->log_[0] = net; } else { scope->nlog_ += 1; scope->log_ = (ivl_net_logic_t*) realloc(scope->log_, scope->nlog_*sizeof(ivl_net_logic_t)); scope->log_[scope->nlog_-1] = net; } } void scope_add_event(ivl_scope_t scope, ivl_event_t net) { if (scope->nevent_ == 0) { scope->nevent_ = 1; scope->event_ = (ivl_event_t*)malloc(sizeof(ivl_event_t)); scope->event_[0] = net; } else { scope->nevent_ += 1; scope->event_ = (ivl_event_t*) realloc(scope->event_, scope->nevent_*sizeof(ivl_event_t)); scope->event_[scope->nevent_-1] = net; } } static void scope_add_lpm(ivl_scope_t scope, ivl_lpm_t net) { if (scope->nlpm_ == 0) { assert(scope->lpm_ == 0); scope->nlpm_ = 1; scope->lpm_ = (ivl_lpm_t*)malloc(sizeof(ivl_lpm_t)); scope->lpm_[0] = net; } else { assert(scope->lpm_); scope->nlpm_ += 1; scope->lpm_ = (ivl_lpm_t*) realloc(scope->lpm_, scope->nlpm_*sizeof(ivl_lpm_t)); scope->lpm_[scope->nlpm_-1] = net; } } static void scope_add_switch(ivl_scope_t scope, ivl_switch_t net) { scope->switches.push_back(net); } ivl_parameter_t dll_target::scope_find_param(ivl_scope_t scope, const char*name) { unsigned idx = 0; while (idx < scope->param.size()) { if (strcmp(name, scope->param[idx].basename) == 0) return &scope->param[idx]; idx += 1; } return 0; } /* * This method scans the parameters of the scope, and makes * ivl_parameter_t objects. This involves saving the name and scanning * the expression value. */ void dll_target::make_scope_parameters(ivl_scope_t scop, const NetScope*net) { if (net->parameters.empty()) { scop->param.clear(); return; } scop->param.resize(net->parameters.size()); unsigned idx = 0; typedef map::const_iterator pit_t; for (pit_t cur_pit = net->parameters.begin() ; cur_pit != net->parameters.end() ; ++ cur_pit ) { assert(idx < scop->param.size()); ivl_parameter_t cur_par = &scop->param[idx]; cur_par->basename = cur_pit->first; cur_par->local = cur_pit->second.local_flag; /* Either both the MSB and LSB expressions are provided or * neither are provided. */ if (cur_pit->second.msb) { assert(cur_pit->second.lsb); /* The MSB and LSB expressions must be integral constants. */ const NetEConst *msbc = dynamic_cast(cur_pit->second.msb); const NetEConst *lsbc = dynamic_cast(cur_pit->second.lsb); assert(msbc); assert(lsbc); cur_par->msb = msbc->value().as_long(); cur_par->lsb = lsbc->value().as_long(); } else { assert(! cur_pit->second.lsb); cur_par->msb = cur_pit->second.val->expr_width() - 1; assert(cur_par->msb >= 0); cur_par->lsb = 0; } cur_par->signed_flag = cur_pit->second.signed_flag; cur_par->scope = scop; FILE_NAME(cur_par, &(cur_pit->second)); NetExpr*etmp = cur_pit->second.val; if (etmp == 0) { cerr << "?:?: internal error: What is the parameter " << "expression for " << cur_pit->first << " in " << net->fullname() << "?" << endl; } assert(etmp); make_scope_param_expr(cur_par, etmp); idx += 1; } } void dll_target::make_scope_param_expr(ivl_parameter_t cur_par, NetExpr*etmp) { if (const NetEConst*e = dynamic_cast(etmp)) { expr_const(e); assert(expr_); switch (expr_->type_) { case IVL_EX_STRING: expr_->u_.string_.parameter = cur_par; break; case IVL_EX_NUMBER: expr_->u_.number_.parameter = cur_par; break; default: assert(0); } } else if (const NetECReal*er = dynamic_cast(etmp)) { expr_creal(er); assert(expr_); assert(expr_->type_ == IVL_EX_REALNUM); expr_->u_.real_.parameter = cur_par; } if (expr_ == 0) { cerr << etmp->get_fileline() << ": internal error: " << "Parameter expression not reduced to constant? " << *etmp << endl; } ivl_assert(*etmp, expr_); cur_par->value = expr_; expr_ = 0; } void dll_target::add_root(const NetScope *s) { ivl_scope_t root_ = new struct ivl_scope_s; perm_string name = s->basename(); root_->name_ = name; FILE_NAME(root_, s); root_->parent = 0; root_->nlog_ = 0; root_->log_ = 0; root_->nevent_ = 0; root_->event_ = 0; root_->nlpm_ = 0; root_->lpm_ = 0; root_->def = 0; make_scope_parameters(root_, s); root_->tname_ = root_->name_; root_->time_precision = s->time_precision(); root_->time_units = s->time_unit(); root_->nattr = s->attr_cnt(); root_->attr = fill_in_attributes(s); root_->is_auto = 0; root_->is_cell = s->is_cell(); switch (s->type()) { case NetScope::PACKAGE: root_->type_ = IVL_SCT_PACKAGE; break; case NetScope::MODULE: root_->type_ = IVL_SCT_MODULE; break; case NetScope::CLASS: root_->type_ = IVL_SCT_CLASS; break; case NetScope::TASK: { const NetTaskDef*def = s->task_def(); if (def == 0) { cerr << "?:?" << ": internal error: " << "task " << root_->name_ << " has no definition." << endl; } assert(def); root_->type_ = IVL_SCT_TASK; root_->tname_ = def->scope()->basename(); break; } break; case NetScope::FUNC: root_->type_ = IVL_SCT_FUNCTION; break; default: assert(0); } switch (s->type()) { case NetScope::MODULE: root_->ports = s->module_port_nets(); if (root_->ports > 0) { root_->u_.net = new NetNet*[root_->ports]; for (unsigned idx = 0; idx < root_->ports; idx += 1) { root_->u_.net[idx] = s->module_port_net(idx); } } root_->module_ports_info = s->module_port_info(); des_.roots.push_back(root_); break; case NetScope::PACKAGE: root_->ports = 0; des_.packages.push_back(root_); break; case NetScope::CLASS: root_->ports = 0; des_.classes[s] = root_; break; case NetScope::TASK: case NetScope::FUNC: des_.root_tasks[s] = root_; break; default: assert(0); break; } } bool dll_target::start_design(const Design*des) { const char*dll_path_ = des->get_flag("DLL"); dll_ = ivl_dlopen(dll_path_); if ((dll_ == 0) && (dll_path_[0] != '/')) { size_t len = strlen(basedir) + 1 + strlen(dll_path_) + 1; char*tmp = new char[len]; sprintf(tmp, "%s/%s", basedir, dll_path_); dll_ = ivl_dlopen(tmp); delete[]tmp; } if (dll_ == 0) { cerr << "error: " << dll_path_ << " failed to load." << endl; cerr << dll_path_ << ": " << dlerror() << endl; return false; } stmt_cur_ = 0; // Initialize the design object. des_.self = des; des_.time_precision = des->get_precision(); des_.disciplines.resize(disciplines.size()); unsigned idx = 0; for (map::const_iterator cur = disciplines.begin() ; cur != disciplines.end() ; ++ cur ) { des_.disciplines[idx] = cur->second; idx += 1; } assert(idx == des_.disciplines.size()); list scope_list = des->find_roottask_scopes(); for (list::const_iterator cur = scope_list.begin() ; cur != scope_list.end() ; ++ cur) { add_root(*cur); } scope_list = des->find_package_scopes(); for (list::const_iterator cur = scope_list.begin() ; cur != scope_list.end(); ++ cur ) { add_root(*cur); } scope_list = des->find_root_scopes(); for (list::const_iterator cur = scope_list.begin() ; cur != scope_list.end(); ++ cur ) { add_root(*cur); } target_ = (target_design_f)ivl_dlsym(dll_, LU "target_design" TU); if (target_ == 0) { cerr << dll_path_ << ": error: target_design entry " "point is missing." << endl; return false; } return true; } /* * Here ivl is telling us that the design is scanned completely, and * here is where we call the API to process the constructed design. */ int dll_target::end_design(const Design*) { int rc; if (errors == 0) { if (verbose_flag) { cout << " ... invoking target_design" << endl; } rc = (target_)(&des_); } else { if (verbose_flag) { cout << " ... skipping target_design due to errors." << endl; } rc = errors; } ivl_dlclose(dll_); return rc; } void dll_target::switch_attributes(struct ivl_switch_s *obj, const NetNode*net) { obj->nattr = net->attr_cnt(); obj->attr = fill_in_attributes(net); } void dll_target::logic_attributes(struct ivl_net_logic_s *obj, const NetNode*net) { obj->nattr = net->attr_cnt(); obj->attr = fill_in_attributes(net); } void dll_target::make_delays_(ivl_expr_t*delay, const NetObj*net) { delay[0] = 0; delay[1] = 0; delay[2] = 0; /* Translate delay expressions to ivl_target form. Try to preserve pointer equality, not as a rule but to save on expression trees. */ if (net->rise_time()) { expr_ = 0; net->rise_time()->expr_scan(this); delay[0] = expr_; expr_ = 0; } if (net->fall_time()) { if (net->fall_time() == net->rise_time()) { delay[1] = delay[0]; } else { expr_ = 0; net->fall_time()->expr_scan(this); delay[1] = expr_; expr_ = 0; } } if (net->decay_time()) { if (net->decay_time() == net->rise_time()) { delay[2] = delay[0]; } else { expr_ = 0; net->decay_time()->expr_scan(this); delay[2] = expr_; expr_ = 0; } } } void dll_target::make_logic_delays_(struct ivl_net_logic_s*obj, const NetObj*net) { make_delays_(obj->delay, net); } void dll_target::make_switch_delays_(struct ivl_switch_s*obj, const NetObj*net) { make_delays_(obj->delay, net); } void dll_target::make_lpm_delays_(struct ivl_lpm_s*obj, const NetObj*net) { make_delays_(obj->delay, net); } void dll_target::make_const_delays_(struct ivl_net_const_s*obj, const NetObj*net) { make_delays_(obj->delay, net); } bool dll_target::branch(const NetBranch*net) { struct ivl_branch_s*obj = net->target_obj(); ivl_assert(*net, net->pin_count() == 2); assert(net->pin(0).nexus()->t_cookie()); obj->pins[0] = net->pin(0).nexus()->t_cookie(); nexus_bra_add(obj->pins[0], obj, 0); assert(net->pin(1).nexus()->t_cookie()); obj->pins[1] = net->pin(1).nexus()->t_cookie(); nexus_bra_add(obj->pins[1], obj, 1); obj->island = net->get_island(); return true; } /* * Add a bufz object to the scope that contains it. * * Note that in the ivl_target API a BUFZ device is a special kind of * ivl_net_logic_t device, so create an ivl_net_logic_t cookie to * handle it. */ bool dll_target::bufz(const NetBUFZ*net) { struct ivl_net_logic_s *obj = new struct ivl_net_logic_s; assert(net->pin_count() == 2); obj->type_ = net->transparent()? IVL_LO_BUFT : IVL_LO_BUFZ; obj->width_= net->width(); obj->is_cassign = 0; obj->npins_= 2; obj->pins_ = new ivl_nexus_t[2]; FILE_NAME(obj, net); /* Get the ivl_nexus_t objects connected to the two pins. (We know a priori that the ivl_nexus_t objects have been allocated, because the signals have been scanned before me. This saves me the trouble of allocating them.) */ assert(net->pin(0).nexus()->t_cookie()); obj->pins_[0] = net->pin(0).nexus()->t_cookie(); ivl_nexus_ptr_t out_ptr = nexus_log_add(obj->pins_[0], obj, 0); out_ptr->drive0 = net->pin(0).drive0(); out_ptr->drive1 = net->pin(0).drive1(); assert(net->pin(1).nexus()->t_cookie()); obj->pins_[1] = net->pin(1).nexus()->t_cookie(); nexus_log_add(obj->pins_[1], obj, 1); /* Attach the logic device to the scope that contains it. */ assert(net->scope()); ivl_scope_t scop = find_scope(des_, net->scope()); assert(scop); obj->scope_ = scop; obj->name_ = net->name(); logic_attributes(obj, net); make_logic_delays_(obj, net); scope_add_logic(scop, obj); return true; } bool dll_target::class_type(const NetScope*in_scope, netclass_t*net) { ivl_scope_t use_scope = find_scope(des_, in_scope); use_scope->classes.push_back(net); return true; } bool dll_target::enumeration(const NetScope*in_scope, netenum_t*net) { ivl_scope_t use_scope = find_scope(des_, in_scope); use_scope->enumerations_.push_back(net); return true; } void dll_target::event(const NetEvent*net) { struct ivl_event_s *obj = new struct ivl_event_s; FILE_NAME(obj, net); ivl_scope_t scop = find_scope(des_, net->scope()); obj->name = net->name(); obj->scope = scop; scope_add_event(scop, obj); obj->nany = 0; obj->nneg = 0; obj->npos = 0; if (net->nprobe() >= 1) { for (unsigned idx = 0 ; idx < net->nprobe() ; idx += 1) { const NetEvProbe*pr = net->probe(idx); switch (pr->edge()) { case NetEvProbe::ANYEDGE: obj->nany += pr->pin_count(); break; case NetEvProbe::NEGEDGE: obj->nneg += pr->pin_count(); break; case NetEvProbe::POSEDGE: obj->npos += pr->pin_count(); break; } } unsigned npins = obj->nany + obj->nneg + obj->npos; obj->pins = (ivl_nexus_t*)calloc(npins, sizeof(ivl_nexus_t)); } else { obj->pins = 0; } } void dll_target::logic(const NetLogic*net) { struct ivl_net_logic_s *obj = new struct ivl_net_logic_s; obj->width_ = net->width(); FILE_NAME(obj, net); switch (net->type()) { case NetLogic::AND: obj->type_ = IVL_LO_AND; break; case NetLogic::BUF: obj->type_ = IVL_LO_BUF; break; case NetLogic::BUFIF0: obj->type_ = IVL_LO_BUFIF0; break; case NetLogic::BUFIF1: obj->type_ = IVL_LO_BUFIF1; break; case NetLogic::CMOS: obj->type_ = IVL_LO_CMOS; break; case NetLogic::NAND: obj->type_ = IVL_LO_NAND; break; case NetLogic::NMOS: obj->type_ = IVL_LO_NMOS; break; case NetLogic::NOR: obj->type_ = IVL_LO_NOR; break; case NetLogic::NOT: obj->type_ = IVL_LO_NOT; break; case NetLogic::NOTIF0: obj->type_ = IVL_LO_NOTIF0; break; case NetLogic::NOTIF1: obj->type_ = IVL_LO_NOTIF1; break; case NetLogic::OR: obj->type_ = IVL_LO_OR; break; case NetLogic::PULLDOWN: obj->type_ = IVL_LO_PULLDOWN; break; case NetLogic::PULLUP: obj->type_ = IVL_LO_PULLUP; break; case NetLogic::RCMOS: obj->type_ = IVL_LO_RCMOS; break; case NetLogic::RNMOS: obj->type_ = IVL_LO_RNMOS; break; case NetLogic::RPMOS: obj->type_ = IVL_LO_RPMOS; break; case NetLogic::PMOS: obj->type_ = IVL_LO_PMOS; break; case NetLogic::XNOR: obj->type_ = IVL_LO_XNOR; break; case NetLogic::XOR: obj->type_ = IVL_LO_XOR; break; default: assert(0); obj->type_ = IVL_LO_NONE; break; } /* Some of the logical gates are used to represent operators in a * continuous assignment, so set a flag if that is the case. */ obj->is_cassign = net->is_cassign(); /* Connect all the ivl_nexus_t objects to the pins of the device. */ obj->npins_ = net->pin_count(); obj->pins_ = new ivl_nexus_t[obj->npins_]; for (unsigned idx = 0 ; idx < obj->npins_ ; idx += 1) { const Nexus*nex = net->pin(idx).nexus(); assert(nex->t_cookie()); obj->pins_[idx] = nex->t_cookie(); ivl_nexus_ptr_t tmp = nexus_log_add(obj->pins_[idx], obj, idx); if (idx == 0) { tmp->drive0 = net->pin(0).drive0(); tmp->drive1 = net->pin(0).drive1(); } } assert(net->scope()); ivl_scope_t scop = find_scope(des_, net->scope()); assert(scop); obj->scope_= scop; obj->name_ = net->name(); logic_attributes(obj, net); make_logic_delays_(obj, net); scope_add_logic(scop, obj); } bool dll_target::tran(const NetTran*net) { struct ivl_switch_s*obj = new struct ivl_switch_s; obj->type = net->type(); obj->width = net->vector_width(); obj->part = 0; obj->offset = 0; obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); obj->island = net->get_island(); assert(obj->scope); assert(obj->island); FILE_NAME(obj, net); const Nexus*nex; nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->pins[0] = nex->t_cookie(); nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->pins[1] = nex->t_cookie(); nexus_switch_add(obj->pins[0], obj, 0); nexus_switch_add(obj->pins[1], obj, 1); if (net->pin_count() > 2) { nex = net->pin(2).nexus(); assert(nex->t_cookie()); obj->pins[2] = nex->t_cookie(); nexus_switch_add(obj->pins[2], obj, 2); } else { obj->pins[2] = 0; } if (obj->type == IVL_SW_TRAN_VP) { obj->part = net->part_width(); obj->offset= net->part_offset(); } switch_attributes(obj, net); make_switch_delays_(obj, net); scope_add_switch(obj->scope, obj); return true; } bool dll_target::substitute(const NetSubstitute*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_SUBSTITUTE; obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); obj->u_.substitute.base = net->base(); obj->u_.substitute.q = net->pin(0).nexus()->t_cookie(); obj->u_.substitute.a = net->pin(1).nexus()->t_cookie(); obj->u_.substitute.s = net->pin(2).nexus()->t_cookie(); nexus_lpm_add(obj->u_.substitute.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nexus_lpm_add(obj->u_.substitute.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nexus_lpm_add(obj->u_.substitute.s, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } bool dll_target::sign_extend(const NetSignExtend*net) { struct ivl_lpm_s*obj = new struct ivl_lpm_s; obj->type = IVL_LPM_SIGN_EXT; obj->width = net->width(); obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); const Nexus*nex; nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.reduce.q = nex->t_cookie(); nexus_lpm_add(obj->u_.reduce.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.reduce.a = nex->t_cookie(); nexus_lpm_add(obj->u_.reduce.a, obj, 1, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } bool dll_target::ureduce(const NetUReduce*net) { struct ivl_lpm_s*obj = new struct ivl_lpm_s; switch (net->type()) { case NetUReduce::NONE: assert(0); delete obj; return false; case NetUReduce::AND: obj->type = IVL_LPM_RE_AND; break; case NetUReduce::OR: obj->type = IVL_LPM_RE_OR; break; case NetUReduce::XOR: obj->type = IVL_LPM_RE_XOR; break; case NetUReduce::NAND: obj->type = IVL_LPM_RE_NAND; break; case NetUReduce::NOR: obj->type = IVL_LPM_RE_NOR; break; case NetUReduce::XNOR: obj->type = IVL_LPM_RE_XNOR; break; } obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); const Nexus*nex; nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.reduce.q = nex->t_cookie(); nexus_lpm_add(obj->u_.reduce.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.reduce.a = nex->t_cookie(); nexus_lpm_add(obj->u_.reduce.a, obj, 1, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } void dll_target::net_case_cmp(const NetCaseCmp*net) { struct ivl_lpm_s*obj = new struct ivl_lpm_s; switch (net->kind()) { case NetCaseCmp::EEQ: obj->type = IVL_LPM_CMP_EEQ; break; case NetCaseCmp::NEQ: obj->type = IVL_LPM_CMP_NEE; break; case NetCaseCmp::XEQ: obj->type = IVL_LPM_CMP_EQX; break; case NetCaseCmp::ZEQ: obj->type = IVL_LPM_CMP_EQZ; break; } obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); obj->u_.arith.signed_flag = 0; const Nexus*nex; nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin(2).nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } ivl_event_t dll_target::make_lpm_trigger(const NetEvWait*net) { ivl_event_t trigger = 0; if (net) { const NetEvent*ev = net->event(0); /* Locate the event by name. */ ivl_scope_t ev_scope = lookup_scope_(ev->scope()); assert(ev_scope); assert(ev_scope->nevent_ > 0); for (unsigned idx = 0; idx < ev_scope->nevent_; idx += 1) { const char*ename = ivl_event_basename(ev_scope->event_[idx]); if (strcmp(ev->name(), ename) == 0) { trigger = ev_scope->event_[idx]; break; } } /* Connect up the probe pins. This wasn't done during the ::event method because the signals weren't scanned yet. */ assert(ev->nprobe() == 1); const NetEvProbe*pr = ev->probe(0); for (unsigned bit = 0; bit < pr->pin_count(); bit += 1) { ivl_nexus_t nex = (ivl_nexus_t) pr->pin(bit).nexus()->t_cookie(); assert(nex); trigger->pins[bit] = nex; } } return trigger; } bool dll_target::net_sysfunction(const NetSysFunc*net) { unsigned idx; const Nexus*nex; struct ivl_lpm_s*obj = new struct ivl_lpm_s; obj->type = IVL_LPM_SFUNC; obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->u_.sfunc.ports = net->pin_count(); assert(net->pin_count() >= 1); obj->width = net->vector_width(); obj->u_.sfunc.fun_name = net->func_name(); obj->u_.sfunc.pins = new ivl_nexus_t[net->pin_count()]; nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.sfunc.pins[0] = nex->t_cookie(); nexus_lpm_add(obj->u_.sfunc.pins[0], obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); for (idx = 1 ; idx < net->pin_count() ; idx += 1) { nex = net->pin(idx).nexus(); assert(nex->t_cookie()); obj->u_.sfunc.pins[idx] = nex->t_cookie(); nexus_lpm_add(obj->u_.sfunc.pins[idx], obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } /* Save information about the trigger event if it exists. */ obj->u_.sfunc.trigger = make_lpm_trigger(net->trigger()); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } /* * An IVL_LPM_UFUNC represents a node in a combinational expression * that calls a user defined function. I create an LPM object that has * the right connections, and refers to the ivl_scope_t of the * definition. */ bool dll_target::net_function(const NetUserFunc*net) { struct ivl_lpm_s*obj = new struct ivl_lpm_s; obj->type = IVL_LPM_UFUNC; obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); /* Get the definition of the function and save it. */ const NetScope*def = net->def(); assert(def); obj->u_.ufunc.def = lookup_scope_(def); /* Save information about the ports in the ivl_lpm_s structure. Note that port 0 is the return value. */ obj->u_.ufunc.ports = net->pin_count(); assert(net->pin_count() >= 1); obj->width = net->port_width(0); /* Now collect all the pins and connect them to the nexa of the net. The output pins have strong drive, and the remaining input pins are HiZ. */ obj->u_.ufunc.pins = new ivl_nexus_t[net->pin_count()]; for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) { const Nexus*nex = net->pin(idx).nexus(); assert(nex->t_cookie()); ivl_nexus_t nn = nex->t_cookie(); assert(nn); obj->u_.ufunc.pins[idx] = nn; ivl_drive_t drive = idx == 0 ? IVL_DR_STRONG : IVL_DR_HiZ; nexus_lpm_add(obj->u_.ufunc.pins[idx], obj, idx, drive, drive); } /* Save information about the trigger event if it exists. */ obj->u_.ufunc.trigger = make_lpm_trigger(net->trigger()); make_lpm_delays_(obj, net); /* All done. Add this LPM to the scope. */ scope_add_lpm(obj->scope, obj); return true; } void dll_target::udp(const NetUDP*net) { struct ivl_net_logic_s *obj = new struct ivl_net_logic_s; obj->type_ = IVL_LO_UDP; FILE_NAME(obj, net); /* The NetUDP class hasn't learned about width yet, so we assume a width of 1. */ obj->width_ = 1; obj->is_cassign = 0; static map udps; ivl_udp_t u; if (udps.find(net->udp_name()) != udps.end()) { u = udps[net->udp_name()]; } else { u = new struct ivl_udp_s; u->nrows = net->rows(); u->table = (ivl_udp_s::ccharp_t*)malloc((u->nrows+1)*sizeof(char*)); u->table[u->nrows] = 0x0; u->nin = net->nin(); u->sequ = net->is_sequential(); u->file = net->udp_file(); u->lineno = net->udp_lineno(); if (u->sequ) u->init = net->get_initial(); else u->init = 'x'; u->name = net->udp_name(); string inp; char out; unsigned int i = 0; if (net->first(inp, out)) do { string tt = inp+out; u->table[i++] = strings_.add(tt.c_str()); } while (net->next(inp, out)); assert(i==u->nrows); assert((u->nin + 1) == net->port_count()); u->ports = new string [u->nin + 1]; for(unsigned idx = 0; idx <= u->nin; idx += 1) { u->ports[idx] = net->port_name(idx); } udps[net->udp_name()] = u; } obj->udp = u; // Some duplication of code here, see: dll_target::logic() /* Connect all the ivl_nexus_t objects to the pins of the device. */ obj->npins_ = net->pin_count(); obj->pins_ = new ivl_nexus_t[obj->npins_]; for (unsigned idx = 0 ; idx < obj->npins_ ; idx += 1) { /* Skip unconnected input pins. These will take on HiZ values by the code generators. */ if (! net->pin(idx).is_linked()) { obj->pins_[idx] = 0; continue; } const Nexus*nex = net->pin(idx).nexus(); ivl_assert(*net, nex && nex->t_cookie()); obj->pins_[idx] = nex->t_cookie(); nexus_log_add(obj->pins_[idx], obj, idx); } assert(net->scope()); ivl_scope_t scop = find_scope(des_, net->scope()); assert(scop); obj->scope_= scop; obj->name_ = net->name(); FILE_NAME(obj, net); make_logic_delays_(obj, net); obj->nattr = 0; obj->attr = 0; scope_add_logic(scop, obj); } void dll_target::lpm_abs(const NetAbs*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_ABS; obj->name = net->name(); // NetAddSub names are permallocated. assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->u_.arith.signed_flag = 0; obj->width = net->width(); const Nexus*nex; /* the output is pin(0) */ nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin(1).nexus(); assert(nex->t_cookie()); /* pin(1) is the input data. */ obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } void dll_target::lpm_add_sub(const NetAddSub*net) { ivl_lpm_t obj = new struct ivl_lpm_s; if (net->attribute(perm_string::literal("LPM_Direction")) == verinum("SUB")) obj->type = IVL_LPM_SUB; else obj->type = IVL_LPM_ADD; obj->name = net->name(); // NetAddSub names are permallocated. assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->u_.arith.signed_flag = 0; /* Choose the width of the adder. If the carry bit is connected, then widen the adder by one and plan on leaving the fake inputs unconnected. */ obj->width = net->width(); if (net->pin_Cout().is_linked()) { obj->width += 1; } const Nexus*nex; nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_DataA().nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_DataB().nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); /* If the carry output is connected, then connect the extra Q pin to the carry nexus and zero the a and b inputs. */ if (net->pin_Cout().is_linked()) { cerr << "XXXX: t-dll.cc: Forgot how to connect cout." << endl; } make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } bool dll_target::lpm_array_dq(const NetArrayDq*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_ARRAY; obj->name = net->name(); obj->u_.array.sig = find_signal(des_, net->mem()); assert(obj->u_.array.sig); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); obj->u_.array.swid = net->awidth(); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); const Nexus*nex; nex = net->pin_Address().nexus(); assert(nex->t_cookie()); obj->u_.array.a = nex->t_cookie(); nexus_lpm_add(obj->u_.array.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.array.q = nex->t_cookie(); nexus_lpm_add(obj->u_.array.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); return true; } /* * The lpm_clshift device represents both left and right shifts, * depending on what is connected to the Direction pin. We convert * this device into SHIFTL or SHIFTR devices. */ void dll_target::lpm_clshift(const NetCLShift*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_SHIFTL; obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); /* Look at the direction input of the device, and select the shift direction accordingly. */ if (net->right_flag()) obj->type = IVL_LPM_SHIFTR; if (net->signed_flag()) obj->u_.shift.signed_flag = 1; else obj->u_.shift.signed_flag = 0; obj->width = net->width(); obj->u_.shift.select = net->width_dist(); const Nexus*nex; nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.shift.q = nex->t_cookie(); nexus_lpm_add(obj->u_.shift.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_Data().nexus(); assert(nex->t_cookie()); obj->u_.shift.d = nex->t_cookie(); nexus_lpm_add(obj->u_.shift.d, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_Distance().nexus(); assert(nex->t_cookie()); obj->u_.shift.s = nex->t_cookie(); nexus_lpm_add(obj->u_.shift.s, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } bool dll_target::lpm_arith1_(ivl_lpm_type_t lpm_type, unsigned width, bool signed_flag, const NetNode*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = lpm_type; obj->name = net->name(); // NetCastInt2 names are permallocated assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = width; obj->u_.arith.signed_flag = signed_flag? 1 : 0; const Nexus*nex; nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } bool dll_target::lpm_cast_int2(const NetCastInt2*net) { return lpm_arith1_(IVL_LPM_CAST_INT2, net->width(), true, net); } bool dll_target::lpm_cast_int4(const NetCastInt4*net) { return lpm_arith1_(IVL_LPM_CAST_INT, net->width(), true, net); } bool dll_target::lpm_cast_real(const NetCastReal*net) { return lpm_arith1_(IVL_LPM_CAST_REAL, 0, net->signed_flag(), net); } /* * Make out of the NetCompare object an ivl_lpm_s object. The * comparators in ivl_target do not support < or <=, but they can be * trivially converted to > and >= by swapping the operands. */ void dll_target::lpm_compare(const NetCompare*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->name = net->name(); // NetCompare names are permallocated assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); bool swap_operands = false; obj->width = net->width(); obj->u_.arith.signed_flag = net->get_signed()? 1 : 0; const Nexus*nex; nex = net->pin_DataA().nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nex = net->pin_DataB().nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); if (net->pin_AGEB().is_linked()) { nex = net->pin_AGEB().nexus(); obj->type = IVL_LPM_CMP_GE; assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); } else if (net->pin_AGB().is_linked()) { nex = net->pin_AGB().nexus(); obj->type = IVL_LPM_CMP_GT; assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); } else if (net->pin_ALEB().is_linked()) { nex = net->pin_ALEB().nexus(); obj->type = IVL_LPM_CMP_GE; assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); swap_operands = true; } else if (net->pin_ALB().is_linked()) { nex = net->pin_ALB().nexus(); obj->type = IVL_LPM_CMP_GT; assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); swap_operands = true; } else if (net->pin_AEB().is_linked()) { nex = net->pin_AEB().nexus(); obj->type = IVL_LPM_CMP_EQ; assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); } else if (net->pin_ANEB().is_linked()) { nex = net->pin_ANEB().nexus(); obj->type = IVL_LPM_CMP_NE; assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); } else { assert(0); } if (swap_operands) { ivl_nexus_t tmp = obj->u_.arith.a; obj->u_.arith.a = obj->u_.arith.b; obj->u_.arith.b = tmp; } nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } void dll_target::lpm_divide(const NetDivide*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_DIVIDE; obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); unsigned wid = net->width_r(); obj->width = wid; obj->u_.arith.signed_flag = net->get_signed()? 1 : 0; const Nexus*nex; nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_DataA().nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_DataB().nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } void dll_target::lpm_modulo(const NetModulo*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_MOD; obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); unsigned wid = net->width_r(); obj->width = wid; obj->u_.arith.signed_flag = net->get_signed()? 1 : 0; const Nexus*nex; nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_DataA().nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_DataB().nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } void dll_target::lpm_ff(const NetFF*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_FF; obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); scope_add_lpm(obj->scope, obj); const Nexus*nex; /* Set the clock polarity. */ obj->u_.ff.negedge_flag = net->is_negedge(); /* Set the clk signal to point to the nexus, and the nexus to point back to this device. */ nex = net->pin_Clock().nexus(); assert(nex->t_cookie()); obj->u_.ff.clk = nex->t_cookie(); assert(obj->u_.ff.clk); nexus_lpm_add(obj->u_.ff.clk, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); /* If there is a clock enable, then connect it up to the FF device. */ if (net->pin_Enable().is_linked()) { nex = net->pin_Enable().nexus(); assert(nex->t_cookie()); obj->u_.ff.we = nex->t_cookie(); assert(obj->u_.ff.we); nexus_lpm_add(obj->u_.ff.we, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } else { obj->u_.ff.we = 0; } if (net->pin_Aclr().is_linked()) { nex = net->pin_Aclr().nexus(); assert(nex->t_cookie()); obj->u_.ff.aclr = nex->t_cookie(); assert(obj->u_.ff.aclr); nexus_lpm_add(obj->u_.ff.aclr, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } else { obj->u_.ff.aclr = 0; } if (net->pin_Aset().is_linked()) { nex = net->pin_Aset().nexus(); assert(nex->t_cookie()); obj->u_.ff.aset = nex->t_cookie(); assert(obj->u_.ff.aset); nexus_lpm_add(obj->u_.ff.aset, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); verinum tmp = net->aset_value(); if (tmp.len() > 0) obj->u_.ff.aset_value = expr_from_value_(tmp); else obj->u_.ff.aset_value = 0; } else { obj->u_.ff.aset = 0; obj->u_.ff.aset_value = 0; } if (net->pin_Sclr().is_linked()) { nex = net->pin_Sclr().nexus(); assert(nex->t_cookie()); obj->u_.ff.sclr = nex->t_cookie(); assert(obj->u_.ff.sclr); nexus_lpm_add(obj->u_.ff.sclr, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } else { obj->u_.ff.sclr = 0; } if (net->pin_Sset().is_linked()) { nex = net->pin_Sset().nexus(); assert(nex->t_cookie()); obj->u_.ff.sset = nex->t_cookie(); assert(obj->u_.ff.sset); nexus_lpm_add(obj->u_.ff.sset, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); verinum tmp = net->sset_value(); if (tmp.len() > 0) obj->u_.ff.sset_value = expr_from_value_(tmp); else obj->u_.ff.sset_value = 0; } else { obj->u_.ff.sset = 0; obj->u_.ff.sset_value = 0; } nex = net->pin_Q().nexus(); assert(nex->t_cookie()); obj->u_.ff.q.pin = nex->t_cookie(); nexus_lpm_add(obj->u_.ff.q.pin, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_Data().nexus(); assert(nex->t_cookie()); obj->u_.ff.d.pin = nex->t_cookie(); nexus_lpm_add(obj->u_.ff.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } /* * Make the NetMult object into an IVL_LPM_MULT node. */ void dll_target::lpm_mult(const NetMult*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_MULT; obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); unsigned wid = net->width_r(); obj->width = wid; obj->u_.arith.signed_flag = 0; const Nexus*nex; nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_DataA().nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_DataB().nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } /* * Hook up the mux devices so that the select expression selects the * correct sub-expression with the ivl_lpm_data2 function. */ void dll_target::lpm_mux(const NetMux*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_MUX; obj->name = net->name(); // The NetMux permallocates its name. obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); obj->u_.mux.size = net->size(); obj->u_.mux.swid = net->sel_width(); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); const Nexus*nex; /* Connect the output bits. */ nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.mux.q = nex->t_cookie(); nexus_lpm_add(obj->u_.mux.q, obj, 0, net->pin_Result().drive0(), net->pin_Result().drive1()); /* Connect the select bits. */ nex = net->pin_Sel().nexus(); assert(nex->t_cookie()); obj->u_.mux.s = nex->t_cookie(); nexus_lpm_add(obj->u_.mux.s, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); unsigned selects = obj->u_.mux.size; obj->u_.mux.d = new ivl_nexus_t [selects]; for (unsigned sdx = 0 ; sdx < selects ; sdx += 1) { nex = net->pin_Data(sdx).nexus(); ivl_nexus_t tmp = nex->t_cookie(); obj->u_.mux.d[sdx] = tmp; if (tmp == 0) { cerr << net->get_fileline() << ": internal error: " << "dll_target::lpm_mux: " << "Missing data port " << sdx << " of mux " << obj->name << "." << endl; } ivl_assert(*net, tmp); nexus_lpm_add(tmp, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } } /* * Make the NetPow object into an IVL_LPM_POW node. */ void dll_target::lpm_pow(const NetPow*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_POW; FILE_NAME(obj, net); obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); unsigned wid = net->width_r(); obj->u_.arith.signed_flag = net->get_signed()? 1 : 0; obj->width = wid; const Nexus*nex; nex = net->pin_Result().nexus(); assert(nex->t_cookie()); obj->u_.arith.q = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nex = net->pin_DataA().nexus(); assert(nex->t_cookie()); obj->u_.arith.a = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nex = net->pin_DataB().nexus(); assert(nex->t_cookie()); obj->u_.arith.b = nex->t_cookie(); nexus_lpm_add(obj->u_.arith.b, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); } bool dll_target::concat(const NetConcat*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = net->transparent()? IVL_LPM_CONCATZ : IVL_LPM_CONCAT; obj->name = net->name(); // NetConcat names are permallocated assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); obj->u_.concat.inputs = net->pin_count() - 1; obj->u_.concat.pins = new ivl_nexus_t[obj->u_.concat.inputs+1]; for (unsigned idx = 0 ; idx < obj->u_.concat.inputs+1 ; idx += 1) { ivl_drive_t dr = idx == 0? IVL_DR_STRONG : IVL_DR_HiZ; const Nexus*nex = net->pin(idx).nexus(); assert(nex->t_cookie()); obj->u_.concat.pins[idx] = nex->t_cookie(); nexus_lpm_add(obj->u_.concat.pins[idx], obj, 0, dr, dr); } make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } bool dll_target::part_select(const NetPartSelect*net) { ivl_lpm_t obj = new struct ivl_lpm_s; switch (net->dir()) { case NetPartSelect::VP: obj->type = IVL_LPM_PART_VP; break; case NetPartSelect::PV: obj->type = IVL_LPM_PART_PV; break; } obj->name = net->name(); // NetPartSelect names are permallocated. assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); /* Part selects are always unsigned, so we use this to indicate * if the part select base signal is signed or not. */ if (net->signed_flag()) obj->u_.part.signed_flag = 1; else obj->u_.part.signed_flag = 0; /* Choose the width of the part select. */ obj->width = net->width(); obj->u_.part.base = net->base(); obj->u_.part.s = 0; const Nexus*nex; switch (obj->type) { case IVL_LPM_PART_VP: /* NetPartSelect:pin(0) is the output pin. */ nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.part.q = nex->t_cookie(); /* NetPartSelect:pin(1) is the input pin. */ nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.part.a = nex->t_cookie(); /* If the part select has an additional pin, that pin is a variable select base. */ if (net->pin_count() >= 3) { nex = net->pin(2).nexus(); assert(nex->t_cookie()); obj->u_.part.s = nex->t_cookie(); } break; case IVL_LPM_PART_PV: /* NetPartSelect:pin(1) is the output pin. */ nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.part.q = nex->t_cookie(); /* NetPartSelect:pin(0) is the input pin. */ nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.part.a = nex->t_cookie(); break; default: assert(0); } nexus_lpm_add(obj->u_.part.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); /* The select input is optional. */ if (obj->u_.part.s) nexus_lpm_add(obj->u_.part.s, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } bool dll_target::replicate(const NetReplicate*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_REPEAT; obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); assert(obj->scope); FILE_NAME(obj, net); obj->width = net->width(); obj->u_.repeat.count = net->repeat(); ivl_drive_t dr = IVL_DR_STRONG; const Nexus*nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->u_.repeat.q = nex->t_cookie(); nexus_lpm_add(obj->u_.repeat.q, obj, 0, dr, dr); dr = IVL_DR_HiZ; nex = net->pin(1).nexus(); assert(nex->t_cookie()); obj->u_.repeat.a = nex->t_cookie(); nexus_lpm_add(obj->u_.repeat.a, obj, 0, dr, dr); make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); return true; } /* * The assignment l-values are captured by the assignment statements * themselves in the process handling. */ void dll_target::net_assign(const NetAssign_*) const { } bool dll_target::net_const(const NetConst*net) { unsigned idx; char*bits; static char*bits_tmp = 0; static unsigned bits_cnt = 0; struct ivl_net_const_s *obj = new struct ivl_net_const_s; if (net->is_string()) { obj->type = IVL_VT_STRING; assert((net->width() % 8) == 0); } else obj->type = IVL_VT_BOOL; assert(net->scope()); obj->scope = find_scope(des_, net->scope()); FILE_NAME(obj, net); /* constants have a single vector output. */ assert(net->pin_count() == 1); obj->width_ = net->width(); obj->signed_ = net->value().has_sign(); if (obj->width_ <= sizeof(obj->b.bit_)) { bits = obj->b.bit_; } else { if (obj->width_ >= bits_cnt) { bits_tmp = (char*)realloc(bits_tmp, obj->width_+1); bits_cnt = obj->width_+1; } bits = bits_tmp; } for (idx = 0 ; idx < obj->width_ ; idx += 1) switch (net->value(idx)) { case verinum::V0: bits[idx] = '0'; break; case verinum::V1: bits[idx] = '1'; break; case verinum::Vx: if (obj->type == IVL_VT_BOOL) obj->type = IVL_VT_LOGIC; bits[idx] = 'x'; assert(! net->is_string()); break; case verinum::Vz: if (obj->type == IVL_VT_BOOL) obj->type = IVL_VT_LOGIC; bits[idx] = 'z'; assert(! net->is_string()); break; } if (obj->width_ > sizeof(obj->b.bit_)) { bits[obj->width_] = 0; obj->b.bits_ = net_const_strings.make(bits); } /* Connect to all the nexus objects. Note that the one-bit case can be handled more efficiently without allocating array space. */ ivl_drive_t drv0, drv1; drive_from_link(net->pin(0), drv0, drv1); const Nexus*nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->pin_ = nex->t_cookie(); nexus_con_add(obj->pin_, obj, 0, drv0, drv1); des_.consts.push_back(obj); make_const_delays_(obj, net); return true; } bool dll_target::net_literal(const NetLiteral*net) { struct ivl_net_const_s *obj = new struct ivl_net_const_s; obj->type = IVL_VT_REAL; assert(net->scope()); obj->scope = find_scope(des_, net->scope()); FILE_NAME(obj, net); obj->width_ = 1; obj->signed_ = 1; obj->b.real_value = net->value_real().as_double(); /* Connect to all the nexus objects. Note that the one-bit case can be handled more efficiently without allocating array space. */ ivl_drive_t drv0, drv1; drive_from_link(net->pin(0), drv0, drv1); const Nexus*nex = net->pin(0).nexus(); assert(nex->t_cookie()); obj->pin_ = nex->t_cookie(); nexus_con_add(obj->pin_, obj, 0, drv0, drv1); des_.consts.push_back(obj); make_const_delays_(obj, net); return true; } void dll_target::net_probe(const NetEvProbe*) { } void dll_target::scope(const NetScope*net) { if (net->parent()==0 && net->type()==NetScope::CLASS) { if (debug_emit) { cerr << "dll_target::scope: " << "Add class " << scope_path(net) << " as a root scope." << endl; } add_root(net); } if (net->parent() == 0) { // Root scopes are already created... } else { perm_string sname = make_scope_name(net->fullname()); ivl_scope_t scop = new struct ivl_scope_s; scop->name_ = sname; FILE_NAME(scop, net); scop->parent = find_scope(des_, net->parent()); assert(scop->parent); scop->parent->children[net->fullname()] = scop; scop->parent->child .push_back(scop); scop->nlog_ = 0; scop->log_ = 0; scop->nevent_ = 0; scop->event_ = 0; scop->nlpm_ = 0; scop->lpm_ = 0; scop->def = 0; make_scope_parameters(scop, net); scop->time_precision = net->time_precision(); scop->time_units = net->time_unit(); scop->nattr = net->attr_cnt(); scop->attr = fill_in_attributes(net); scop->is_auto = net->is_auto(); scop->is_cell = net->is_cell(); switch (net->type()) { case NetScope::PACKAGE: cerr << "?:?" << ": internal error: " << "Package scopes should not have parents." << endl; case NetScope::MODULE: scop->type_ = IVL_SCT_MODULE; scop->tname_ = net->module_name(); scop->ports = net->module_port_nets(); if (scop->ports > 0) { scop->u_.net = new NetNet*[scop->ports]; for (unsigned idx = 0; idx < scop->ports; idx += 1) { scop->u_.net[idx] = net->module_port_net(idx); } } scop->module_ports_info = net->module_port_info(); break; case NetScope::TASK: { const NetTaskDef*def = net->task_def(); if (def == 0) { cerr << "?:?" << ": internal error: " << "task " << scop->name_ << " has no definition." << endl; } assert(def); scop->type_ = IVL_SCT_TASK; scop->tname_ = def->scope()->basename(); break; } case NetScope::FUNC: scop->type_ = IVL_SCT_FUNCTION; scop->tname_ = net->func_def()->scope()->basename(); break; case NetScope::BEGIN_END: scop->type_ = IVL_SCT_BEGIN; scop->tname_ = scop->name_; break; case NetScope::FORK_JOIN: scop->type_ = IVL_SCT_FORK; scop->tname_ = scop->name_; break; case NetScope::GENBLOCK: scop->type_ = IVL_SCT_GENERATE; scop->tname_ = scop->name_; break; case NetScope::CLASS: assert(0); break; } } } void dll_target::convert_module_ports(const NetScope*net) { ivl_scope_t scop = find_scope(des_, net); if (scop->ports > 0) { NetNet**nets = scop->u_.net; scop->u_.nex = new ivl_nexus_t[scop->ports]; for (unsigned idx = 0; idx < scop->ports; idx += 1) { ivl_signal_t sig = find_signal(des_, nets[idx]); scop->u_.nex[idx] = nexus_sig_make(sig, 0); } delete [] nets; } } void dll_target::signal(const NetNet*net) { ivl_signal_t obj = new struct ivl_signal_s; obj->name_ = net->name(); /* Attach the signal to the ivl_scope_t object that contains it. This involves growing the sigs_ array in the scope object, or creating the sigs_ array if this is the first signal. */ obj->scope_ = find_scope(des_, net->scope()); assert(obj->scope_); FILE_NAME(obj, net); obj->scope_->sigs_.push_back(obj); /* Save the primitive properties of the signal in the ivl_signal_t object. */ { size_t idx = 0; vector::const_iterator cur; obj->packed_dims.resize(net->packed_dims().size()); for (cur = net->packed_dims().begin(), idx = 0 ; cur != net->packed_dims().end() ; ++cur, idx += 1) { obj->packed_dims[idx] = *cur; } } obj->net_type = net->net_type(); obj->local_ = net->local_flag()? 1 : 0; obj->forced_net_ = (net->type() != NetNet::REG) && (net->peek_lref() > 0) ? 1 : 0; obj->discipline = net->get_discipline(); obj->array_dimensions_ = net->unpacked_dimensions(); assert(obj->array_dimensions_ == net->unpacked_dimensions()); switch (net->port_type()) { case NetNet::PINPUT: obj->port_ = IVL_SIP_INPUT; break; case NetNet::POUTPUT: obj->port_ = IVL_SIP_OUTPUT; break; case NetNet::PINOUT: obj->port_ = IVL_SIP_INOUT; break; default: obj->port_ = IVL_SIP_NONE; break; } obj->module_port_index_ = net->get_module_port_index(); switch (net->type()) { case NetNet::REG: obj->type_ = IVL_SIT_REG; break; /* The SUPPLY0/1 net types are replaced with pulldown/up by elaborate. They should not make it here. */ case NetNet::SUPPLY0: assert(0); break; case NetNet::SUPPLY1: assert(0); break; /* We will convert this to a TRI after we check that there is only one driver. */ case NetNet::UNRESOLVED_WIRE: obj->type_ = IVL_SIT_UWIRE; break; case NetNet::TRI: case NetNet::WIRE: case NetNet::IMPLICIT: obj->type_ = IVL_SIT_TRI; break; case NetNet::TRI0: obj->type_ = IVL_SIT_TRI0; break; case NetNet::TRI1: obj->type_ = IVL_SIT_TRI1; break; case NetNet::TRIAND: case NetNet::WAND: obj->type_ = IVL_SIT_TRIAND; break; case NetNet::TRIOR: case NetNet::WOR: obj->type_ = IVL_SIT_TRIOR; break; default: obj->type_ = IVL_SIT_NONE; break; } /* Initialize the path fields to be filled in later. */ obj->npath = 0; obj->path = 0; obj->nattr = net->attr_cnt(); obj->attr = fill_in_attributes(net); /* Get the nexus objects for all the pins of the signal. If the signal has only one pin, then write the single ivl_nexus_t object into n.pin_. Otherwise, make an array of ivl_nexus_t cookies. When I create an ivl_nexus_t object, store it in the t_cookie of the Nexus object so that I find it again when I next encounter the nexus. */ if (obj->array_dimensions_ == 1) { const vector& dims = net->unpacked_dims(); if (dims[0].get_msb() < dims[0].get_lsb()) { obj->array_base = dims[0].get_msb(); obj->array_addr_swapped = false; } else { obj->array_base = dims[0].get_lsb(); obj->array_addr_swapped = true; } obj->array_words = net->unpacked_count(); } else { // The back-end API doesn't yet support multi-dimension // unpacked arrays, so just report the canonical dimensions. obj->array_base = 0; obj->array_words = net->unpacked_count(); obj->array_addr_swapped = 0; } ivl_assert(*net, obj->array_words == net->pin_count()); if (debug_optimizer && obj->array_words > 1000) cerr << "debug: " "t-dll creating nexus array " << obj->array_words << " long" << endl; if (obj->array_words > 1 && net->pins_are_virtual()) { obj->pins = NULL; if (debug_optimizer && obj->array_words > 1000) cerr << "debug: " "t-dll used NULL for big nexus array" << endl; return; } if (obj->array_words > 1) obj->pins = new ivl_nexus_t[obj->array_words]; for (unsigned idx = 0 ; idx < obj->array_words ; idx += 1) { const Nexus*nex = net->pins_are_virtual() ? 0 : net->pin(idx).nexus(); if (nex == 0) { // Special case: This pin is connected to // nothing. This can happen, for example, if the // variable is only used in behavioral // code. Create a stub nexus. ivl_nexus_t tmp = nexus_sig_make(obj, idx); tmp->nexus_ = nex; tmp->name_ = 0; if (obj->array_words > 1) obj->pins[idx] = tmp; else obj->pin = tmp; } else if (nex->t_cookie()) { if (obj->array_words > 1) { obj->pins[idx] = nex->t_cookie(); nexus_sig_add(obj->pins[idx], obj, idx); } else { obj->pin = nex->t_cookie(); nexus_sig_add(obj->pin, obj, idx); } } else { ivl_nexus_t tmp = nexus_sig_make(obj, idx); tmp->nexus_ = nex; tmp->name_ = 0; nex->t_cookie(tmp); if (obj->array_words > 1) obj->pins[idx] = tmp; else obj->pin = tmp; } } if (debug_optimizer && obj->array_words > 1000) cerr << "debug: t-dll done with big nexus array" << endl; } bool dll_target::signal_paths(const NetNet*net) { /* Nothing to do if there are no paths for this signal. */ if (net->delay_paths() == 0) return true; ivl_signal_t obj = find_signal(des_, net); assert(obj); /* We cannot have already set up the paths for this signal. */ assert(obj->npath == 0); assert(obj->path == 0); /* Figure out how many paths there really are. */ for (unsigned idx = 0 ; idx < net->delay_paths() ; idx += 1) { const NetDelaySrc*src = net->delay_path(idx); obj->npath += src->src_count(); } obj->path = new struct ivl_delaypath_s[obj->npath]; unsigned ptr = 0; for (unsigned idx = 0 ; idx < net->delay_paths() ; idx += 1) { const NetDelaySrc*src = net->delay_path(idx); /* If this path has a condition, then hook it up. */ ivl_nexus_t path_condit = 0; if (src->has_condit()) { const Nexus*nt = src->condit_pin().nexus(); path_condit = nt->t_cookie(); } for (unsigned pin = 0; pin < src->src_count(); pin += 1) { const Nexus*nex = src->src_pin(pin).nexus(); if (! nex->t_cookie()) { cerr << src->get_fileline() << ": internal error: " << "No signal connected to pin " << pin << " of delay path to " << net->name() << "." << endl; } assert(nex->t_cookie()); obj->path[ptr].scope = lookup_scope_(src->scope()); obj->path[ptr].src = nex->t_cookie(); obj->path[ptr].condit = path_condit; obj->path[ptr].conditional = src->is_condit(); obj->path[ptr].posedge = src->is_posedge(); obj->path[ptr].negedge = src->is_negedge(); for (unsigned pe = 0 ; pe < 12 ; pe += 1) { obj->path[ptr].delay[pe] = src->get_delay(pe); } ptr += 1; } } return true; } void dll_target::test_version(const char*target_name) { dll_ = ivl_dlopen(target_name); if ((dll_ == 0) && (target_name[0] != '/')) { size_t len = strlen(basedir) + 1 + strlen(target_name) + 1; char*tmp = new char[len]; sprintf(tmp, "%s/%s", basedir, target_name); dll_ = ivl_dlopen(tmp); delete[]tmp; } if (dll_ == 0) { cout << "\n\nUnable to load " << target_name << " for version details." << endl; return; } target_query_f target_query = (target_query_f)ivl_dlsym(dll_, LU "target_query" TU); if (target_query == 0) { cerr << "Target " << target_name << " has no version hooks." << endl; return; } const char*version_string = (*target_query) ("version"); if (version_string == 0) { cerr << "Target " << target_name << " has no version string" << endl; return; } cout << target_name << ": " << version_string << endl; } iverilog-10_1/t-dll.h000066400000000000000000000563151265551621300145410ustar00rootroot00000000000000#ifndef IVL_t_dll_H #define IVL_t_dll_H /* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "target.h" # include "ivl_target.h" # include "ivl_target_priv.h" # include "StringHeap.h" # include "netlist.h" # include # include #if defined(__MINGW32__) #include typedef void *ivl_dll_t; #elif defined(HAVE_DLFCN_H) # include typedef void* ivl_dll_t; #elif defined(HAVE_DL_H) # include typedef shl_t ivl_dll_t; #else # error No DLL stub support for this target. #endif /* * The DLL target type loads a named object file to handle the process * of scanning the netlist. When it is time to start the design, I * locate and link in the desired DLL, then start calling methods. The * DLL will call me back to get information out of the netlist in * particular. */ struct dll_target : public target_t, public expr_scan_t { // This is a special function for loading and testing the // version of a loadable target code generator. void test_version(const char*target_name); bool start_design(const Design*); int end_design(const Design*); bool bufz(const NetBUFZ*); bool branch(const NetBranch*); bool class_type(const NetScope*, netclass_t*); bool enumeration(const NetScope*, netenum_t*); void event(const NetEvent*); void logic(const NetLogic*); bool tran(const NetTran*); bool ureduce(const NetUReduce*); void net_case_cmp(const NetCaseCmp*); void udp(const NetUDP*); void lpm_abs(const NetAbs*); void lpm_add_sub(const NetAddSub*); bool lpm_array_dq(const NetArrayDq*); bool lpm_cast_int2(const NetCastInt2*); bool lpm_cast_int4(const NetCastInt4*); bool lpm_cast_real(const NetCastReal*); void lpm_clshift(const NetCLShift*); void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); void lpm_ff(const NetFF*); void lpm_modulo(const NetModulo*); void lpm_mult(const NetMult*); void lpm_mux(const NetMux*); void lpm_pow(const NetPow*); bool concat(const NetConcat*); bool part_select(const NetPartSelect*); bool replicate(const NetReplicate*); void net_assign(const NetAssign_*) const; bool net_sysfunction(const NetSysFunc*); bool net_function(const NetUserFunc*); bool net_const(const NetConst*); bool net_literal(const NetLiteral*); void net_probe(const NetEvProbe*); bool sign_extend(const NetSignExtend*); bool substitute(const NetSubstitute*); bool process(const NetProcTop*); bool process(const NetAnalogTop*); void scope(const NetScope*); void convert_module_ports(const NetScope*); void signal(const NetNet*); bool signal_paths(const NetNet*); ivl_dll_t dll_; ivl_design_s des_; target_design_f target_; /* These methods and members are used for forming the statements of a thread. */ struct ivl_statement_s*stmt_cur_; void proc_alloc(const NetAlloc*); bool proc_assign(const NetAssign*); void proc_assign_nb(const NetAssignNB*); bool proc_block(const NetBlock*); void proc_case(const NetCase*); bool proc_cassign(const NetCAssign*); bool proc_condit(const NetCondit*); bool proc_contribution(const NetContribution*); bool proc_deassign(const NetDeassign*); bool proc_delay(const NetPDelay*); bool proc_disable(const NetDisable*); void proc_do_while(const NetDoWhile*); bool proc_force(const NetForce*); void proc_forever(const NetForever*); void proc_free(const NetFree*); bool proc_release(const NetRelease*); void proc_repeat(const NetRepeat*); void proc_stask(const NetSTask*); bool proc_trigger(const NetEvTrig*); void proc_utask(const NetUTask*); bool proc_wait(const NetEvWait*); void proc_while(const NetWhile*); bool func_def(const NetScope*); void task_def(const NetScope*); struct ivl_expr_s*expr_; void expr_access_func(const NetEAccess*); void expr_array_pattern(const NetEArrayPattern*); void expr_binary(const NetEBinary*); void expr_concat(const NetEConcat*); void expr_const(const NetEConst*); void expr_creal(const NetECReal*); void expr_last(const NetELast*); void expr_new(const NetENew*); void expr_null(const NetENull*); void expr_param(const NetEConstParam*); void expr_property(const NetEProperty*); void expr_rparam(const NetECRealParam*); void expr_event(const NetEEvent*); void expr_scope(const NetEScope*); void expr_scopy(const NetEShallowCopy*); void expr_netenum(const NetENetenum*); void expr_select(const NetESelect*); void expr_sfunc(const NetESFunc*); void expr_ternary(const NetETernary*); void expr_ufunc(const NetEUFunc*); void expr_unary(const NetEUnary*); void expr_signal(const NetESignal*); ivl_scope_t lookup_scope_(const NetScope*scope); ivl_attribute_s* fill_in_attributes(const Attrib*net); void switch_attributes(struct ivl_switch_s *obj, const NetNode*net); void logic_attributes(struct ivl_net_logic_s *obj, const NetNode*net); private: StringHeap strings_; static ivl_scope_t find_scope(ivl_design_s &des, const NetScope*cur); static ivl_signal_t find_signal(ivl_design_s &des, const NetNet*net); static ivl_parameter_t scope_find_param(ivl_scope_t scope, const char*name); void add_root(const NetScope *s); bool make_assign_lvals_(const NetAssignBase*net); bool make_single_lval_(const LineInfo*li, struct ivl_lval_s*cur, const NetAssign_*asn); void sub_off_from_expr_(long); void mul_expr_by_const_(long); void make_delays_(ivl_expr_t*delay, const NetObj*net); void make_logic_delays_(struct ivl_net_logic_s*obj, const NetObj*net); void make_switch_delays_(struct ivl_switch_s*obj, const NetObj*net); void make_lpm_delays_(struct ivl_lpm_s*obj, const NetObj*net); void make_const_delays_(struct ivl_net_const_s*obj, const NetObj*net); void make_scope_parameters(ivl_scope_t scope, const NetScope*net); void make_scope_param_expr(ivl_parameter_t cur_par, NetExpr*etmp); ivl_event_t make_lpm_trigger(const NetEvWait*ev); bool lpm_arith1_(ivl_lpm_type_t lpm_type, unsigned wid, bool signed_flag, const NetNode*net); static ivl_expr_t expr_from_value_(const verinum&that); }; extern struct dll_target dll_target_obj; /* * These are various private declarations used by the t-dll target. */ struct ivl_delaypath_s { ivl_scope_t scope; ivl_nexus_t src; ivl_nexus_t condit; bool conditional; bool posedge; bool negedge; uint64_t delay[12]; }; struct ivl_event_s { perm_string name; ivl_scope_t scope; perm_string file; unsigned lineno; unsigned nany, nneg, npos; ivl_nexus_t*pins; }; /* * The ivl_expr_t is an opaque reference to one of these * structures. This structure holds all the information we need about * an expression node, including its type, the expression width, and * type specific properties. */ struct ivl_expr_s { ivl_expr_type_t type_; ivl_variable_type_t value_; ivl_type_t net_type; perm_string file; unsigned lineno; unsigned width_; unsigned signed_ : 1; unsigned sized_ : 1; union { struct { char op_; ivl_expr_t lef_; ivl_expr_t rig_; } binary_; struct { size_t parms; ivl_expr_t*parm; } array_pattern_; struct { ivl_select_type_t sel_type_; ivl_expr_t expr_; ivl_expr_t base_; } select_; struct { ivl_expr_t dest; ivl_expr_t src; } shallow_; struct { ivl_branch_t branch; ivl_nature_t nature; } branch_; struct { unsigned rept; unsigned parms; ivl_expr_t*parm; } concat_; struct { char*bits_; ivl_parameter_t parameter; } number_; struct { ivl_event_t event; } event_; struct { ivl_scope_t scope; } scope_; struct { ivl_enumtype_t type; } enumtype_; struct { ivl_signal_t sig; ivl_expr_t word; } signal_; struct { const char *name_; ivl_expr_t *parm; unsigned parms; } sfunc_; struct { char*value_; ivl_parameter_t parameter; } string_; struct { ivl_expr_t cond; ivl_expr_t true_e; ivl_expr_t false_e; } ternary_; struct { ivl_memory_t mem_; ivl_expr_t idx_; } memory_; struct { ivl_scope_t def; ivl_expr_t *parm; unsigned parms; } ufunc_; struct { unsigned long value; } ulong_; struct { double value; ivl_parameter_t parameter; } real_; struct { char op_; ivl_expr_t sub_; } unary_; struct { uint64_t value; } delay_; struct { ivl_expr_t size; ivl_expr_t init_val; } new_; struct { ivl_signal_t sig; unsigned prop_idx; ivl_expr_t index; } property_; } u_; }; /* * LPM devices are handled by this suite of types. The ivl_lpm_s * structure holds the core, including a type code, the object name * and scope. The other properties of the device are held in the type * specific member of the union. */ struct ivl_lpm_s { ivl_lpm_type_t type; ivl_scope_t scope; perm_string name; perm_string file; unsigned lineno; // Value returned by ivl_lpm_width; unsigned width; ivl_expr_t delay[3]; union { struct ivl_lpm_ff_s { unsigned negedge_flag :1; ivl_nexus_t clk; ivl_nexus_t we; ivl_nexus_t aclr; ivl_nexus_t aset; ivl_nexus_t sclr; ivl_nexus_t sset; union { ivl_nexus_t*pins; ivl_nexus_t pin; } q; union { ivl_nexus_t*pins; ivl_nexus_t pin; } d; ivl_expr_t aset_value; ivl_expr_t sset_value; } ff; struct ivl_lpm_mux_s { unsigned size; unsigned swid; ivl_nexus_t*d; ivl_nexus_t q, s; } mux; struct ivl_lpm_shift_s { unsigned select; unsigned signed_flag :1; ivl_nexus_t q, d, s; } shift; struct ivl_lpm_arith_s { unsigned signed_flag :1; ivl_nexus_t q, a, b; } arith; struct ivl_lpm_array_s { ivl_signal_t sig; unsigned swid; ivl_nexus_t q, a; } array; struct ivl_concat_s { unsigned inputs; ivl_nexus_t*pins; } concat; struct ivl_part_s { unsigned base; unsigned signed_flag :1; ivl_nexus_t q, a, s; } part; // IVL_LPM_RE_* and IVL_LPM_SIGN_EXT use this. struct ivl_lpm_reduce_s { ivl_nexus_t q, a; } reduce; struct ivl_lpm_repeat_s { unsigned count; ivl_nexus_t q, a; } repeat; struct ivl_lpm_sfunc_s { const char* fun_name; unsigned ports; ivl_nexus_t*pins; ivl_event_t trigger; } sfunc; struct ivl_lpm_substitute { unsigned base; ivl_nexus_t q, a, s; } substitute; struct ivl_lpm_ufunc_s { ivl_scope_t def; unsigned ports; ivl_nexus_t*pins; ivl_event_t trigger; } ufunc; } u_; }; /* * This object represents l-values to assignments. The l-value can be * a register bit or part select, or a memory word select with a part * select. */ enum ivl_lval_type_t { IVL_LVAL_REG = 0, IVL_LVAL_ARR = 4, IVL_LVAL_LVAL= 5 // Nested l-value }; struct ivl_lval_s { ivl_expr_t loff; ivl_select_type_t sel_type :3; ivl_expr_t idx; unsigned width_; unsigned type_ : 8; /* values from ivl_lval_type_t */ int property_idx; union { ivl_signal_t sig; ivl_lval_t nest; // type_ == IVL_LVAL_LVAL } n; }; /* * This object represents a literal constant, possibly signed, in a * structural context. */ struct ivl_net_const_s { ivl_variable_type_t type : 4; unsigned width_ : 24; unsigned signed_ : 1; perm_string file; unsigned lineno; ivl_scope_t scope; union { double real_value; char bit_[sizeof(char*)]; const char* bits_; } b; ivl_nexus_t pin_; ivl_expr_t delay[3]; void* operator new (size_t s); void operator delete(void*obj, size_t s); // Not implemented }; /* * Logic gates (just about everything that has a single output) are * represented structurally by instances of this object. */ struct ivl_net_logic_s { ivl_logic_t type_; unsigned width_; unsigned is_cassign; ivl_udp_t udp; perm_string name_; ivl_scope_t scope_; perm_string file; unsigned lineno; unsigned npins_; ivl_nexus_t*pins_; struct ivl_attribute_s*attr; unsigned nattr; ivl_expr_t delay[3]; }; struct ivl_switch_s { ivl_switch_type_t type; unsigned width; unsigned part; unsigned offset; perm_string name; ivl_scope_t scope; ivl_island_t island; struct ivl_attribute_s*attr; unsigned nattr; ivl_expr_t delay[3]; ivl_nexus_t pins[3]; perm_string file; unsigned lineno; }; /* * UDP definition. */ struct ivl_udp_s { perm_string name; unsigned nin; int sequ; /* boolean */ char init; unsigned nrows; typedef const char*ccharp_t; ccharp_t*table; // zero terminated array of pointers perm_string file; unsigned lineno; string*ports; }; /* * The ivl_nexus_t is a single-bit link of some number of pins of * devices. the __nexus_ptr structure is a helper that actually does * the pointing. * * The type_ member specifies which of the object pointers in the * union are valid. * * The drive01 members gives the strength of the drive that the device * is applying to the nexus, with 0 HiZ and 3 supply. If the pin is an * input to the device, then the drives are both HiZ. */ struct ivl_nexus_ptr_s { unsigned pin_; unsigned type_ : 8; unsigned drive0 : 3; unsigned drive1 : 3; union { ivl_signal_t sig; /* type 0 */ ivl_net_logic_t log; /* type 1 */ ivl_net_const_t con; /* type 2 */ ivl_lpm_t lpm; /* type 3 */ ivl_switch_t swi; /* type 4 */ ivl_branch_t bra; /* type 5 */ } l; }; # define __NEXUS_PTR_SIG 0 # define __NEXUS_PTR_LOG 1 # define __NEXUS_PTR_CON 2 # define __NEXUS_PTR_LPM 3 # define __NEXUS_PTR_SWI 4 # define __NEXUS_PTR_BRA 5 /* * NOTE: ONLY allocate ivl_nexus_s objects with the included "new" operator. */ struct ivl_nexus_s { ivl_nexus_s() : ptrs_(1), nexus_(0), name_(0), private_data(0) { } vectorptrs_; const Nexus*nexus_; const char*name_; void*private_data; void* operator new (size_t s); void operator delete(void*obj, size_t s); // Not implemented }; /* * This is the implementation of a parameter. Each scope has a list of * these. */ struct ivl_parameter_s { perm_string basename; ivl_scope_t scope; ivl_expr_t value; long msb; long lsb; bool signed_flag; bool local; perm_string file; unsigned lineno; }; /* * All we know about a process is its type (initial or always) and the * single statement that is it. A process also has a scope, although * that generally only matters for VPI calls. */ struct ivl_process_s { ivl_process_type_t type_ : 2; unsigned int analog_flag : 1; ivl_scope_t scope_; ivl_statement_t stmt_; perm_string file; unsigned lineno; struct ivl_attribute_s*attr; unsigned nattr; ivl_process_t next_; }; /* * Scopes are kept in a tree. Each scope points to its first child, * and also to any siblings. Thus a parent can scan all its children * by following its child pointer, then following sibling pointers from * there. */ struct ivl_scope_s { ivl_scope_t parent; std::map children; // This is just like the children map above, but in vector // form for convenient access. std::vector child; perm_string name_; perm_string tname_; perm_string file; perm_string def_file; unsigned lineno; unsigned def_lineno; ivl_scope_type_t type_; std::vector classes; std::vector enumerations_; std::vector sigs_; unsigned nlog_; ivl_net_logic_t*log_; unsigned nevent_; ivl_event_t* event_; unsigned nlpm_; ivl_lpm_t* lpm_; std::vector param; /* Scopes that are tasks/functions have a definition. */ ivl_statement_t def; unsigned is_auto; unsigned is_cell; // Ports of Module scope (just introspection data for VPI) - actual connections // are nets defined in u_.net (may be > 1 per module port) std::vector module_ports_info; unsigned ports; union { ivl_signal_t*port; ivl_nexus_t*nex; NetNet**net; } u_; std::vectorswitches; signed int time_precision :8; signed int time_units :8; struct ivl_attribute_s*attr; unsigned nattr; }; /* * A signal is a thing like a wire, a reg, or whatever. It has a type, * and if it is a port is also has a direction. Signals are collected * into scopes (which also point back to me) and have pins that * connect to the rest of the netlist. */ struct ivl_signal_s { ivl_signal_type_t type_; ivl_signal_port_t port_; int module_port_index_; ivl_discipline_t discipline; perm_string file; unsigned lineno; // This is the type for the signal ivl_type_t net_type; unsigned local_ : 1; unsigned forced_net_ : 1; /* For now, support only 0 or 1 array dimensions. */ unsigned array_dimensions_ : 8; unsigned array_addr_swapped : 1; /* These encode the declared packed dimensions for the signal, in case they are needed by the run-time */ std::vector packed_dims; perm_string name_; ivl_scope_t scope_; unsigned array_words; int array_base; union { ivl_nexus_t pin; ivl_nexus_t*pins; }; ivl_delaypath_s*path; unsigned npath; struct ivl_attribute_s*attr; unsigned nattr; }; /* * The ivl_statement_t represents any statement. The type of statement * is defined by the ivl_statement_type_t enumeration. Given the type, * certain information about the statement may be available. */ struct ivl_statement_s { enum ivl_statement_type_e type_; perm_string file; unsigned lineno; union { struct { /* IVL_ST_ALLOC */ ivl_scope_t scope; } alloc_; struct { /* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN, IVL_ST_DEASSIGN */ unsigned lvals_; struct ivl_lval_s*lval_; char oper; // Operator if this is a compressed assignment. ivl_expr_t rval_; ivl_expr_t delay; // The following are only for NB event control. ivl_expr_t count; unsigned nevent; union { ivl_event_t event; ivl_event_t*events; }; } assign_; struct { /* IVL_ST_BLOCK, IVL_ST_FORK */ struct ivl_statement_s*stmt_; unsigned nstmt_; ivl_scope_t scope; } block_; struct { /* IVL_ST_CASE, IVL_ST_CASEX, IVL_ST_CASEZ */ ivl_expr_t cond; unsigned ncase; ivl_expr_t*case_ex; struct ivl_statement_s*case_st; } case_; struct { /* IVL_ST_CONDIT */ /* This is the condition expression */ ivl_expr_t cond_; /* This is two statements, the true and false. */ struct ivl_statement_s*stmt_; } condit_; struct { /* IVL_ST_CONTRIB */ ivl_expr_t lval; ivl_expr_t rval; } contrib_; struct { /* IVL_ST_DELAY */ uint64_t value; ivl_statement_t stmt_; } delay_; struct { /* IVL_ST_DELAYX */ ivl_expr_t expr; /* XXXX */ ivl_statement_t stmt_; } delayx_; struct { /* IVL_ST_DISABLE */ ivl_scope_t scope; } disable_; struct { /* IVL_ST_FOREVER */ ivl_statement_t stmt_; } forever_; struct { /* IVL_ST_FREE */ ivl_scope_t scope; } free_; struct { /* IVL_ST_STASK */ const char*name_; ivl_sfunc_as_task_t sfunc_as_task_; unsigned nparm_; ivl_expr_t*parms_; } stask_; struct { /* IVL_ST_UTASK */ ivl_scope_t def; } utask_; struct { /* IVL_ST_TRIGGER IVL_ST_WAIT */ unsigned nevent; union { ivl_event_t event; ivl_event_t*events; }; ivl_statement_t stmt_; } wait_; struct { /* IVL_ST_WHILE IVL_ST_REPEAT */ ivl_expr_t cond_; ivl_statement_t stmt_; } while_; } u_; }; /* * The FILE_NAME function is a shorthand for attaching file/line * information to the statement object. */ static inline void FILE_NAME(ivl_expr_t expr, const LineInfo*info) { expr->file = info->get_file(); expr->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_event_t event, const LineInfo*info) { event->file = info->get_file(); event->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_lpm_t lpm, const LineInfo*info) { lpm->file = info->get_file(); lpm->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_net_const_t net, const LineInfo*info) { net->file = info->get_file(); net->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_net_logic_t net, const LineInfo*info) { net->file = info->get_file(); net->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_parameter_t net, const LineInfo*info) { net->file = info->get_file(); net->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_process_t net, const LineInfo*info) { net->file = info->get_file(); net->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_scope_t scope, const NetScope*info) { scope->file = info->get_file(); scope->def_file = info->get_def_file(); scope->lineno = info->get_lineno(); scope->def_lineno = info->get_def_lineno(); } static inline void FILE_NAME(ivl_statement_t stmt, const LineInfo*info) { stmt->file = info->get_file(); stmt->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_switch_t net, const LineInfo*info) { net->file = info->get_file(); net->lineno = info->get_lineno(); } static inline void FILE_NAME(ivl_signal_t net, const LineInfo*info) { net->file = info->get_file(); net->lineno = info->get_lineno(); } #endif /* IVL_t_dll_H */ iverilog-10_1/t-dll.txt000066400000000000000000000040631265551621300151220ustar00rootroot00000000000000 LOADABLE TARGETS Icarus Verilog supports dynamically loading code generator modules to perform the back-end processing of the completed design. The user specifies on the command line the module to load. The compiler loads the module (once the design is compiled and elaborated) and calls it to finally handle the design. Loadable target modules implement a set of functions that the core compiler calls to pass the design to it, and the module in turn uses a collection of functions in the core (the API) to access details of the design. LOADING TARGET MODULES The target module loader is invoked with the ivl flag "-tdll". That is, the DLL loader is a linked in target type. The name of the target module to load is then specified with the DLL flag, i.e. "-fDLL=". COMPILING TARGET MODULES LOADABLE TARGET MODULE API The target module API is defined in the ivl_target.h header file. This declares all the type and functions that a loadable module needs to access the design. ABOUT SPECIFIC EXPRESSION TYPES In this section find notes about the various kinds of expression nodes. The notes here are in addition to the more general documentation in the ivl_target.h header file. * IVL_EX_CONCAT The concatenation operator forms an expression node that holds the repeat count and all the parameter expressions. The repeat count is an integer that is calculated by the core compiler so it fully evaluated, and *not* an expression. The parameter expressions are retrieved by the ivl_expr_parm method, with the index increasing as parameters go from left to right, from most significant to least significant. (Note that this is different from the order of bits within an expression node.) * IVL_EX_NUMBER This is a constant number. The width is fully known, and the bit values are all represented by the ASCII characters 0, 1, x or z. The ivl_expr_bits method returns a pointer to the least significant bit, and the remaining bits are ordered from least significant to most significant. For example, 5'b1zzx0 is the 5 character string "0xzz1". iverilog-10_1/target.cc000066400000000000000000000347121265551621300151460ustar00rootroot00000000000000/* * Copyright (c) 1998-2013 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include # include "target.h" # include target_t::~target_t() { } void target_t::scope(const NetScope*) { } void target_t::convert_module_ports(const NetScope*) { } bool target_t::branch(const NetBranch*obj) { cerr << obj->get_fileline() << ": error: target (" << typeid(*this).name() << "): Unhandled branch." << endl; return false; } bool target_t::class_type(const NetScope*, netclass_t*obj) { cerr << "<>:0" << ": error: target (" << typeid(*this).name() << "): Unhandled class_type <" << obj << ">." << endl; return false; } void target_t::event(const NetEvent*ev) { cerr << ev->get_fileline() << ": error: target (" << typeid(*this).name() << "): Unhandled event <" << ev->name() << ">." << endl; } bool target_t::enumeration(const NetScope*, netenum_t*obj) { cerr << "<>:0" << ": error: target (" << typeid(*this).name() << "): Unhandled enumeration <" << obj << ">." << endl; return false; } bool target_t::signal_paths(const NetNet*) { return true; } bool target_t::func_def(const NetScope*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled function definition." << endl; return false; } void target_t::task_def(const NetScope*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled task definition." << endl; } void target_t::logic(const NetLogic*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled logic gate" << endl; } bool target_t::tran(const NetTran*) { cerr << "target (" << typeid(*this).name() << "): " << "TRAN devices not supported." << endl; return false; } bool target_t::bufz(const NetBUFZ*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled continuous assign (BUFZ)." << endl; return false; } void target_t::udp(const NetUDP*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled UDP." << endl; } bool target_t::ureduce(const NetUReduce*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled unary reduction logic gate." << endl; return false; } void target_t::lpm_abs(const NetAbs*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetAbs." << endl; } void target_t::lpm_add_sub(const NetAddSub*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetAddSub." << endl; } bool target_t::lpm_array_dq(const NetArrayDq*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetArrayDq." << endl; return false; } bool target_t::lpm_cast_int2(const NetCastInt2*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetCastInt2." << endl; return false; } bool target_t::lpm_cast_int4(const NetCastInt4*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetCastInt4." << endl; return false; } bool target_t::lpm_cast_real(const NetCastReal*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetCastReal." << endl; return false; } void target_t::lpm_clshift(const NetCLShift*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetCLShift." << endl; } void target_t::lpm_compare(const NetCompare*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetCompare." << endl; } void target_t::lpm_divide(const NetDivide*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetDivide." << endl; } void target_t::lpm_modulo(const NetModulo*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetModulo." << endl; } void target_t::lpm_ff(const NetFF*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetFF." << endl; } void target_t::lpm_mult(const NetMult*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetMult." << endl; } void target_t::lpm_mux(const NetMux*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetMux." << endl; } void target_t::lpm_pow(const NetPow*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetPow." << endl; } bool target_t::concat(const NetConcat*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetConcat." << endl; return false; } bool target_t::part_select(const NetPartSelect*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetPartSelect." << endl; return false; } bool target_t::replicate(const NetReplicate*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetReplicate." << endl; return false; } void target_t::net_case_cmp(const NetCaseCmp*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled case compare node." << endl; } bool target_t::net_const(const NetConst*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled CONSTANT node." << endl; return false; } bool target_t::net_sysfunction(const NetSysFunc*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetSysFunc node." << endl; return false; } bool target_t::net_function(const NetUserFunc*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetUserFunc node." << endl; return false; } bool target_t::net_literal(const NetLiteral*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled LITERAL node." << endl; return false; } void target_t::net_probe(const NetEvProbe*net) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled probe trigger node" << endl; net->dump_node(cerr, 4); } bool target_t::sign_extend(const NetSignExtend*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetSignExtend node." << endl; return false; } bool target_t::substitute(const NetSubstitute*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled NetSubstitute node." << endl; return false; } bool target_t::process(const NetProcTop*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled process(NetProcTop)." << endl; return false; } bool target_t::process(const NetAnalogTop*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled process(NetAnalogTop)." << endl; return false; } void target_t::proc_alloc(const NetAlloc*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_alloc." << endl; } bool target_t::proc_assign(const NetAssign*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled procedural assignment." << endl; return false; } void target_t::proc_assign_nb(const NetAssignNB*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled non-blocking assignment." << endl; } bool target_t::proc_block(const NetBlock*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_block." << endl; return false; } void target_t::proc_case(const NetCase*cur) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled case:" << endl; cur->dump(cerr, 6); } bool target_t::proc_cassign(const NetCAssign*dev) { cerr << "target (" << typeid(*this).name() << "): "; cerr << dev->get_fileline(); cerr << ": Target does not support procedural continuous assignment." << endl; return false; } bool target_t::proc_condit(const NetCondit*condit) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled conditional:" << endl; condit->dump(cerr, 6); return false; } bool target_t::proc_contribution(const NetContribution*net) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled contribution:" << endl; net->dump(cerr, 6); return false; } bool target_t::proc_deassign(const NetDeassign*dev) { cerr << dev->get_fileline() << ": internal error: " << "target (" << typeid(*this).name() << "): " << "Unhandled proc_deassign." << endl; return false; } bool target_t::proc_delay(const NetPDelay*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_delay." << endl; return false; } bool target_t::proc_disable(const NetDisable*obj) { cerr << obj->get_fileline() << ": internal error: " << "target (" << typeid(*this).name() << "): " << "does not support disable statements." << endl; return false; } void target_t::proc_do_while(const NetDoWhile*net) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled do/while:" << endl; net->dump(cerr, 6); } bool target_t::proc_force(const NetForce*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_force." << endl; return false; } void target_t::proc_forever(const NetForever*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_forever." << endl; } void target_t::proc_free(const NetFree*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_free." << endl; } bool target_t::proc_release(const NetRelease*dev) { cerr << dev->get_fileline() << ": internal error: " << "target (" << typeid(*this).name() << "): " << "Unhandled proc_release." << endl; return false; } void target_t::proc_repeat(const NetRepeat*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_repeat." << endl; } bool target_t::proc_trigger(const NetEvTrig*tr) { cerr << tr->get_fileline() << ": error: target (" << typeid(*this).name() << "): Unhandled event trigger." << endl; return false; } void target_t::proc_stask(const NetSTask*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_stask." << endl; } void target_t::proc_utask(const NetUTask*) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled proc_utask." << endl; } bool target_t::proc_wait(const NetEvWait*tr) { cerr << tr->get_fileline() << ": error: target (" << typeid(*this).name() << "): Unhandled event wait." << endl; return false; } void target_t::proc_while(const NetWhile*net) { cerr << "target (" << typeid(*this).name() << "): " "Unhandled while:" << endl; net->dump(cerr, 6); } int target_t::end_design(const Design*) { return 0; } expr_scan_t::~expr_scan_t() { } void expr_scan_t::expr_access_func(const NetEAccess*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_access_func." << endl; } void expr_scan_t::expr_array_pattern(const NetEArrayPattern*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_array_pattern." << endl; } void expr_scan_t::expr_const(const NetEConst*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_const." << endl; } void expr_scan_t::expr_last(const NetELast*exp) { cerr << exp->get_fileline() << ": expr_scan_t(" << typeid(*this).name() << "): " << "unhandled expr_last." << endl; } void expr_scan_t::expr_new(const NetENew*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_new." << endl; } void expr_scan_t::expr_null(const NetENull*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_null." << endl; } void expr_scan_t::expr_param(const NetEConstParam*that) { expr_const(that); } void expr_scan_t::expr_property(const NetEProperty*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_property." << endl; } void expr_scan_t::expr_creal(const NetECReal*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_creal." << endl; } void expr_scan_t::expr_rparam(const NetECRealParam*that) { expr_creal(that); } void expr_scan_t::expr_concat(const NetEConcat*that) { cerr << that->get_fileline() << ": expr_scan_t (" << typeid(*this).name() << "): unhandled expr_concat." << endl; } void expr_scan_t::expr_event(const NetEEvent*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_event." << endl; } void expr_scan_t::expr_netenum(const NetENetenum*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_netenum." << endl; } void expr_scan_t::expr_scope(const NetEScope*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_scope." << endl; } void expr_scan_t::expr_scopy(const NetEShallowCopy*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_scopy." << endl; } void expr_scan_t::expr_select(const NetESelect*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_select." << endl; } void expr_scan_t::expr_sfunc(const NetESFunc*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_sfunc." << endl; } void expr_scan_t::expr_signal(const NetESignal*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_signal." << endl; } void expr_scan_t::expr_ternary(const NetETernary*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_ternary." << endl; } void expr_scan_t::expr_ufunc(const NetEUFunc*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled function call." << endl; } void expr_scan_t::expr_unary(const NetEUnary*) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_unary." << endl; } void expr_scan_t::expr_binary(const NetEBinary*ex) { cerr << "expr_scan_t (" << typeid(*this).name() << "): " "unhandled expr_binary: " <<*ex << endl; } iverilog-10_1/target.h000066400000000000000000000170731265551621300150110ustar00rootroot00000000000000#ifndef IVL_target_H #define IVL_target_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "netlist.h" /* * This header file describes the types and constants used to describe * the possible target output types of the compiler. The backends * provide one of these in order to tell the previous steps what the * backend is able to do. */ /* * The backend driver is hooked into the compiler, and given a name, * by creating an instance of the target structure. The structure has * the name that the compiler will use to locate the driver, and a * pointer to a target_t object that is the actual driver. */ struct target { const char* name; struct target_t* meth; }; /* * The emit process uses a target_t driver to send the completed * design to a file. It is up to the driver object to follow along in * the iteration through the design, generating output as it can. */ struct target_t { inline target_t() : errors(0) { } virtual ~target_t(); /* Set this to count errors encountered during emit. */ int errors; /* Start the design. This sets the main output file stream that the target should use. */ virtual bool start_design(const Design*) =0; /* This is called once for each scope in the design, before anything else is called. */ virtual void scope(const NetScope*); virtual bool class_type(const NetScope*, netclass_t*); /* This is called to convert module ports from a NetNet* to an * ivl_signal_t object. */ virtual void convert_module_ports(const NetScope*); /* Output an event object. Called for each named event in the scope. */ virtual void event(const NetEvent*); /* Output an enumeration typespec. */ virtual bool enumeration(const NetScope*, netenum_t*); /* Output a signal (called for each signal) */ virtual void signal(const NetNet*) =0; virtual bool signal_paths(const NetNet*); /* Analog branches */ virtual bool branch(const NetBranch*); /* Output a defined task. */ virtual void task_def(const NetScope*); virtual bool func_def(const NetScope*); /* LPM style components are handled here. */ virtual void lpm_abs(const NetAbs*); virtual void lpm_add_sub(const NetAddSub*); virtual bool lpm_array_dq(const NetArrayDq*); virtual void lpm_clshift(const NetCLShift*); virtual bool lpm_cast_int2(const NetCastInt2*); virtual bool lpm_cast_int4(const NetCastInt4*); virtual bool lpm_cast_real(const NetCastReal*); virtual void lpm_compare(const NetCompare*); virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); virtual void lpm_ff(const NetFF*); virtual void lpm_mult(const NetMult*); virtual void lpm_mux(const NetMux*); virtual void lpm_pow(const NetPow*); virtual bool concat(const NetConcat*); virtual bool part_select(const NetPartSelect*); virtual bool replicate(const NetReplicate*); /* Output a gate (called for each gate) */ virtual void logic(const NetLogic*); virtual bool tran(const NetTran*); virtual bool ureduce(const NetUReduce*); /* unary reduction operator */ virtual bool bufz(const NetBUFZ*); virtual void udp(const NetUDP*); virtual void net_case_cmp(const NetCaseCmp*); virtual bool net_const(const NetConst*); virtual bool net_sysfunction(const NetSysFunc*); virtual bool net_function(const NetUserFunc*); virtual bool net_literal(const NetLiteral*); virtual void net_probe(const NetEvProbe*); virtual bool sign_extend(const NetSignExtend*); virtual bool substitute(const NetSubstitute*); /* Output a process (called for each process). It is up to the target to recurse if desired. */ virtual bool process(const NetProcTop*); virtual bool process(const NetAnalogTop*); /* Various kinds of process nodes are dispatched through these. */ virtual void proc_alloc(const NetAlloc*); virtual bool proc_assign(const NetAssign*); virtual void proc_assign_nb(const NetAssignNB*); virtual bool proc_block(const NetBlock*); virtual void proc_case(const NetCase*); virtual bool proc_cassign(const NetCAssign*); virtual bool proc_condit(const NetCondit*); virtual bool proc_contribution(const NetContribution*); virtual bool proc_deassign(const NetDeassign*); virtual bool proc_delay(const NetPDelay*); virtual bool proc_disable(const NetDisable*); virtual void proc_do_while(const NetDoWhile*); virtual bool proc_force(const NetForce*); virtual void proc_forever(const NetForever*); virtual void proc_free(const NetFree*); virtual bool proc_release(const NetRelease*); virtual void proc_repeat(const NetRepeat*); virtual bool proc_trigger(const NetEvTrig*); virtual void proc_stask(const NetSTask*); virtual void proc_utask(const NetUTask*); virtual bool proc_wait(const NetEvWait*); virtual void proc_while(const NetWhile*); /* Done with the design. The target returns !0 if there is some error in the code generation. */ virtual int end_design(const Design*); }; /* This class is used by the NetExpr class to help with the scanning of expressions. */ struct expr_scan_t { virtual ~expr_scan_t(); virtual void expr_access_func(const NetEAccess*); virtual void expr_array_pattern(const NetEArrayPattern*); virtual void expr_const(const NetEConst*); virtual void expr_last(const NetELast*); virtual void expr_new(const NetENew*); virtual void expr_null(const NetENull*); virtual void expr_param(const NetEConstParam*); virtual void expr_property(const NetEProperty*); virtual void expr_rparam(const NetECRealParam*); virtual void expr_creal(const NetECReal*); virtual void expr_concat(const NetEConcat*); virtual void expr_event(const NetEEvent*); virtual void expr_scope(const NetEScope*); virtual void expr_scopy(const NetEShallowCopy*); virtual void expr_select(const NetESelect*); virtual void expr_sfunc(const NetESFunc*); virtual void expr_signal(const NetESignal*); virtual void expr_ternary(const NetETernary*); virtual void expr_ufunc(const NetEUFunc*); virtual void expr_unary(const NetEUnary*); virtual void expr_binary(const NetEBinary*); virtual void expr_netenum(const NetENetenum*); }; /* This function takes a fully qualified Verilog name (which may have, for example, dots in it) and produces a mangled version that can be used by most any language. */ extern string mangle(const string&str); /* This function takes a string and produces an escaped version that can be used inside a string constant for a C++ compiler. */ extern string stresc(const string&str); #endif /* IVL_target_H */ iverilog-10_1/tgt-blif/000077500000000000000000000000001265551621300150525ustar00rootroot00000000000000iverilog-10_1/tgt-blif/Makefile.in000066400000000000000000000056311265551621300171240ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ O = blif.o constants.o logic_gate.o lpm.o lpm_add.o lpm_cmp_eq.o lpm_cmp_gt.o \ lpm_ff.o lpm_mux.o lpm_part_vp.o lpm_re_logic.o nex_data.o all: dep blif.tgt check: all clean: rm -rf *.o dep blif.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.cc) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-blif/$@ dep: mkdir dep %.o: %.cc $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif blif.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl$(suffix)/blif.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/blif.conf $(libdir)/ivl$(suffix)/blif-s.conf $(libdir)/ivl$(suffix)/blif.tgt: ./blif.tgt $(INSTALL_PROGRAM) ./blif.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.tgt" $(libdir)/ivl$(suffix)/blif.conf: $(srcdir)/blif.conf $(INSTALL_DATA) $(srcdir)/blif.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.conf" $(libdir)/ivl$(suffix)/blif-s.conf: $(srcdir)/blif-s.conf $(INSTALL_DATA) $(srcdir)/blif-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/blif-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.tgt" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/blif-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-blif/README-BLIF.txt000066400000000000000000000026221265551621300172640ustar00rootroot00000000000000 BLIF TARGET ----------- The BLIF code generator supports emitting the design to a blif format file as accepted by: ABC: A System for Sequential Synthesis and Verification This package contains tools sometimes used by ASIC designers. This blif target emits .blif file that the ABC system can read int via the "read_blif" command. USAGE ----- This code generator is intended to process structural Verilog source code. To convert a design to blif, use this command: iverilog -tblif -o.blif ... The source files can be Verilog, SystemVerilog, VHDL, whatever Icarus Verilog supports, so long as it elaborates down to the limited subset that the code generator supports. In other words, the files must be structural. The root module of the elaborated design becomes the model is generated. That module may instantiate sub-modules and so on down the design, completing the design. The output model is flattened, so it doesn't invoke any subcircuits. Bit vectors are exploded out at the model ports and internally. This is necessary since blif in particular and ABC in general processes bits, not vectors. LIMITATIONS ----------- Currently, only explicit logic gates and continuous assignments are supported. The design must contain only one root module. The name of that root module becomes the name of the blif model in the ".model" record. iverilog-10_1/tgt-blif/blif-s.conf000066400000000000000000000001401265551621300170700ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=blif.tgt iverilog-10_1/tgt-blif/blif.cc000066400000000000000000000142141265551621300162770ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "version_base.h" # include "version_tag.h" # include "priv.h" # include "ivl_target.h" # include "nex_data.h" # include # include # include # include using namespace std; /* * This is a BLIF target module. */ static const char*version_string = "Icarus Verilog BLIF Code Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (c) 2013,2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; int blif_errors = 0; static void emit_blif(const char*blif_path, ivl_design_t des, ivl_scope_t model); static int process_scan_fun(ivl_process_t net, void* /*raw*/) { fprintf(stderr, "%s:%u: sorry: BLIF: Processes not supported yet.\n", ivl_process_file(net), ivl_process_lineno(net)); blif_errors += 1; return 0; } int target_design(ivl_design_t des) { const char*blif_path = ivl_design_flag(des, "-o"); // Locate the root scope for the design. Note that the BLIF // format implies that there is a single root of the model. ivl_scope_t*roots; unsigned nroots; ivl_design_roots(des, &roots, &nroots); if (nroots != 1) { fprintf(stderr, "BLIF: The BLIF code generator requires that there be only one root scope.\n"); return 1; } assert(roots[0]); if (ivl_scope_type(roots[0]) != IVL_SCT_MODULE) { fprintf(stderr, "BLIF: The root scope %s must be a module.\n", ivl_scope_basename(roots[0])); return 1; } // Detect processes and dispatch them. ivl_design_process(des, &process_scan_fun, 0); // Emit to the destination file. assert(blif_path); emit_blif(blif_path, des, roots[0]); return blif_errors; } const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } /* * Print all the bits of a signal. This is for the .input or .output * lines of a .model. All the bits need to be exploded, so print each * bit of a vector as its own name. */ static void print_signal_bits(FILE*fd, ivl_signal_t sig) { ivl_nexus_t nex = ivl_signal_nex(sig, 0); blif_nex_data_t* ned = blif_nex_data_t::get_nex_data(nex); ned->set_name(sig); for (unsigned idx = 0 ; idx < ned->get_width() ; idx += 1) { fprintf(fd, " %s%s", ned->get_name(), ned->get_name_index(idx)); } } static void emit_scope(FILE*fd, ivl_scope_t scope) { for (unsigned idx = 0 ; idx < ivl_scope_logs(scope) ; idx += 1) { ivl_net_logic_t net = ivl_scope_log(scope, idx); assert(net); blif_errors += print_logic_gate(fd, net); } for (unsigned idx = 0 ; idx < ivl_scope_lpms(scope) ; idx += 1) { ivl_lpm_t net = ivl_scope_lpm(scope, idx); blif_errors += print_lpm(fd, net); } for (size_t idx = 0 ; idx < ivl_scope_childs(scope) ; idx += 1) { ivl_scope_t child = ivl_scope_child(scope, idx); emit_scope(fd, child); } } static void emit_blif(const char*blif_path, ivl_design_t des, ivl_scope_t model) { FILE*fd = fopen(blif_path, "wt"); if (fd == 0) { perror(blif_path); blif_errors += 1; return; } fprintf(fd, ".model %s\n", ivl_scope_basename(model)); // The root scope vector ports_in; vector ports_out; for (unsigned idx = 0 ; idx < ivl_scope_sigs(model) ; idx += 1) { ivl_signal_t prt = ivl_scope_sig(model, idx); ivl_signal_port_t dir = ivl_signal_port(prt); switch (dir) { case IVL_SIP_NONE: break; case IVL_SIP_INPUT: ports_in.push_back(prt); break; case IVL_SIP_OUTPUT: ports_out.push_back(prt); break; case IVL_SIP_INOUT: fprintf(stderr, "BLIF: error: " "Model port %s is bi-directional.\n", ivl_signal_basename(prt)); blif_errors += 1; ports_in.push_back(prt); ports_out.push_back(prt); break; } } if (ports_in.size() > 0) { fprintf(fd, ".inputs"); for (size_t idx = 0 ; idx < ports_in.size() ; idx += 1) { ivl_signal_t prt = ports_in[idx]; print_signal_bits(fd, prt); } fprintf(fd, "\n"); } if (ports_out.size() > 0) { fprintf(fd, ".outputs"); for (size_t idx = 0 ; idx < ports_out.size() ; idx += 1) { ivl_signal_t prt = ports_out[idx]; print_signal_bits(fd, prt); } fprintf(fd, "\n"); } emit_scope(fd, model); emit_constants(fd, des, model); fprintf(fd, ".end\n"); fclose(fd); } bool scope_is_in_model(ivl_scope_t model, ivl_scope_t scope) { while (scope) { if (scope==model) return true; scope = ivl_scope_parent(scope); } return false; } iverilog-10_1/tgt-blif/blif.conf000066400000000000000000000001401265551621300166300ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=blif.tgt iverilog-10_1/tgt-blif/constants.cc000066400000000000000000000036171265551621300174040ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include static void print_constant(FILE*fd, ivl_net_const_t net) { unsigned wid = ivl_const_width(net); const char*val = ivl_const_bits(net); ivl_nexus_t nex = ivl_const_nex(net); blif_nex_data_t*ned = blif_nex_data_t::get_nex_data(nex); for (unsigned idx = 0 ; idx < wid ; idx += 1) { switch (val[idx]) { case '1': fprintf(fd, ".names %s%s # const 1\n1\n", ned->get_name(), ned->get_name_index(idx)); break; case '0': fprintf(fd, ".names %s%s # const 0\n", ned->get_name(), ned->get_name_index(idx)); break; default: fprintf(fd, ".names %s%s # const %c\n", ned->get_name(), ned->get_name_index(idx), val[idx]); break; } } } void emit_constants(FILE*fd, ivl_design_t des, ivl_scope_t model) { for (unsigned idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) { ivl_net_const_t net = ivl_design_const(des, idx); if (! scope_is_in_model(model, ivl_const_scope(net))) continue; print_constant(fd, net); } } iverilog-10_1/tgt-blif/cppcheck.sup000066400000000000000000000002731265551621300173650ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:blif.cc:66 // target_query() unusedFunction:blif.cc:98 iverilog-10_1/tgt-blif/logic_gate.cc000066400000000000000000000057721265551621300174710ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include static int do_print_logic_gate(FILE*fd, ivl_net_logic_t net, unsigned bit); int print_logic_gate(FILE*fd, ivl_net_logic_t net) { int rc = 0; for (unsigned idx = 0 ; idx < ivl_logic_width(net) ; idx += 1) { rc += do_print_logic_gate(fd, net, idx); if (rc != 0) break; } return rc; } static int do_print_logic_gate(FILE*fd, ivl_net_logic_t net, unsigned bit) { int rc = 0; ivl_nexus_t nex_out = ivl_logic_pin(net,0); blif_nex_data_t*ned_out = blif_nex_data_t::get_nex_data(nex_out); assert(ned_out->get_width() > bit); fprintf(fd, ".names"); for (unsigned idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { ivl_nexus_t nex = ivl_logic_pin(net,idx); blif_nex_data_t*ned = blif_nex_data_t::get_nex_data(nex); fprintf(fd, " %s%s", ned->get_name(), ned->get_name_index(bit)); } fprintf(fd, " %s%s", ned_out->get_name(), ned_out->get_name_index(bit)); fprintf(fd, "\n"); switch (ivl_logic_type(net)) { case IVL_LO_AND: for (unsigned idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) fprintf(fd, "1"); fprintf(fd, " 1\n"); break; case IVL_LO_OR: assert(ivl_logic_pins(net)==3); fprintf(fd, "1- 1\n"); fprintf(fd, "-1 1\n"); break; case IVL_LO_XOR: assert(ivl_logic_pins(net)==3); fprintf(fd, "10 1\n"); fprintf(fd, "01 1\n"); break; case IVL_LO_NAND: assert(ivl_logic_pins(net)==3); fprintf(fd, "0- 1\n"); fprintf(fd, "-0 1\n"); break; case IVL_LO_NOR: for (unsigned idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) fprintf(fd, "0"); fprintf(fd, " 1\n"); break; case IVL_LO_XNOR: assert(ivl_logic_pins(net)==3); fprintf(fd, "00 1\n"); fprintf(fd, "11 1\n"); break; case IVL_LO_BUF: assert(ivl_logic_pins(net)==2); fprintf(fd, "1 1\n"); break; case IVL_LO_NOT: assert(ivl_logic_pins(net)==2); fprintf(fd, "0 1\n"); break; default: fprintf(fd, "# ERROR: Logic type %d not handled\n", ivl_logic_type(net)); rc += 1; break; } return rc; } iverilog-10_1/tgt-blif/lpm.cc000066400000000000000000000065551265551621300161640ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include /* * Implement IVL_LPM_CONCAT devices by creating a .names buffer for * each bit of the output. Connect the output bit to the input bit. So * for example: * Q = {A, B, C} * becomes: * .names C Q[0] * 1 1 * .names B Q[1] * 1 1 * .names A Q[2] * 1 1 * (In this example, A, B, and C are 1 bit.) */ static int print_concat(FILE*fd, ivl_lpm_t net) { fprintf(fd, "# %s:%u: IVL_LPM_CONCAT: width=%u\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); ivl_nexus_t nex_q = ivl_lpm_q(net); blif_nex_data_t*ned_q = blif_nex_data_t::get_nex_data(nex_q); ivl_nexus_t nex_d = ivl_lpm_data(net,0); blif_nex_data_t*ned_d = blif_nex_data_t::get_nex_data(nex_d); unsigned didx = 0; unsigned dpos = 0; for (unsigned wid = 0 ; wid < ivl_lpm_width(net) ; wid += 1) { if (dpos >= ned_d->get_width()) { didx += 1; dpos = 0; nex_d = ivl_lpm_data(net,didx); ned_d = blif_nex_data_t::get_nex_data(nex_d); } fprintf(fd, ".names %s%s %s%s\n1 1\n", ned_d->get_name(), ned_d->get_name_index(dpos), ned_q->get_name(), ned_q->get_name_index(wid)); dpos += 1; } return 0; } int print_lpm(FILE*fd, ivl_lpm_t net) { int rc = 0; switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: rc += print_lpm_add(fd, net); break; case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EEQ: rc += print_lpm_cmp_eq(fd, net); break; case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: rc += print_lpm_cmp_gt(fd, net); break; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: rc += print_concat(fd, net); break; case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: rc += print_lpm_cmp_ne(fd, net); break; case IVL_LPM_FF: rc += print_lpm_ff(fd, net); break; case IVL_LPM_MUX: rc += print_lpm_mux(fd, net); break; case IVL_LPM_PART_VP: rc += print_lpm_part_vp(fd, net); break; case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: rc += print_lpm_re_logic(fd, net); break; case IVL_LPM_SUB: rc += print_lpm_sub(fd, net); break; default: fprintf(fd, "# XXXX ivl_lpm_type(net) --> %d\n", ivl_lpm_type(net)); fprintf(stderr, "%s:%u: sorry: ivl_lpm_type(net)==%d not implemented.\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_type(net)); rc += 1; break; } return rc; } iverilog-10_1/tgt-blif/lpm_add.cc000066400000000000000000000130741265551621300167660ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include /* * assign Q = A ^ B ^ Cin; * assign Cout = A&B | A&Cin | B&Cin; */ int print_lpm_add(FILE*fd, ivl_lpm_t net) { fprintf(fd, "# %s:%u: IVL_LPM_ADD: width=%u\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); ivl_nexus_t q_nex = ivl_lpm_q(net); ivl_nexus_t a_nex = ivl_lpm_data(net,0); ivl_nexus_t b_nex = ivl_lpm_data(net,1); blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); blif_nex_data_t*a_ned = blif_nex_data_t::get_nex_data(a_nex); blif_nex_data_t*b_ned = blif_nex_data_t::get_nex_data(b_nex); assert(ivl_lpm_width(net) == q_ned->get_width()); assert(ivl_lpm_width(net) == a_ned->get_width()); assert(ivl_lpm_width(net) == b_ned->get_width()); // Q[0] = A[0] ^ B[0] fprintf(fd, ".names %s%s %s%s %s%s\n" "10 1\n" "01 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); // Special case: If this is a 1-bit adder, then all we need is // the XOR above. We're done. if (ivl_lpm_width(net) == 1) return 0; // Cout[0] = A[0] & B[0] fprintf(fd, ".names %s%s %s%s %s%s/cout\n" "11 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); for (unsigned idx = 1 ; idx < ivl_lpm_width(net) ; idx += 1) { char cin[128]; snprintf(cin, sizeof cin, "%s%s/cout", q_ned->get_name(), q_ned->get_name_index(idx-1)); // Q[idx] = A[idx] ^ B[idx] ^ cin fprintf(fd, ".names %s%s %s%s %s %s%s\n" "001 1\n" "010 1\n" "100 1\n" "111 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), cin, q_ned->get_name(), q_ned->get_name_index(idx)); if ((idx+1) == ivl_lpm_width(net)) continue; // Cout[idx] = A[idx]&B[idx] | A[idx]&Cin | B[idx]&Cin fprintf(fd, ".names %s%s %s%s %s %s%s/cout\n" "011 1\n" "101 1\n" "11- 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), cin, q_ned->get_name(), q_ned->get_name_index(idx)); } return 0; } /* * Cin[0] = 1; * Cin[n: n!=0] = Cout[n-1] * assign Q[n] = A[n] ^ (~B[n]) ^ Cin[n]; * assign Cout[n] = A[n]&~B[n] | A[n]&Cin[n] | (~B[n])&Cin[n]; */ int print_lpm_sub(FILE*fd, ivl_lpm_t net) { fprintf(fd, "# %s:%u: IVL_LPM_SUB: width=%u\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); ivl_nexus_t q_nex = ivl_lpm_q(net); ivl_nexus_t a_nex = ivl_lpm_data(net,0); ivl_nexus_t b_nex = ivl_lpm_data(net,1); blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); blif_nex_data_t*a_ned = blif_nex_data_t::get_nex_data(a_nex); blif_nex_data_t*b_ned = blif_nex_data_t::get_nex_data(b_nex); assert(ivl_lpm_width(net) == q_ned->get_width()); assert(ivl_lpm_width(net) == a_ned->get_width()); assert(ivl_lpm_width(net) == b_ned->get_width()); // Q[0] = A[0] ^ ~B[0] ^ 1 fprintf(fd, ".names %s%s %s%s %s%s\n" "01 1\n" "10 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); if (ivl_lpm_width(net) == 1) return 0; // Cout[0] = A[0] & ~B[0] | A[0]&1 | ~B[0]&1 // = A[0] & ~B[0] | A[0] | ~B[0] // = A[0] | ~B[0] fprintf(fd, ".names %s%s %s%s %s%s/cout\n" "1- 1\n" "-0 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); for (unsigned idx = 1 ; idx < ivl_lpm_width(net) ; idx += 1) { char cin[128]; snprintf(cin, sizeof cin, "%s%s/cout", q_ned->get_name(), q_ned->get_name_index(idx-1)); // Q[idx] = A[idx] ^ ~B[idx] ^ cin fprintf(fd, ".names %s%s %s%s %s %s%s\n" "011 1\n" "000 1\n" "110 1\n" "101 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), cin, q_ned->get_name(), q_ned->get_name_index(idx)); if ((idx+1) == ivl_lpm_width(net)) continue; // Cout[idx] = A[idx]&~B[idx] | A[idx]&Cin | ~B[idx]&Cin fprintf(fd, ".names %s%s %s%s %s %s%s/cout\n" "001 1\n" "111 1\n" "10- 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), cin, q_ned->get_name(), q_ned->get_name_index(idx)); } return 0; } iverilog-10_1/tgt-blif/lpm_cmp_eq.cc000066400000000000000000000103371265551621300175010ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include /* * Implement IVL_LPM_CMP_EQ devices */ int print_lpm_cmp_eq(FILE*fd, ivl_lpm_t net) { fprintf(fd, "# %s:%u: IVL_LPM_CMP_EQ: width=%u\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); ivl_nexus_t q_nex = ivl_lpm_q(net); ivl_nexus_t a_nex = ivl_lpm_data(net,0); ivl_nexus_t b_nex = ivl_lpm_data(net,1); blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); blif_nex_data_t*a_ned = blif_nex_data_t::get_nex_data(a_nex); blif_nex_data_t*b_ned = blif_nex_data_t::get_nex_data(b_nex); assert(1 == q_ned->get_width()); assert(ivl_lpm_width(net) == a_ned->get_width()); assert(ivl_lpm_width(net) == b_ned->get_width()); if (ivl_lpm_width(net) == 1) { fprintf(fd, ".names %s%s %s%s %s%s\n" "11 1\n" "00 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); return 0; } for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, ".names %s%s %s%s %s/%u\n" "11 1\n" "00 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), q_ned->get_name(), idx); } fprintf(fd, ".names"); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) fprintf(fd, " %s/%u", q_ned->get_name(), idx); fprintf(fd, " %s\n", q_ned->get_name()); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) fputc('1', fd); fprintf(fd, " 1\n"); return 0; } /* * Implement IVL_LPM_CMP_NE devices */ int print_lpm_cmp_ne(FILE*fd, ivl_lpm_t net) { fprintf(fd, "# %s:%u: IVL_LPM_CMP_NE: width=%u\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); ivl_nexus_t q_nex = ivl_lpm_q(net); ivl_nexus_t a_nex = ivl_lpm_data(net,0); ivl_nexus_t b_nex = ivl_lpm_data(net,1); blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); blif_nex_data_t*a_ned = blif_nex_data_t::get_nex_data(a_nex); blif_nex_data_t*b_ned = blif_nex_data_t::get_nex_data(b_nex); assert(1 == q_ned->get_width()); assert(ivl_lpm_width(net) == a_ned->get_width()); assert(ivl_lpm_width(net) == b_ned->get_width()); if (ivl_lpm_width(net) == 1) { fprintf(fd, ".names %s%s %s%s %s%s\n" "10 1\n" "01 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); return 0; } for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, ".names %s%s %s%s %s/%u\n" "10 1\n" "01 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), q_ned->get_name(), idx); } fprintf(fd, ".names"); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) fprintf(fd, " %s/%u", q_ned->get_name(), idx); fprintf(fd, " %s\n", q_ned->get_name()); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { for (unsigned jdx = 0 ; jdx < ivl_lpm_width(net) ; jdx += 1) { if (idx==jdx) fputc('1', fd); else fputc('-', fd); } fprintf(fd, " 1\n"); } return 0; } iverilog-10_1/tgt-blif/lpm_cmp_gt.cc000066400000000000000000000125331265551621300175060ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include static int print_lpm_cmp_gt_s(FILE*fd, ivl_lpm_t net) { ivl_nexus_t q_nex = ivl_lpm_q(net); ivl_nexus_t a_nex = ivl_lpm_data(net,0); ivl_nexus_t b_nex = ivl_lpm_data(net,1); blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); blif_nex_data_t*a_ned = blif_nex_data_t::get_nex_data(a_nex); blif_nex_data_t*b_ned = blif_nex_data_t::get_nex_data(b_nex); assert(1 == q_ned->get_width()); assert(ivl_lpm_width(net) == a_ned->get_width()); assert(ivl_lpm_width(net) == b_ned->get_width()); // This is true if the operator is GE instead of just GT. bool ge_flag = ivl_lpm_type(net)==IVL_LPM_CMP_GE; // Special case: if the input vector is a single bit, then the // operation is trivial. Note that since this is signed, the // "1" bit represents -1 and is <0. if (ivl_lpm_width(net) == 1) { fprintf(fd, ".names %s%s %s%s %s%s\n" "01 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); if (ge_flag) fprintf(fd, "00 1\n11 1\n"); return 0; } fprintf(stderr, "%s:%u: sorry: blif: Signed magnitude compare not implemented yet\n", ivl_lpm_file(net), ivl_lpm_lineno(net)); return 1; } static int print_lpm_cmp_gt_u(FILE*fd, ivl_lpm_t net) { ivl_nexus_t q_nex = ivl_lpm_q(net); ivl_nexus_t a_nex = ivl_lpm_data(net,0); ivl_nexus_t b_nex = ivl_lpm_data(net,1); blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); blif_nex_data_t*a_ned = blif_nex_data_t::get_nex_data(a_nex); blif_nex_data_t*b_ned = blif_nex_data_t::get_nex_data(b_nex); assert(1 == q_ned->get_width()); assert(ivl_lpm_width(net) == a_ned->get_width()); assert(ivl_lpm_width(net) == b_ned->get_width()); // This is true if the operator is GE instead of just GT. bool ge_flag = ivl_lpm_type(net)==IVL_LPM_CMP_GE; // Special case: if the input vector is a single bit, then the // operation is trivial. if (ivl_lpm_width(net) == 1) { fprintf(fd, ".names %s%s %s%s %s%s\n" "10 1\n", a_ned->get_name(), a_ned->get_name_index(0), b_ned->get_name(), b_ned->get_name_index(0), q_ned->get_name(), q_ned->get_name_index(0)); if (ge_flag) fprintf(fd, "00 1\n11 1\n"); return 0; } // Calculate GT and EQ for each bit. for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, ".names %s%s %s%s %s%s/G%u\n" "10 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), q_ned->get_name(), q_ned->get_name_index(0), idx); } for (unsigned idx = ge_flag?0 : 1 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, ".names %s%s %s%s %s%s/E%u\n" "11 1\n" "00 1\n", a_ned->get_name(), a_ned->get_name_index(idx), b_ned->get_name(), b_ned->get_name_index(idx), q_ned->get_name(), q_ned->get_name_index(0), idx); } // Generate a wide function to blend the per-bit comparisons. fprintf(fd, ".names"); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, " %s%s/G%u", q_ned->get_name(), q_ned->get_name_index(0), idx); } for (unsigned idx = ge_flag?0:1 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, " %s%s/E%u", q_ned->get_name(), q_ned->get_name_index(0), idx); } fprintf(fd, " %s%s\n", q_ned->get_name(), q_ned->get_name_index(0)); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { for (unsigned jdx = 0 ; jdx < ivl_lpm_width(net) ; jdx += 1) { if (jdx != idx) fprintf(fd, "-"); else fprintf(fd, "1"); } for (unsigned jdx = ge_flag?0:1 ; jdx < ivl_lpm_width(net) ; jdx += 1) { if (jdx <= idx) fprintf(fd, "-"); else fprintf(fd, "1"); } fprintf(fd, " 1\n"); } if (ge_flag) { for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) fputc('-', fd); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) fputc('1', fd); fprintf(fd, " 1\n"); } return 0; } int print_lpm_cmp_gt(FILE*fd, ivl_lpm_t net) { fprintf(fd, "# %s:%u: LPM_LPM_CMP_GT: width=%u\n", ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); if (ivl_lpm_signed(net)) return print_lpm_cmp_gt_s(fd, net); else return print_lpm_cmp_gt_u(fd, net); } iverilog-10_1/tgt-blif/lpm_ff.cc000066400000000000000000000063301265551621300166260ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include using namespace std; int print_lpm_ff(FILE*fd, ivl_lpm_t net) { int errors = 0; ivl_nexus_t nex_q = ivl_lpm_q(net); blif_nex_data_t*ned_q = blif_nex_data_t::get_nex_data(nex_q); ivl_nexus_t nex_d = ivl_lpm_data(net,0); blif_nex_data_t*ned_d = blif_nex_data_t::get_nex_data(nex_d); ivl_nexus_t nex_c = ivl_lpm_clk(net); blif_nex_data_t*ned_c = blif_nex_data_t::get_nex_data(nex_c); ivl_nexus_t nex_ce = ivl_lpm_enable(net); blif_nex_data_t*ned_ce = nex_ce? blif_nex_data_t::get_nex_data(nex_ce) : 0; if (ivl_lpm_async_clr(net)) { errors += 1; fprintf(stderr, "%s:%u: sorry: blif: Asynchronous clear not implemented yet\n", ivl_lpm_file(net), ivl_lpm_lineno(net)); } if (ivl_lpm_async_set(net)) { errors += 1; fprintf(stderr, "%s:%u: sorry: blif: Asynchronous set not implemented yet\n", ivl_lpm_file(net), ivl_lpm_lineno(net)); } fprintf(fd, "# IVL_LPM_FF: width=%u, Q=%s, D=%s, C=%s, CE=%s\n", ivl_lpm_width(net), ned_q->get_name(), ned_d->get_name(), ned_c->get_name(), ned_ce? ned_ce->get_name() : "N/A"); if (ned_ce) { // If there is a clock-enable, rewrite this in a form // that blif can accept. Transform this: // // always @(posedge C) if (CE) Q <= D; // // to this: // // always @(posedge C) Q <= CE? D : Q; // // In ASIC-land, this is probably OK. for (unsigned wid = 0 ; wid < ivl_lpm_width(net) ; wid += 1) { fprintf(fd, ".names %s%s %s%s %s%s %s%s/EN\n" "0-1 1\n" "11- 1\n", ned_ce->get_name(), ned_ce->get_name_index(0), ned_d ->get_name(), ned_d ->get_name_index(wid), ned_q ->get_name(), ned_q ->get_name_index(wid), ned_d ->get_name(), ned_d ->get_name_index(wid)); fprintf(fd, ".latch %s%s/EN %s%s re %s%s 3\n", ned_d->get_name(), ned_d->get_name_index(wid), ned_q->get_name(), ned_q->get_name_index(wid), ned_c->get_name(), ned_c->get_name_index(0)); } } else { for (unsigned wid = 0 ; wid < ivl_lpm_width(net) ; wid += 1) { fprintf(fd, ".latch %s%s %s%s re %s%s 3\n", ned_d->get_name(), ned_d->get_name_index(wid), ned_q->get_name(), ned_q->get_name_index(wid), ned_c->get_name(), ned_c->get_name_index(0)); } } return errors; } iverilog-10_1/tgt-blif/lpm_mux.cc000066400000000000000000000104541265551621300170460ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "priv.h" # include "nex_data.h" # include # include using namespace std; /* * This handles the special case that the select port to the MUX is a * single bit. In other words, a binary mux. This may have an * arbitrary data width, but it selects from input A or input B. */ static int print_lpm_mux_s1(FILE*fd, ivl_lpm_t net) { ivl_nexus_t nex_out = ivl_lpm_q(net); blif_nex_data_t*ned_out = blif_nex_data_t::get_nex_data(nex_out); ivl_nexus_t nex_sel = ivl_lpm_select(net); blif_nex_data_t*ned_sel = blif_nex_data_t::get_nex_data(nex_sel); ivl_nexus_t nex_d0 = ivl_lpm_data(net,0); blif_nex_data_t*ned_d0 = blif_nex_data_t::get_nex_data(nex_d0); ivl_nexus_t nex_d1 = ivl_lpm_data(net,1); blif_nex_data_t*ned_d1 = blif_nex_data_t::get_nex_data(nex_d1); fprintf(fd, "# IVL_LPM_MUX ivl_lpm_width(net)=%u, Q=%s, D0=%s, D1=%s\n", ivl_lpm_width(net), ned_out->get_name(), ned_d0->get_name(), ned_d1->get_name()); assert(ivl_lpm_width(net) == ned_out->get_width()); assert(ivl_lpm_width(net) == ned_d0->get_width()); assert(ivl_lpm_width(net) == ned_d1->get_width()); // Only support single-bit select assert(ned_sel->get_width() == 1); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, ".names %s%s %s%s %s%s %s%s\n" "01- 1\n" "1-1 1\n", ned_sel->get_name(), ned_sel->get_name_index(0), ned_d0->get_name(), ned_d0->get_name_index(idx), ned_d1->get_name(), ned_d1->get_name_index(idx), ned_out->get_name(), ned_out->get_name_index(idx)); } return 0; } static int print_lpm_mux_sN(FILE*fd, ivl_lpm_t net) { ivl_nexus_t nex_out = ivl_lpm_q(net); blif_nex_data_t*ned_out = blif_nex_data_t::get_nex_data(nex_out); ivl_nexus_t nex_sel = ivl_lpm_select(net); blif_nex_data_t*ned_sel = blif_nex_data_t::get_nex_data(nex_sel); vector ned_d (ivl_lpm_size(net)); for (size_t idx = 0 ; idx < ned_d.size() ; idx += 1) { ivl_nexus_t tmp = ivl_lpm_data(net,idx); ned_d[idx] = blif_nex_data_t::get_nex_data(tmp); } for (unsigned wid = 0 ; wid < ivl_lpm_width(net) ; wid += 1) { // First, print the names record with all the ports... fprintf(fd, ".names"); for (size_t idx = 0 ; idx < ned_sel->get_width() ; idx += 1) { fprintf(fd, " %s%s", ned_sel->get_name(), ned_sel->get_name_index(idx)); } for (size_t idx = 0 ; idx < ned_d.size() ; idx += 1) { fprintf(fd, " %s%s", ned_d[idx]->get_name(), ned_d[idx]->get_name_index(wid)); } fprintf(fd, " %s%s\n", ned_out->get_name(), ned_out->get_name_index(wid)); // Print the logic table. We need one line for each // select address. The select pins must exactly match // the select address. The output depends only on the // select D input. for (size_t didx = 0 ; didx < ned_d.size() ; didx += 1) { for (size_t idx = 0 ; idx < ned_sel->get_width() ; idx += 1) { if (didx & (1< static int print_part_vp_mux(FILE*fd, ivl_lpm_t net); /* * This implements the IVL_LPM_PART_VP, which is the vector-to-part * part select. Implement this as a .names buffer. */ int print_lpm_part_vp(FILE*fd, ivl_lpm_t net) { int rc = 0; // If this is a non-constant bit select, then handle it // elsewhere. if (ivl_lpm_data(net,1) != 0) return print_part_vp_mux(fd, net); ivl_nexus_t nex_out = ivl_lpm_q(net); blif_nex_data_t*ned_out = blif_nex_data_t::get_nex_data(nex_out); assert(ivl_lpm_width(net) == ned_out->get_width()); // Only handle constant part select base. assert(ivl_lpm_data(net,1) == 0); unsigned bit_sel = ivl_lpm_base(net); ivl_nexus_t nex_in = ivl_lpm_data(net,0); blif_nex_data_t*ned_in = blif_nex_data_t::get_nex_data(nex_in); assert(bit_sel < ned_in->get_width()); for (unsigned idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { fprintf(fd, ".names %s%s %s%s # %s:%u\n1 1\n", ned_in->get_name(), ned_in->get_name_index(bit_sel+idx), ned_out->get_name(), ned_out->get_name_index(idx), ivl_lpm_file(net), ivl_lpm_lineno(net)); } return rc; } static int print_part_vp_mux(FILE*fd, ivl_lpm_t net) { // Only handle constant part select base. assert(ivl_lpm_data(net,1) != 0); ivl_nexus_t nex_out = ivl_lpm_q(net); blif_nex_data_t*ned_out = blif_nex_data_t::get_nex_data(nex_out); // Only handle bit selects. assert(ned_out->get_width() == 1); ivl_nexus_t nex_in = ivl_lpm_data(net,0); blif_nex_data_t*ned_in = blif_nex_data_t::get_nex_data(nex_in); ivl_nexus_t nex_sel = ivl_lpm_data(net,1); blif_nex_data_t*ned_sel = blif_nex_data_t::get_nex_data(nex_sel); unsigned sel_wid = ned_sel->get_width(); unsigned in_wid = ned_in->get_width(); assert((1U<get_name(), ned_sel->get_name_index(idx)); } for (unsigned idx = 0 ; idx < in_wid ; idx += 1) { fprintf(fd, " %s%s", ned_in->get_name(), ned_in->get_name_index(idx)); } fprintf(fd, " %s%s\n", ned_out->get_name(), ned_out->get_name_index(0)); for (unsigned idx = 0 ; idx < (1U< static bool re_xor(unsigned val) { bool flag = false; for (size_t idx = 0 ; idx < 8*sizeof(val) ; idx += 1) { if (val&1) flag ^= true; val >>= 1; } return flag; } int print_lpm_re_logic(FILE*fd, ivl_lpm_t net) { ivl_nexus_t nex_q = ivl_lpm_q(net); blif_nex_data_t*ned_q = blif_nex_data_t::get_nex_data(nex_q); ivl_nexus_t nex_d = ivl_lpm_data(net,0); blif_nex_data_t*ned_d = blif_nex_data_t::get_nex_data(nex_d); assert(ned_q->get_width() == 1); fprintf(fd, ".names"); for (size_t idx = 0 ; idx < ned_d->get_width() ; idx += 1) { fprintf(fd, " %s%s", ned_d->get_name(), ned_d->get_name_index(idx)); } fprintf(fd, " %s%s\n", ned_q->get_name(), ned_q->get_name_index(0)); switch (ivl_lpm_type(net)) { case IVL_LPM_RE_AND: for (size_t idx = 0 ; idx < ned_d->get_width() ; idx += 1) fputc('1', fd); fprintf(fd, " 1\n"); break; case IVL_LPM_RE_OR: for (size_t idx = 0 ; idx < ned_d->get_width() ; idx += 1) { for (size_t wid = 0 ; wid < ned_d->get_width() ; wid += 1) { if (wid==idx) fputc('1', fd); else fputc('-', fd); } fprintf(fd, " 1\n"); } break; case IVL_LPM_RE_XOR: assert(ned_d->get_width() < 8*sizeof(unsigned)); for (unsigned val = 0; val < (1U<get_width()); val += 1) { if (! re_xor(val)) continue; for (size_t idx = 0 ; idx < ned_d->get_width() ; idx += 1) { if (val & (1<get_width() ; idx += 1) { for (size_t wid = 0 ; wid < ned_d->get_width() ; wid += 1) { if (wid==idx) fputc('0', fd); else fputc('-', fd); } fprintf(fd, " 1\n"); } break; case IVL_LPM_RE_NOR: for (size_t idx = 0 ; idx < ned_d->get_width() ; idx += 1) fputc('0', fd); fprintf(fd, " 1\n"); break; case IVL_LPM_RE_XNOR: assert(ned_d->get_width() < 8*sizeof(unsigned)); for (unsigned val = 0; val < (1U<get_width()); val += 1) { if (re_xor(val)) continue; for (size_t idx = 0 ; idx < ned_d->get_width() ; idx += 1) { if (val & (1< # include # include # include # include using namespace std; inline blif_nex_data_t::blif_nex_data_t(ivl_nexus_t nex) : nex_(nex), name_(0) { } blif_nex_data_t::~blif_nex_data_t() { if (name_) free(name_); for (size_t idx = 0 ; idx < name_index_.size() ; idx += 1) if (name_index_[idx]) free(name_index_[idx]); } blif_nex_data_t* blif_nex_data_t::get_nex_data(ivl_nexus_t nex) { void*tmp = ivl_nexus_get_private(nex); if (tmp != 0) return reinterpret_cast (tmp); blif_nex_data_t*data = new blif_nex_data_t(nex); ivl_nexus_set_private(nex, data); return data; } void blif_nex_data_t::make_name_from_sig_(ivl_signal_t sig) { assert(name_ == 0); string tmp = ivl_signal_basename(sig); for (ivl_scope_t sscope = ivl_signal_scope(sig) ; ivl_scope_parent(sscope) ; sscope = ivl_scope_parent(sscope)) { tmp = ivl_scope_basename(sscope) + string("/") + tmp; } name_ = strdup(tmp.c_str()); assert(name_index_.size()==0); if (ivl_signal_width(sig) > 1) { name_index_.resize(ivl_signal_width(sig)); assert(ivl_signal_packed_dimensions(sig) == 1); int msb = ivl_signal_packed_msb(sig,0); int lsb = ivl_signal_packed_lsb(sig,0); int dir = (lsb <= msb)? 1 : -1; int val = lsb; for (unsigned idx = 0 ; idx < ivl_signal_width(sig) ; idx += 1) { char buf[64]; snprintf(buf, sizeof buf, "[%d]", val); name_index_[idx] = strdup(buf); val += dir; } } } /* * Given that there is not an explicit binding to a signal for naming, * search for a signal and use that signal to derive the name of this * nexus. */ void blif_nex_data_t::select_name_(void) { for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex_) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex_, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; make_name_from_sig_(sig); break; } assert(name_); } void blif_nex_data_t::set_name(ivl_signal_t sig) { assert(name_ == 0); assert(ivl_signal_nex(sig,0) == nex_); make_name_from_sig_(sig); } const char* blif_nex_data_t::get_name(void) { if (name_==0) select_name_(); return name_; } const char* blif_nex_data_t::get_name_index(unsigned bit) { if (name_==0) select_name_(); if (name_index_.size()==0) { assert(bit == 0); return ""; } assert(bit < name_index_.size()); assert(name_index_[bit]); return name_index_[bit]; } /* * Get the width from any signal that is attached to the nexus. */ size_t blif_nex_data_t::get_width(void) { if (name_==0) select_name_(); // Special case: If the nexus width is 1 bit, then there is no // need for index_name maps, so the name_index_ will be empty. if (name_index_.size()==0) return 1; return name_index_.size(); } iverilog-10_1/tgt-blif/nex_data.h000066400000000000000000000050621265551621300170110ustar00rootroot00000000000000#ifndef IVL_nex_data_H #define IVL_nex_data_H /* * Copyright (c) 2013-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "ivl_target.h" # include # include /* * The ivl_target.h API allows for binding data to a nexus. This class * represents the data that we want to attach to a nexus. */ class blif_nex_data_t { private: // The constructors are private. Only the get_nex_data() // function can create these objects. blif_nex_data_t(ivl_nexus_t nex); ~blif_nex_data_t(); public: // Return the blif_nex_data_t object that is associated with // the given nexus. If the nexus does not have a nex_data_t // object, then create it and bind it to the nexus. Thus, this // function will always return the same nex_data instance for // the same nexus. static blif_nex_data_t* get_nex_data(ivl_nexus_t nex); // In certain situations, we know a priori what we want the // nexus name to be. In those cases, the context can use this // method to set the name (by the signal from width the name // is derived). Note that this must be called before the name // is otherwise queried. void set_name(ivl_signal_t sig); // Get the symbolic name chosen for this nexus. const char*get_name(void); // Map a canonical bit index (0 : width-1) to the bit number // as understood by the signal. This is normally a null // mapping, but sometimes the signal name used for the mapping // has a non-canonical bit numbering. const char*get_name_index(unsigned bit); // Get the vector width for this nexus. size_t get_width(void); public: ivl_nexus_t nex_; char*name_; std::vector name_index_; private: void select_name_(void); void make_name_from_sig_(ivl_signal_t sig); }; #endif /* IVL_nex_data_H */ iverilog-10_1/tgt-blif/priv.h000066400000000000000000000042201265551621300162010ustar00rootroot00000000000000#ifndef IVL_priv_H #define IVL_priv_H /* * Copyright (c) 2013-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "ivl_target.h" # include /* * Errors are counted here. When the blif processing is done, this * value is returned to ivl so that it can report error counts. */ extern int blif_errors; extern int print_logic_gate(FILE*fd, ivl_net_logic_t net); extern int print_lpm(FILE*fd, ivl_lpm_t net); extern int print_lpm_add(FILE*fd, ivl_lpm_t net); extern int print_lpm_ff(FILE*fd, ivl_lpm_t net); extern int print_lpm_sub(FILE*fd, ivl_lpm_t net); extern int print_lpm_cmp_eq(FILE*fd, ivl_lpm_t net); extern int print_lpm_cmp_gt(FILE*fd, ivl_lpm_t net); extern int print_lpm_cmp_ne(FILE*fd, ivl_lpm_t net); extern int print_lpm_mux(FILE*fd, ivl_lpm_t net); extern int print_lpm_part_vp(FILE*fd, ivl_lpm_t net); extern int print_lpm_re_logic(FILE*fd, ivl_lpm_t net); /* * Emit all the constants for a model. This works by scanning the * design for all constants, testing that they are part of the model, * and writing them out as .names records. */ extern void emit_constants(FILE*fd, ivl_design_t des, ivl_scope_t model); /* * Return true if the passed scope is under the model scope, at any * depth. The scope may be an immediate child, or a child several * levels removed. */ extern bool scope_is_in_model(ivl_scope_t model, ivl_scope_t scope); #endif /* IVL_priv_H */ iverilog-10_1/tgt-fpga/000077500000000000000000000000001265551621300150535ustar00rootroot00000000000000iverilog-10_1/tgt-fpga/Makefile.in000066400000000000000000000074011265551621300171220ustar00rootroot00000000000000# # Copyright 2003 Stephen Williams (steve at icarus.com) # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ mandir = @mandir@ datarootdir = @datarootdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ D = d-generic.o d-generic-edif.o d-lpm.o d-virtex.o d-virtex2.o O = edif.o fpga.o gates.o mangle.o tables.o generic.o xilinx.o $D all: dep fpga.tgt check: all clean: rm -rf *.o dep fpga.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-fpga/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif fpga.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) iverilog-fpga.ps: $(srcdir)/iverilog-fpga.man man -t $(srcdir)/iverilog-fpga.man > iverilog-fpga.ps iverilog-fpga.pdf: iverilog-fpga.ps ps2pdf iverilog-fpga.ps iverilog-fpga.pdf ifeq (@WIN32@,yes) INSTALL_DOC = $(prefix)/iverilog-fpga$(suffix).pdf $(mandir)/man1/iverilog-fpga$(suffix).1 INSTALL_DOCDIR = $(mandir)/man1 all: iverilog-fpga.pdf else INSTALL_DOC = $(mandir)/man1/iverilog-fpga$(suffix).1 INSTALL_DOCDIR = $(mandir)/man1 endif install: all installdirs $(libdir)/ivl$(suffix)/fpga.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/fpga.conf $(libdir)/ivl$(suffix)/fpga-s.conf $(libdir)/ivl$(suffix)/fpga.tgt: ./fpga.tgt $(INSTALL_PROGRAM) ./fpga.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.tgt" $(libdir)/ivl$(suffix)/fpga.conf: $(srcdir)/fpga.conf $(INSTALL_DATA) $(srcdir)/fpga.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.conf" $(libdir)/ivl$(suffix)/fpga-s.conf: $(srcdir)/fpga-s.conf $(INSTALL_DATA) $(srcdir)/fpga-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga-s.conf" $(mandir)/man1/iverilog-fpga$(suffix).1: $(srcdir)/iverilog-fpga.man $(INSTALL_DATA) $(srcdir)/iverilog-fpga.man "$(DESTDIR)$(mandir)/man1/iverilog-fpga$(suffix).1" $(prefix)/iverilog-fpga$(suffix).pdf: iverilog-fpga.pdf $(INSTALL_DATA) iverilog-fpga.pdf "$(DESTDIR)$(prefix)/iverilog-fpga$(suffix).pdf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.tgt" rm -f "$(DESTDIR)$(INSTALL_DOC)" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga-s.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-fpga/cppcheck.sup000066400000000000000000000002161265551621300173630ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:fpga.c:104 iverilog-10_1/tgt-fpga/d-generic-edif.c000066400000000000000000000333241265551621300177660ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "device.h" # include "fpga_priv.h" # include # include # include # include "ivl_alloc.h" struct nexus_recall { struct nexus_recall*next; ivl_nexus_t nex; char* joined; }; static struct nexus_recall*net_list = 0; static unsigned edif_uref = 0; static void edif_set_nexus_joint(ivl_nexus_t nex, const char*joint) { size_t newlen; struct nexus_recall*rec; rec = (struct nexus_recall*)ivl_nexus_get_private(nex); if (rec == 0) { rec = malloc(sizeof(struct nexus_recall)); rec->nex = nex; rec->joined = malloc(8); rec->joined[0] = 0; rec->next = net_list; net_list = rec; ivl_nexus_set_private(nex, rec); } newlen = strlen(rec->joined) + strlen(joint) + 2; rec->joined = realloc(rec->joined, newlen); strcat(rec->joined, " "); strcat(rec->joined, joint); } static void show_root_ports_edif(ivl_scope_t root) { char jbuf[1024]; unsigned cnt = ivl_scope_sigs(root); unsigned idx; for (idx = 0 ; idx < cnt ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(root, idx); const char*use_name; const char*dir = 0; if (ivl_signal_attr(sig, "PAD") != 0) continue; switch (ivl_signal_port(sig)) { case IVL_SIP_NONE: continue; case IVL_SIP_INPUT: dir = "INPUT"; break; case IVL_SIP_OUTPUT: dir = "OUTPUT"; break; case IVL_SIP_INOUT: dir = "INOUT"; break; } use_name = ivl_signal_basename(sig); if (ivl_signal_pins(sig) == 1) { fprintf(xnf, " (port %s (direction %s))\n", use_name, dir); sprintf(jbuf, "(portRef %s)", use_name); edif_set_nexus_joint(ivl_signal_pin(sig, 0), jbuf); } else { unsigned pin; for (pin = 0 ; pin < ivl_signal_pins(sig); pin += 1) { fprintf(xnf, " (port (rename %s_%u " "\"%s[%u]\") (direction %s))\n", use_name, pin, use_name, pin, dir); sprintf(jbuf, "(portRef %s_%u)", use_name, pin); edif_set_nexus_joint(ivl_signal_pin(sig, pin), jbuf); } } } } static void edif_show_header_generic(ivl_design_t des, const char*library) { ivl_scope_t root = ivl_design_root(des); /* write the primitive header */ fprintf(xnf, "(edif %s\n", ivl_scope_name(root)); fprintf(xnf, " (edifVersion 2 0 0)\n"); fprintf(xnf, " (edifLevel 0)\n"); fprintf(xnf, " (keywordMap (keywordLevel 0))\n"); fprintf(xnf, " (status\n"); fprintf(xnf, " (written\n"); fprintf(xnf, " (timeStamp 0 0 0 0 0 0)\n"); fprintf(xnf, " (author \"unknown\")\n"); fprintf(xnf, " (program \"Icarus Verilog/fpga.tgt\")))\n"); /* Write out the external references here? */ fputs(library, xnf); /* Write out the library header */ fprintf(xnf, " (library DESIGN\n"); fprintf(xnf, " (edifLevel 0)\n"); fprintf(xnf, " (technology (numberDefinition))\n"); /* The root module is a cell in the library. */ fprintf(xnf, " (cell %s\n", ivl_scope_name(root)); fprintf(xnf, " (cellType GENERIC)\n"); fprintf(xnf, " (view net\n"); fprintf(xnf, " (viewType NETLIST)\n"); fprintf(xnf, " (interface\n"); show_root_ports_edif(root); fprintf(xnf, " )\n"); /* end the (interface ) sexp */ fprintf(xnf, " (contents\n"); } static const char*external_library_text = " (external VIRTEX (edifLevel 0) (technology (numberDefinition))\n" " (cell AND2 (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface\n" " (port O (direction OUTPUT))\n" " (port I0 (direction INPUT))\n" " (port I1 (direction INPUT)))))\n" " (cell BUF (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface\n" " (port O (direction OUTPUT))\n" " (port I (direction INPUT)))))\n" " (cell FDCE (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface\n" " (port Q (direction OUTPUT))\n" " (port D (direction INPUT))\n" " (port C (direction INPUT))\n" " (port CE (direction INPUT))\n" " (port CLR (direction INPUT)))))\n" " (cell FDCPE (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface\n" " (port Q (direction OUTPUT))\n" " (port D (direction INPUT))\n" " (port C (direction INPUT))\n" " (port CE (direction INPUT))\n" " (port PRE (direction INPUT))\n" " (port CLR (direction INPUT)))))\n" " (cell GND (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface (port G (direction OUTPUT)))))\n" " (cell NOR2 (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface\n" " (port O (direction OUTPUT))\n" " (port I0 (direction INPUT))\n" " (port I1 (direction INPUT)))))\n" " (cell NOR3 (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface\n" " (port O (direction OUTPUT))\n" " (port I0 (direction INPUT))\n" " (port I1 (direction INPUT))\n" " (port I2 (direction INPUT)))))\n" " (cell VCC (cellType GENERIC)\n" " (view net\n" " (viewType NETLIST)\n" " (interface (port P (direction OUTPUT)))))\n" " )\n" ; static void edif_show_header(ivl_design_t des) { edif_show_header_generic(des, external_library_text); } static void edif_show_consts(ivl_design_t des) { unsigned idx; char jbuf[128]; for (idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) { unsigned pin; ivl_net_const_t net = ivl_design_const(des, idx); const char*val = ivl_const_bits(net); for (pin = 0 ; pin < ivl_const_pins(net) ; pin += 1) { ivl_nexus_t nex = ivl_const_pin(net, pin); const char*name; const char*port; edif_uref += 1; switch (val[pin]) { case '0': name = "GND"; port = "GROUND"; break; case '1': name = "VCC"; port = "VCC"; break; default: name = "???"; port = "?"; break; } fprintf(xnf, "(instance U%u " "(viewRef net" " (cellRef %s (libraryRef VIRTEX))))\n", edif_uref, name); sprintf(jbuf, "(portRef %s (instanceRef U%u))", port, edif_uref); edif_set_nexus_joint(nex, jbuf); } } } static void edif_show_footer(ivl_design_t des) { unsigned nref = 0; struct nexus_recall*cur; ivl_scope_t root = ivl_design_root(des); edif_show_consts(des); for (cur = net_list ; cur ; cur = cur->next) { fprintf(xnf, "(net (rename N%u \"%s\") (joined %s))\n", nref, ivl_nexus_name(cur->nex), cur->joined); nref += 1; } fprintf(xnf, " )\n"); /* end the (contents ) sexp */ fprintf(xnf, " )\n"); /* end the (view ) sexp */ fprintf(xnf, " )\n"); /* end the (cell ) sexp */ fprintf(xnf, " )\n"); /* end the (library ) sexp */ /* Make an instance of the defined object */ fprintf(xnf, " (design %s\n", ivl_scope_name(root)); fprintf(xnf, " (cellRef %s (libraryRef DESIGN))\n", ivl_scope_name(root)); if (part) fprintf(xnf, " (property PART (string \"%s\"))\n", part); fprintf(xnf, " )\n"); fprintf(xnf, ")\n"); /* end the (edif ) sexp */ } static void edif_show_logic(ivl_net_logic_t net) { char jbuf[1024]; unsigned idx; edif_uref += 1; switch (ivl_logic_type(net)) { case IVL_LO_AND: assert(ivl_logic_pins(net) <= 10); assert(ivl_logic_pins(net) >= 3); fprintf(xnf, "(instance (rename U%u \"%s\")", edif_uref, ivl_logic_name(net)); fprintf(xnf, " (viewRef net" " (cellRef AND%u (libraryRef VIRTEX))))\n", ivl_logic_pins(net) - 1); sprintf(jbuf, "(portRef O (instanceRef U%u))", edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, 0), jbuf); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { sprintf(jbuf, "(portRef I%u (instanceRef U%u))", idx-1, edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, idx), jbuf); } break; case IVL_LO_BUF: assert(ivl_logic_pins(net) == 2); fprintf(xnf, "(instance (rename U%u \"%s\")", edif_uref, ivl_logic_name(net)); fprintf(xnf, " (viewRef net" " (cellRef BUF (libraryRef VIRTEX))))\n"); sprintf(jbuf, "(portRef O (instanceRef U%u))", edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, 0), jbuf); sprintf(jbuf, "(portRef I (instanceRef U%u))", edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, 1), jbuf); break; case IVL_LO_BUFZ: { static int bufz_warned_once=0; if (!bufz_warned_once) { fprintf (stderr, "0:0: internal warning: BUFZ objects found " "in EDIF netlist.\n"); fprintf (stderr, "0:0: : I'll make BUFs for them.\n"); bufz_warned_once=1; } assert(ivl_logic_pins(net) == 2); fprintf(xnf, "(instance (rename U%u \"%s\")", edif_uref, ivl_logic_name(net)); fprintf(xnf, " (viewRef net" " (cellRef BUF (libraryRef VIRTEX))))\n"); sprintf(jbuf, "(portRef O (instanceRef U%u))", edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, 0), jbuf); sprintf(jbuf, "(portRef I (instanceRef U%u))", edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, 1), jbuf); } break; case IVL_LO_NOR: assert(ivl_logic_pins(net) <= 10); assert(ivl_logic_pins(net) >= 3); fprintf(xnf, "(instance (rename U%u \"%s\")", edif_uref, ivl_logic_name(net)); fprintf(xnf, " (viewRef net" " (cellRef NOR%u (libraryRef VIRTEX))))\n", ivl_logic_pins(net) - 1); sprintf(jbuf, "(portRef O (instanceRef U%u))", edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, 0), jbuf); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { sprintf(jbuf, "(portRef I%u (instanceRef U%u))", idx-1, edif_uref); edif_set_nexus_joint(ivl_logic_pin(net, idx), jbuf); } break; default: fprintf(stderr, "UNSUPPORT LOGIC TYPE: %d\n", ivl_logic_type(net)); } } static void edif_show_generic_dff(ivl_lpm_t net) { char jbuf[1024]; unsigned idx; ivl_nexus_t aclr = ivl_lpm_async_clr(net); ivl_nexus_t aset = ivl_lpm_async_set(net); const char*abits = 0; const char*fdcell = "FDCE"; if (aset != 0) { ivl_expr_t avalue = ivl_lpm_aset_value(net); fdcell = "FDCPE"; assert(avalue); abits = ivl_expr_bits(avalue); assert(abits); } for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { ivl_nexus_t nex; edif_uref += 1; fprintf(xnf, "(instance (rename U%u \"%s.%s[%u]\")", edif_uref, ivl_scope_name(ivl_lpm_scope(net)), ivl_lpm_basename(net), idx); fprintf(xnf, " (viewRef net" " (cellRef %s (libraryRef VIRTEX))))\n", fdcell); nex = ivl_lpm_q(net, idx); sprintf(jbuf, "(portRef Q (instanceRef U%u))", edif_uref); edif_set_nexus_joint(nex, jbuf); nex = ivl_lpm_data(net, idx); sprintf(jbuf, "(portRef D (instanceRef U%u))", edif_uref); edif_set_nexus_joint(nex, jbuf); nex = ivl_lpm_clk(net); sprintf(jbuf, "(portRef C (instanceRef U%u))", edif_uref); edif_set_nexus_joint(nex, jbuf); if ((nex = ivl_lpm_enable(net))) { sprintf(jbuf, "(portRef CE (instanceRef U%u))", edif_uref); edif_set_nexus_joint(nex, jbuf); } if (aclr) { sprintf(jbuf, "(portRef CLR (instanceRef U%u))", edif_uref); edif_set_nexus_joint(aclr, jbuf); } if (aset) { if (abits[idx] == '1') { sprintf(jbuf, "(portRef PRE (instanceRef U%u))", edif_uref); edif_set_nexus_joint(aset, jbuf); } else { assert(aclr == 0); sprintf(jbuf, "(portRef CLR (instanceRef U%u))", edif_uref); edif_set_nexus_joint(aset, jbuf); } } } } const struct device_s d_generic_edif = { edif_show_header, edif_show_footer, 0, /* show_cell_scope not implemented. */ 0, /* draw_pad not implemented */ edif_show_logic, edif_show_generic_dff, 0, /* show_cmp_eq */ 0, /* show_cmp_ne */ 0, /* show_cmp_ge */ 0, /* show_cmp_gt */ 0, 0, /* show_add */ 0, /* show_sub */ 0, /* show_shiftl */ 0 /* show_shiftr */ }; iverilog-10_1/tgt-fpga/d-generic.c000066400000000000000000000325301265551621300170570ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "device.h" # include "fpga_priv.h" # include /* * This is the device emitter for the most generic FPGA. It doesn't * know anything special about device types, so can't handle complex * logic. */ static void xnf_draw_pin(ivl_nexus_t nex, const char*nam, char dir) { const char*use_name = nam; const char*nex_name = xnf_mangle_nexus_name(nex); int invert = 0; if (use_name[0] == '~') { invert = 1; use_name += 1; } fprintf(xnf, " PIN, %s, %c, %s", use_name, dir, nex_name); if (invert) fprintf(xnf, ",,INV"); fprintf(xnf, "\n"); } static void show_root_ports_xnf(ivl_scope_t root) { unsigned cnt = ivl_scope_sigs(root); unsigned idx; for (idx = 0 ; idx < cnt ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(root, idx); const char*use_name; if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; use_name = ivl_signal_basename(sig); if (ivl_signal_pins(sig) == 1) { ivl_nexus_t nex = ivl_signal_pin(sig, 0); fprintf(xnf, "SIG, %s, PIN=%s\n", xnf_mangle_nexus_name(nex), use_name); } else { unsigned pin; for (pin = 0 ; pin < ivl_signal_pins(sig); pin += 1) { ivl_nexus_t nex = ivl_signal_pin(sig, pin); fprintf(xnf, "SIG, %s, PIN=%s%u\n", xnf_mangle_nexus_name(nex), use_name, pin); } } } } static void show_design_consts_xnf(ivl_design_t des) { unsigned idx; for (idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) { unsigned pin; ivl_net_const_t net = ivl_design_const(des, idx); const char*val = ivl_const_bits(net); for (pin = 0 ; pin < ivl_const_pins(net) ; pin += 1) { ivl_nexus_t nex = ivl_const_pin(net, pin); fprintf(xnf, "PWR,%c,%s\n", val[pin], xnf_mangle_nexus_name(nex)); } } } static void generic_show_header(ivl_design_t des) { ivl_scope_t root = ivl_design_root(des); fprintf(xnf, "LCANET,6\n"); fprintf(xnf, "PROG,iverilog,$Name: $,\"Icarus Verilog/fpga.tgt\"\n"); if (part && (part[0]!=0)) { fprintf(xnf, "PART,%s\n", part); } show_root_ports_xnf(root); } static void generic_show_footer(ivl_design_t des) { show_design_consts_xnf(des); fprintf(xnf, "EOF\n"); } static void generic_show_logic(ivl_net_logic_t net) { char name[1024]; ivl_nexus_t nex; unsigned idx; xnf_mangle_logic_name(net, name, sizeof name); switch (ivl_logic_type(net)) { case IVL_LO_AND: fprintf(xnf, "SYM, %s, AND, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char ipin[32]; nex = ivl_logic_pin(net, idx); sprintf(ipin, "I%u", idx-1); xnf_draw_pin(nex, ipin, 'I'); } fprintf(xnf, "END\n"); break; case IVL_LO_BUF: assert(ivl_logic_pins(net) == 2); fprintf(xnf, "SYM, %s, BUF, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); nex = ivl_logic_pin(net, 1); xnf_draw_pin(nex, "I", 'I'); fprintf(xnf, "END\n"); break; case IVL_LO_NAND: fprintf(xnf, "SYM, %s, NAND, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char ipin[32]; nex = ivl_logic_pin(net, idx); sprintf(ipin, "I%u", idx-1); xnf_draw_pin(nex, ipin, 'I'); } fprintf(xnf, "END\n"); break; case IVL_LO_NOR: fprintf(xnf, "SYM, %s, NOR, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char ipin[32]; nex = ivl_logic_pin(net, idx); sprintf(ipin, "I%u", idx-1); xnf_draw_pin(nex, ipin, 'I'); } fprintf(xnf, "END\n"); break; case IVL_LO_NOT: assert(ivl_logic_pins(net) == 2); fprintf(xnf, "SYM, %s, INV, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); nex = ivl_logic_pin(net, 1); xnf_draw_pin(nex, "I", 'I'); fprintf(xnf, "END\n"); break; case IVL_LO_OR: fprintf(xnf, "SYM, %s, OR, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char ipin[32]; nex = ivl_logic_pin(net, idx); sprintf(ipin, "I%u", idx-1); xnf_draw_pin(nex, ipin, 'I'); } fprintf(xnf, "END\n"); break; case IVL_LO_XOR: fprintf(xnf, "SYM, %s, XOR, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char ipin[32]; nex = ivl_logic_pin(net, idx); sprintf(ipin, "I%u", idx-1); xnf_draw_pin(nex, ipin, 'I'); } fprintf(xnf, "END\n"); break; case IVL_LO_XNOR: fprintf(xnf, "SYM, %s, XNOR, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char ipin[32]; nex = ivl_logic_pin(net, idx); sprintf(ipin, "I%u", idx-1); xnf_draw_pin(nex, ipin, 'I'); } fprintf(xnf, "END\n"); break; case IVL_LO_BUFIF0: fprintf(xnf, "SYM, %s, TBUF, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); nex = ivl_logic_pin(net, 1); xnf_draw_pin(nex, "I", 'I'); nex = ivl_logic_pin(net, 2); xnf_draw_pin(nex, "~T", 'I'); fprintf(xnf, "END\n"); break; case IVL_LO_BUFIF1: fprintf(xnf, "SYM, %s, TBUF, LIBVER=2.0.0\n", name); nex = ivl_logic_pin(net, 0); xnf_draw_pin(nex, "O", 'O'); nex = ivl_logic_pin(net, 1); xnf_draw_pin(nex, "I", 'I'); nex = ivl_logic_pin(net, 2); xnf_draw_pin(nex, "T", 'I'); fprintf(xnf, "END\n"); break; default: fprintf(stderr, "fpga.tgt: unknown logic type %d\n", ivl_logic_type(net)); break; } } static void generic_show_dff(ivl_lpm_t net) { char name[1024]; ivl_nexus_t nex; xnf_mangle_lpm_name(net, name, sizeof name); fprintf(xnf, "SYM, %s, DFF, LIBVER=2.0.0\n", name); nex = ivl_lpm_q(net, 0); xnf_draw_pin(nex, "Q", 'O'); nex = ivl_lpm_data(net, 0); xnf_draw_pin(nex, "D", 'I'); nex = ivl_lpm_clk(net); xnf_draw_pin(nex, "C", 'I'); if ((nex = ivl_lpm_enable(net))) xnf_draw_pin(nex, "CE", 'I'); fprintf(xnf, "END\n"); } /* * The generic == comparator uses EQN records to generate 2-bit * comparators, that are then connected together by a wide AND gate. */ static void generic_show_cmp_eq(ivl_lpm_t net) { ivl_nexus_t nex; unsigned idx; char name[1024]; /* Make this many dual pair comparators, and */ unsigned deqn = ivl_lpm_width(net) / 2; /* Make this many single pair comparators. */ unsigned seqn = ivl_lpm_width(net) % 2; xnf_mangle_lpm_name(net, name, sizeof name); for (idx = 0 ; idx < deqn ; idx += 1) { fprintf(xnf, "SYM, %s/CD%u, EQN, " "EQN=(~((I0 @ I1) + (I2 @ I3)))\n", name, idx); fprintf(xnf, " PIN, O, O, %s/CDO%u\n", name, idx); nex = ivl_lpm_data(net, 2*idx); xnf_draw_pin(nex, "I0", 'I'); nex = ivl_lpm_datab(net, 2*idx); xnf_draw_pin(nex, "I1", 'I'); nex = ivl_lpm_data(net, 2*idx+1); xnf_draw_pin(nex, "I2", 'I'); nex = ivl_lpm_datab(net, 2*idx+1); xnf_draw_pin(nex, "I3", 'I'); fprintf(xnf, "END\n"); } if (seqn != 0) { fprintf(xnf, "SYM, %s/CT, XNOR, LIBVER=2.0.0\n", name); fprintf(xnf, " PIN, O, O, %s/CTO\n", name); nex = ivl_lpm_data(net, 2*deqn); xnf_draw_pin(nex, "I0", 'I'); nex = ivl_lpm_datab(net, 2*deqn); xnf_draw_pin(nex, "I1", 'I'); fprintf(xnf, "END\n"); } if (ivl_lpm_type(net) == IVL_LPM_CMP_EQ) fprintf(xnf, "SYM, %s/OUT, AND, LIBVER=2.0.0\n", name); else fprintf(xnf, "SYM, %s/OUT, NAND, LIBVER=2.0.0\n", name); nex = ivl_lpm_q(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 0 ; idx < deqn ; idx += 1) fprintf(xnf, " PIN, I%u, I, %s/CDO%u\n", idx, name, idx); for (idx = 0 ; idx < seqn ; idx += 1) fprintf(xnf, " PIN, I%u, I, %s/CTO\n", deqn+idx, name); fprintf(xnf, "END\n"); } /* * This function draws N-bit wide binary mux devices. These are so * very popular because they are the result of such expressions as: * * x = sel? a : b; * * This code only supports the case where sel is a single bit. It * works by drawing for each bit of the width an EQN device that takes * as inputs I0 and I1 the alternative inputs, and I2 the select. The * select bit is common with all the generated mux devices. */ static void generic_show_mux(ivl_lpm_t net) { char name[1024]; ivl_nexus_t sel; unsigned idx; xnf_mangle_lpm_name(net, name, sizeof name); /* Access the single select bit. This is common to the whole width of the mux. */ assert(ivl_lpm_selects(net) == 1); sel = ivl_lpm_select(net, 0); for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { ivl_nexus_t nex; fprintf(xnf, "SYM, %s/M%u, EQN, " "EQN=((I0 * ~I2) + (I1 * I2))\n", name, idx); nex = ivl_lpm_q(net, idx); xnf_draw_pin(nex, "O", 'O'); nex = ivl_lpm_data2(net, 0, idx); xnf_draw_pin(nex, "I0", 'I'); nex = ivl_lpm_data2(net, 1, idx); xnf_draw_pin(nex, "I1", 'I'); xnf_draw_pin(sel, "I2", 'I'); fprintf(xnf, "END\n"); } } /* * This code cheats and just generates ADD4 devices enough to support * the add. Make no effort to optimize, because we have no idea what * kind of device we have. */ static void generic_show_add(ivl_lpm_t net) { char name[1024]; ivl_nexus_t nex; unsigned idx, nadd4, tail; xnf_mangle_lpm_name(net, name, sizeof name); /* Make this many ADD4 devices. */ nadd4 = ivl_lpm_width(net) / 4; tail = ivl_lpm_width(net) % 4; for (idx = 0 ; idx < nadd4 ; idx += 1) { fprintf(xnf, "SYM, %s/A%u, ADD4\n", name, idx); if (idx > 0) fprintf(xnf, " PIN, CI, I, %s/CO%u\n", name, idx-1); nex = ivl_lpm_q(net, idx*4+0); xnf_draw_pin(nex, "S0", 'O'); nex = ivl_lpm_q(net, idx*4+1); xnf_draw_pin(nex, "S1", 'O'); nex = ivl_lpm_q(net, idx*4+2); xnf_draw_pin(nex, "S2", 'O'); nex = ivl_lpm_q(net, idx*4+3); xnf_draw_pin(nex, "S3", 'O'); nex = ivl_lpm_data(net, idx*4+0); xnf_draw_pin(nex, "A0", 'I'); nex = ivl_lpm_data(net, idx*4+1); xnf_draw_pin(nex, "A1", 'I'); nex = ivl_lpm_data(net, idx*4+2); xnf_draw_pin(nex, "A2", 'I'); nex = ivl_lpm_data(net, idx*4+3); xnf_draw_pin(nex, "A3", 'I'); nex = ivl_lpm_datab(net, idx*4+0); xnf_draw_pin(nex, "B0", 'I'); nex = ivl_lpm_datab(net, idx*4+1); xnf_draw_pin(nex, "B1", 'I'); nex = ivl_lpm_datab(net, idx*4+2); xnf_draw_pin(nex, "B2", 'I'); nex = ivl_lpm_datab(net, idx*4+3); xnf_draw_pin(nex, "B3", 'I'); if ((idx*4+4) < ivl_lpm_width(net)) fprintf(xnf, " PIN, CO, O, %s/CO%u\n", name, idx); fprintf(xnf, "END\n"); } if (tail > 0) { fprintf(xnf, "SYM, %s/A%u, ADD4\n", name, nadd4); if (nadd4 > 0) fprintf(xnf, " PIN, CI, I, %s/CO%u\n", name, nadd4-1); switch (tail) { case 3: nex = ivl_lpm_data(net, nadd4*4+2); xnf_draw_pin(nex, "A2", 'I'); nex = ivl_lpm_datab(net, nadd4*4+2); xnf_draw_pin(nex, "B2", 'I'); nex = ivl_lpm_q(net, nadd4*4+2); xnf_draw_pin(nex, "S2", 'O'); case 2: nex = ivl_lpm_data(net, nadd4*4+1); xnf_draw_pin(nex, "A1", 'I'); nex = ivl_lpm_datab(net, nadd4*4+1); xnf_draw_pin(nex, "B1", 'I'); nex = ivl_lpm_q(net, nadd4*4+1); xnf_draw_pin(nex, "S1", 'O'); case 1: nex = ivl_lpm_data(net, nadd4*4+0); xnf_draw_pin(nex, "A0", 'I'); nex = ivl_lpm_datab(net, nadd4*4+0); xnf_draw_pin(nex, "B0", 'I'); nex = ivl_lpm_q(net, nadd4*4+0); xnf_draw_pin(nex, "S0", 'O'); } fprintf(xnf, "END\n"); } } const struct device_s d_generic = { generic_show_header, generic_show_footer, 0, /* show_scope */ 0, /* show_pad not implemented */ generic_show_logic, generic_show_dff, generic_show_cmp_eq, generic_show_cmp_eq, 0, /* ge not implemented */ 0, /* gt not implemented */ generic_show_mux, generic_show_add, 0, /* subtract not implemented */ 0, 0 }; iverilog-10_1/tgt-fpga/d-lpm.c000066400000000000000000000560671265551621300162460ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This is the driver for a purely generic LPM module writer. This * uses LPM version 2 1 0 devices, without particularly considering * the target technology. * * The LPM standard is EIA-IS/103-A October 1996 * The output is EDIF 2 0 0 format. */ # include "device.h" # include "fpga_priv.h" # include "edif.h" # include "generic.h" # include # include static edif_cell_t lpm_cell_buf(void) { static edif_cell_t tmp = 0; if (tmp != 0) return tmp; tmp = edif_xcell_create(xlib, "BUF", 2); edif_cell_portconfig(tmp, 0, "Result", IVL_SIP_OUTPUT); edif_cell_portconfig(tmp, 1, "Data", IVL_SIP_INPUT); /* A buffer is an inverted inverter. */ edif_cell_port_pstring(tmp, 0, "LPM_Polarity", "INVERT"); edif_cell_pstring(tmp, "LPM_TYPE", "LPM_INV"); edif_cell_pinteger(tmp, "LPM_Width", 1); edif_cell_pinteger(tmp, "LPM_Size", 1); return tmp; } static edif_cell_t lpm_cell_inv(void) { static edif_cell_t tmp = 0; if (tmp != 0) return tmp; tmp = edif_xcell_create(xlib, "INV", 2); edif_cell_portconfig(tmp, 0, "Result", IVL_SIP_OUTPUT); edif_cell_portconfig(tmp, 1, "Data", IVL_SIP_INPUT); edif_cell_pstring(tmp, "LPM_TYPE", "LPM_INV"); edif_cell_pinteger(tmp, "LPM_Width", 1); edif_cell_pinteger(tmp, "LPM_Size", 1); return tmp; } static edif_cell_t lpm_cell_bufif0(void) { static edif_cell_t tmp = 0; if (tmp != 0) return tmp; tmp = edif_xcell_create(xlib, "BUFIF1", 3); edif_cell_portconfig(tmp, 0, "TriData", IVL_SIP_OUTPUT); edif_cell_portconfig(tmp, 1, "Data", IVL_SIP_INPUT); edif_cell_portconfig(tmp, 2, "EnableDT", IVL_SIP_INPUT); edif_cell_port_pstring(tmp, 2, "LPM_Polarity", "INVERT"); edif_cell_pstring(tmp, "LPM_TYPE", "LPM_BUSTRI"); edif_cell_pinteger(tmp, "LPM_Width", 1); return tmp; } static edif_cell_t lpm_cell_bufif1(void) { static edif_cell_t tmp = 0; if (tmp != 0) return tmp; tmp = edif_xcell_create(xlib, "BUFIF1", 3); edif_cell_portconfig(tmp, 0, "TriData", IVL_SIP_OUTPUT); edif_cell_portconfig(tmp, 1, "Data", IVL_SIP_INPUT); edif_cell_portconfig(tmp, 2, "EnableDT", IVL_SIP_INPUT); edif_cell_pstring(tmp, "LPM_TYPE", "LPM_BUSTRI"); edif_cell_pinteger(tmp, "LPM_Width", 1); return tmp; } static edif_cell_t lpm_cell_or(unsigned siz) { unsigned idx; edif_cell_t cell; char name[32]; sprintf(name, "or%u", siz); cell = edif_xlibrary_findcell(xlib, name); if (cell != 0) return cell; cell = edif_xcell_create(xlib, strdup(name), siz+1); edif_cell_portconfig(cell, 0, "Result0", IVL_SIP_OUTPUT); for (idx = 0 ; idx < siz ; idx += 1) { sprintf(name, "Data%ux0", idx); edif_cell_portconfig(cell, idx+1, strdup(name), IVL_SIP_INPUT); } edif_cell_pstring(cell, "LPM_TYPE", "LPM_OR"); edif_cell_pinteger(cell, "LPM_Width", 1); edif_cell_pinteger(cell, "LPM_Size", siz); return cell; } static edif_cell_t lpm_cell_and(unsigned siz) { unsigned idx; edif_cell_t cell; char name[32]; sprintf(name, "and%u", siz); cell = edif_xlibrary_findcell(xlib, name); if (cell != 0) return cell; cell = edif_xcell_create(xlib, strdup(name), siz+1); edif_cell_portconfig(cell, 0, "Result0", IVL_SIP_OUTPUT); for (idx = 0 ; idx < siz ; idx += 1) { sprintf(name, "Data%ux0", idx); edif_cell_portconfig(cell, idx+1, strdup(name), IVL_SIP_INPUT); } edif_cell_pstring(cell, "LPM_TYPE", "LPM_AND"); edif_cell_pinteger(cell, "LPM_Width", 1); edif_cell_pinteger(cell, "LPM_Size", siz); return cell; } static edif_cell_t lpm_cell_xor(unsigned siz) { unsigned idx; edif_cell_t cell; char name[32]; sprintf(name, "xor%u", siz); cell = edif_xlibrary_findcell(xlib, name); if (cell != 0) return cell; cell = edif_xcell_create(xlib, strdup(name), siz+1); edif_cell_portconfig(cell, 0, "Result0", IVL_SIP_OUTPUT); for (idx = 0 ; idx < siz ; idx += 1) { sprintf(name, "Data%ux0", idx); edif_cell_portconfig(cell, idx+1, strdup(name), IVL_SIP_INPUT); } edif_cell_pstring(cell, "LPM_TYPE", "LPM_XOR"); edif_cell_pinteger(cell, "LPM_Width", 1); edif_cell_pinteger(cell, "LPM_Size", siz); return cell; } static edif_cell_t lpm_cell_nor(unsigned siz) { unsigned idx; edif_cell_t cell; char name[32]; sprintf(name, "nor%u", siz); cell = edif_xlibrary_findcell(xlib, name); if (cell != 0) return cell; cell = edif_xcell_create(xlib, strdup(name), siz+1); edif_cell_portconfig(cell, 0, "Result0", IVL_SIP_OUTPUT); edif_cell_port_pstring(cell, 0, "LPM_Polarity", "INVERT"); for (idx = 0 ; idx < siz ; idx += 1) { sprintf(name, "Data%ux0", idx); edif_cell_portconfig(cell, idx+1, strdup(name), IVL_SIP_INPUT); } edif_cell_pstring(cell, "LPM_TYPE", "LPM_OR"); edif_cell_pinteger(cell, "LPM_Width", 1); edif_cell_pinteger(cell, "LPM_Size", siz); return cell; } static void lpm_show_header(ivl_design_t des) { unsigned idx; ivl_scope_t root = ivl_design_root(des); unsigned sig_cnt = ivl_scope_sigs(root); unsigned nports = 0, pidx; /* Count the ports I'm going to use. */ for (idx = 0 ; idx < sig_cnt ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(root, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; if (ivl_signal_attr(sig, "PAD") != 0) continue; nports += ivl_signal_pins(sig); } /* Create the base edf object. */ edf = edif_create(ivl_scope_basename(root), nports); pidx = 0; for (idx = 0 ; idx < sig_cnt ; idx += 1) { edif_joint_t jnt; ivl_signal_t sig = ivl_scope_sig(root, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; if (ivl_signal_attr(sig, "PAD") != 0) continue; if (ivl_signal_pins(sig) == 1) { edif_portconfig(edf, pidx, ivl_signal_basename(sig), ivl_signal_port(sig)); assert(ivl_signal_pins(sig) == 1); jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, 0)); edif_port_to_joint(jnt, edf, pidx); } else { const char*name = ivl_signal_basename(sig); ivl_signal_port_t dir = ivl_signal_port(sig); char buf[128]; unsigned bit; for (bit = 0 ; bit < ivl_signal_pins(sig) ; bit += 1) { const char*tmp; sprintf(buf, "%s[%u]", name, bit); tmp = strdup(buf); edif_portconfig(edf, pidx+bit, tmp, dir); jnt = edif_joint_of_nexus(edf,ivl_signal_pin(sig,bit)); edif_port_to_joint(jnt, edf, pidx+bit); } } pidx += ivl_signal_pins(sig); } assert(pidx == nports); xlib = edif_xlibrary_create(edf, "LPM_LIBRARY"); } static void lpm_show_footer(ivl_design_t des) { edif_print(xnf, edf); } static void hookup_logic_gate(ivl_net_logic_t net, edif_cell_t cell) { unsigned pin, idx; edif_joint_t jnt; edif_cellref_t ref = edif_cellref_create(edf, cell); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); pin = edif_cell_port_byname(cell, "Result0"); edif_add_to_joint(jnt, ref, pin); for (idx = 1 ; idx < ivl_logic_pins(net) ; idx += 1) { char name[32]; jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx)); sprintf(name, "Data%ux0", idx-1); pin = edif_cell_port_byname(cell, name); edif_add_to_joint(jnt, ref, pin); } } static void lpm_logic(ivl_net_logic_t net) { edif_cell_t cell; edif_cellref_t ref; edif_joint_t jnt; switch (ivl_logic_type(net)) { case IVL_LO_BUFZ: case IVL_LO_BUF: assert(ivl_logic_pins(net) == 2); cell = lpm_cell_buf(); ref = edif_cellref_create(edf, cell); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, ref, 0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, ref, 1); break; case IVL_LO_BUFIF0: assert(ivl_logic_pins(net) == 3); cell = lpm_cell_bufif0(); ref = edif_cellref_create(edf, cell); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, ref, 0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, ref, 1); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2)); edif_add_to_joint(jnt, ref, 2); break; case IVL_LO_BUFIF1: assert(ivl_logic_pins(net) == 3); cell = lpm_cell_bufif1(); ref = edif_cellref_create(edf, cell); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, ref, 0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, ref, 1); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2)); edif_add_to_joint(jnt, ref, 2); break; case IVL_LO_NOT: assert(ivl_logic_pins(net) == 2); cell = lpm_cell_inv(); ref = edif_cellref_create(edf, cell); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, ref, 0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, ref, 1); break; case IVL_LO_OR: cell = lpm_cell_or(ivl_logic_pins(net)-1); hookup_logic_gate(net, cell); break; case IVL_LO_NOR: cell = lpm_cell_nor(ivl_logic_pins(net)-1); hookup_logic_gate(net, cell); break; case IVL_LO_AND: cell = lpm_cell_and(ivl_logic_pins(net)-1); hookup_logic_gate( net, cell); break; case IVL_LO_XOR: cell = lpm_cell_xor(ivl_logic_pins(net)-1); hookup_logic_gate( net, cell); break; default: fprintf(stderr, "UNSUPPORTED LOGIC TYPE: %d\n", ivl_logic_type(net)); break; } } static void lpm_show_dff(ivl_lpm_t net) { char name[64]; edif_cell_t cell; edif_cellref_t ref; edif_joint_t jnt; unsigned idx; unsigned pin, wid = ivl_lpm_width(net); sprintf(name, "fd%s%s%s%s%s%u", ivl_lpm_enable(net)? "ce" : "", ivl_lpm_async_clr(net)? "cl" : "", ivl_lpm_sync_clr(net)? "sc" : "", ivl_lpm_async_set(net)? "se" : "", ivl_lpm_sync_set(net)? "ss" : "", wid); cell = edif_xlibrary_findcell(xlib, name); if (cell == 0) { unsigned nports = 2 * wid + 1; pin = 0; if (ivl_lpm_enable(net)) nports += 1; if (ivl_lpm_async_clr(net)) nports += 1; if (ivl_lpm_sync_clr(net)) nports += 1; if (ivl_lpm_async_set(net)) nports += 1; if (ivl_lpm_sync_set(net)) nports += 1; cell = edif_xcell_create(xlib, strdup(name), nports); edif_cell_pstring(cell, "LPM_Type", "LPM_FF"); edif_cell_pinteger(cell, "LPM_Width", wid); for (idx = 0 ; idx < wid ; idx += 1) { sprintf(name, "Q%u", idx); edif_cell_portconfig(cell, idx*2+0, strdup(name), IVL_SIP_OUTPUT); sprintf(name, "Data%u", idx); edif_cell_portconfig(cell, idx*2+1, strdup(name), IVL_SIP_INPUT); } pin = wid*2; if (ivl_lpm_enable(net)) { edif_cell_portconfig(cell, pin, "Enable", IVL_SIP_INPUT); pin += 1; } if (ivl_lpm_async_clr(net)) { edif_cell_portconfig(cell, pin, "Aclr", IVL_SIP_INPUT); pin += 1; } if (ivl_lpm_sync_clr(net)) { edif_cell_portconfig(cell, pin, "Sclr", IVL_SIP_INPUT); pin += 1; } if (ivl_lpm_async_set(net)) { edif_cell_portconfig(cell, pin, "Aset", IVL_SIP_INPUT); pin += 1; } if (ivl_lpm_sync_set(net)) { edif_cell_portconfig(cell, pin, "Sset", IVL_SIP_INPUT); pin += 1; } edif_cell_portconfig(cell, pin, "Clock", IVL_SIP_INPUT); pin += 1; assert(pin == nports); } ref = edif_cellref_create(edf, cell); pin = edif_cell_port_byname(cell, "Clock"); jnt = edif_joint_of_nexus(edf, ivl_lpm_clk(net)); edif_add_to_joint(jnt, ref, pin); if (ivl_lpm_enable(net)) { pin = edif_cell_port_byname(cell, "Enable"); jnt = edif_joint_of_nexus(edf, ivl_lpm_enable(net)); edif_add_to_joint(jnt, ref, pin); } if (ivl_lpm_async_clr(net)) { pin = edif_cell_port_byname(cell, "Aclr"); jnt = edif_joint_of_nexus(edf, ivl_lpm_async_clr(net)); edif_add_to_joint(jnt, ref, pin); } if (ivl_lpm_sync_clr(net)) { pin = edif_cell_port_byname(cell, "Sclr"); jnt = edif_joint_of_nexus(edf, ivl_lpm_sync_clr(net)); edif_add_to_joint(jnt, ref, pin); } if (ivl_lpm_async_set(net)) { pin = edif_cell_port_byname(cell, "Aset"); jnt = edif_joint_of_nexus(edf, ivl_lpm_async_set(net)); edif_add_to_joint(jnt, ref, pin); } if (ivl_lpm_sync_set(net)) { ivl_expr_t svalue = ivl_lpm_sset_value(net); pin = edif_cell_port_byname(cell, "Sset"); jnt = edif_joint_of_nexus(edf, ivl_lpm_sync_set(net)); edif_add_to_joint(jnt, ref, pin); edif_cellref_pinteger(ref, "LPM_Svalue", ivl_expr_uvalue(svalue)); } for (idx = 0 ; idx < wid ; idx += 1) { sprintf(name, "Q%u", idx); pin = edif_cell_port_byname(cell, name); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, ref, pin); sprintf(name, "Data%u", idx); pin = edif_cell_port_byname(cell, name); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, ref, pin); } } static void lpm_show_mux(ivl_lpm_t net) { edif_cell_t cell; edif_cellref_t ref; edif_joint_t jnt; unsigned idx, rdx; char cellname[32]; unsigned wid_r = ivl_lpm_width(net); unsigned wid_s = ivl_lpm_selects(net); unsigned wid_z = ivl_lpm_size(net); sprintf(cellname, "mux%u_%u_%u", wid_r, wid_s, wid_z); cell = edif_xlibrary_findcell(xlib, cellname); if (cell == 0) { unsigned pins = wid_r + wid_s + wid_r*wid_z; cell = edif_xcell_create(xlib, strdup(cellname), pins); /* Make the output ports. */ for (idx = 0 ; idx < wid_r ; idx += 1) { sprintf(cellname, "Result%u", idx); edif_cell_portconfig(cell, idx, strdup(cellname), IVL_SIP_OUTPUT); } /* Make the select ports. */ for (idx = 0 ; idx < wid_s ; idx += 1) { sprintf(cellname, "Sel%u", idx); edif_cell_portconfig(cell, wid_r+idx, strdup(cellname), IVL_SIP_INPUT); } for (idx = 0 ; idx < wid_z ; idx += 1) { unsigned base = wid_r + wid_s + wid_r * idx; unsigned rdx; for (rdx = 0 ; rdx < wid_r ; rdx += 1) { sprintf(cellname, "Data%ux%u", idx, rdx); edif_cell_portconfig(cell, base+rdx, strdup(cellname), IVL_SIP_INPUT); } } edif_cell_pstring(cell, "LPM_Type", "LPM_MUX"); edif_cell_pinteger(cell, "LPM_Width", wid_r); edif_cell_pinteger(cell, "LPM_WidthS", wid_s); edif_cell_pinteger(cell, "LPM_Size", wid_z); } ref = edif_cellref_create(edf, cell); /* Connect the pins of the instance to the nexa. Access the cell pins by name. */ for (idx = 0 ; idx < wid_r ; idx += 1) { unsigned pin; sprintf(cellname, "Result%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, ref, pin); } for (idx = 0 ; idx < wid_s ; idx += 1) { unsigned pin; sprintf(cellname, "Sel%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net, idx)); edif_add_to_joint(jnt, ref, pin); } for (idx = 0 ; idx < wid_z ; idx += 1) { for (rdx = 0 ; rdx < wid_r ; rdx += 1) { unsigned pin; sprintf(cellname, "Data%ux%u", idx, rdx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, idx, rdx)); edif_add_to_joint(jnt, ref, pin); } } } static void lpm_show_add(ivl_lpm_t net) { unsigned idx; unsigned cell_width; char cellname[32]; edif_cell_t cell; edif_cellref_t ref; edif_joint_t jnt; const char*type = "ADD"; if (ivl_lpm_type(net) == IVL_LPM_SUB) type = "SUB"; /* Figure out the width of the cell. Normally, it is the LPM width known by IVL. But if the top data input bits are unconnected, then we really have a width one less, and we can use the cout to fill out the output width. */ cell_width = ivl_lpm_width(net); if ( (ivl_lpm_data(net,cell_width-1) == 0) && (ivl_lpm_datab(net,cell_width-1) == 0) ) cell_width -= 1; /* Find the correct ADD/SUB device in the library, search by name. If the device is not there, then create it and put it in the library. */ sprintf(cellname, "%s%u", type, cell_width); cell = edif_xlibrary_findcell(xlib, cellname); if (cell == 0) { unsigned pins = cell_width * 3 + 1; cell = edif_xcell_create(xlib, strdup(cellname), pins); for (idx = 0 ; idx < cell_width ; idx += 1) { sprintf(cellname, "Result%u", idx); edif_cell_portconfig(cell, idx*3+0, strdup(cellname), IVL_SIP_OUTPUT); sprintf(cellname, "DataA%u", idx); edif_cell_portconfig(cell, idx*3+1, strdup(cellname), IVL_SIP_INPUT); sprintf(cellname, "DataB%u", idx); edif_cell_portconfig(cell, idx*3+2, strdup(cellname), IVL_SIP_INPUT); } edif_cell_portconfig(cell, pins-1, "Cout", IVL_SIP_OUTPUT); edif_cell_pstring(cell, "LPM_Type", "LPM_ADD_SUB"); edif_cell_pstring(cell, "LPM_Direction", type); edif_cell_pinteger(cell, "LPM_Width", ivl_lpm_width(net)); } ref = edif_cellref_create(edf, cell); /* Connect the pins of the instance to the nexa. Access the cell pins by name. */ for (idx = 0 ; idx < cell_width ; idx += 1) { unsigned pin; sprintf(cellname, "Result%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, ref, pin); sprintf(cellname, "DataA%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, ref, pin); sprintf(cellname, "DataB%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, ref, pin); } if (cell_width < ivl_lpm_width(net)) { unsigned pin = edif_cell_port_byname(cell, "Cout"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, cell_width)); edif_add_to_joint(jnt, ref, pin); } } static void lpm_show_mult(ivl_lpm_t net) { char name[64]; unsigned idx; edif_cell_t cell; edif_cellref_t ref; sprintf(name, "mult%u", ivl_lpm_width(net)); cell = edif_xlibrary_findcell(xlib, name); if (cell == 0) { cell = edif_xcell_create(xlib, strdup(name), 3 * ivl_lpm_width(net)); for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { sprintf(name, "Result%u", idx); edif_cell_portconfig(cell, idx*3+0, strdup(name), IVL_SIP_OUTPUT); sprintf(name, "DataA%u", idx); edif_cell_portconfig(cell, idx*3+1, strdup(name), IVL_SIP_INPUT); sprintf(name, "DataB%u", idx); edif_cell_portconfig(cell, idx*3+2, strdup(name), IVL_SIP_INPUT); } edif_cell_pstring(cell, "LPM_Type", "LPM_MULT"); edif_cell_pinteger(cell, "LPM_WidthP", ivl_lpm_width(net)); edif_cell_pinteger(cell, "LPM_WidthA", ivl_lpm_width(net)); edif_cell_pinteger(cell, "LPM_WidthB", ivl_lpm_width(net)); } ref = edif_cellref_create(edf, cell); for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { unsigned pin; ivl_nexus_t nex; edif_joint_t jnt; sprintf(name, "Result%u", idx); pin = edif_cell_port_byname(cell, name); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, ref, pin); if ( (nex = ivl_lpm_data(net, idx)) ) { sprintf(name, "DataA%u", idx); pin = edif_cell_port_byname(cell, name); jnt = edif_joint_of_nexus(edf, nex); edif_add_to_joint(jnt, ref, pin); } if ( (nex = ivl_lpm_datab(net, idx)) ) { sprintf(name, "DataB%u", idx); pin = edif_cell_port_byname(cell, name); jnt = edif_joint_of_nexus(edf, nex); edif_add_to_joint(jnt, ref, pin); } } } static void lpm_show_constant(ivl_net_const_t net) { edif_cell_t cell0 = edif_xlibrary_findcell(xlib, "cell0"); edif_cell_t cell1 = edif_xlibrary_findcell(xlib, "cell1"); edif_cellref_t ref0 = 0, ref1 = 0; const char*bits; unsigned idx; if (cell0 == 0) { cell0 = edif_xcell_create(xlib, "cell0", 1); edif_cell_portconfig(cell0, 0, "Result0", IVL_SIP_OUTPUT); edif_cell_pstring(cell0, "LPM_Type", "LPM_CONSTANT"); edif_cell_pinteger(cell0, "LPM_Width", 1); edif_cell_pinteger(cell0, "LPM_CValue", 0); } if (cell1 == 0) { cell1 = edif_xcell_create(xlib, "cell1", 1); edif_cell_portconfig(cell1, 0, "Result0", IVL_SIP_OUTPUT); edif_cell_pstring(cell1, "LPM_Type", "LPM_CONSTANT"); edif_cell_pinteger(cell1, "LPM_Width", 1); edif_cell_pinteger(cell1, "LPM_CValue", 1); } bits = ivl_const_bits(net); for (idx = 0 ; idx < ivl_const_pins(net) ; idx += 1) { if (bits[idx] == '1') { if (ref1 == 0) ref1 = edif_cellref_create(edf, cell1); } else { if (ref0 == 0) ref0 = edif_cellref_create(edf, cell0); } } for (idx = 0 ; idx < ivl_const_pins(net) ; idx += 1) { edif_joint_t jnt; jnt = edif_joint_of_nexus(edf, ivl_const_pin(net,idx)); if (bits[idx] == '1') edif_add_to_joint(jnt, ref1, 0); else edif_add_to_joint(jnt, ref0, 0); } } const struct device_s d_lpm_edif = { lpm_show_header, lpm_show_footer, 0, 0, lpm_logic, lpm_show_dff, /* show_dff */ 0, 0, 0, 0, /* show_cmp_gt */ lpm_show_mux, /* show_mux */ lpm_show_add, /* show_add */ lpm_show_add, /* show_sub */ 0, /* show_shiftl */ 0, /* show_shiftr */ lpm_show_mult, /* show_mult */ lpm_show_constant /* show_constant */ }; iverilog-10_1/tgt-fpga/d-virtex.c000066400000000000000000000613351265551621300167710ustar00rootroot00000000000000/* * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "device.h" # include "fpga_priv.h" # include "edif.h" # include "generic.h" # include "xilinx.h" # include # include # include /* * This is a table of cell types that are accessible via the cellref * attribute to a gate. */ const static struct edif_xlib_celltable virtex_celltable[] = { { "BUFG", xilinx_cell_bufg }, { "MULT_AND", xilinx_cell_mult_and }, { 0, 0} }; /* * The show_header function is called before any of the devices of the * netlist are scanned. * * In this function, we look at the ports of the root module to decide * if they are to be made into ports. Modules that have PAD attributes * are *not* to be used as ports, they will be connected to special * PAD devices instead. */ static void virtex_show_header(ivl_design_t des) { const char*part_str = 0; xilinx_common_header(des); xlib = edif_xlibrary_create(edf, "VIRTEX"); edif_xlibrary_set_celltable(xlib, virtex_celltable); if ( (part_str = ivl_design_flag(des, "part")) && (part_str[0] != 0) ) { edif_pstring(edf, "PART", part_str); } cell_0 = edif_xcell_create(xlib, "GND", 1); edif_cell_portconfig(cell_0, 0, "GROUND", IVL_SIP_OUTPUT); cell_1 = edif_xcell_create(xlib, "VCC", 1); edif_cell_portconfig(cell_1, 0, "VCC", IVL_SIP_OUTPUT); } static void virtex_or_wide(ivl_net_logic_t net) { edif_cell_t cell_muxcy_l = xilinx_cell_muxcy_l(xlib); edif_cell_t cell_muxcy = xilinx_cell_muxcy(xlib); edif_cell_t cell_lut4 = xilinx_cell_lut4(xlib); edif_cellref_t true_out, false_out; edif_cellref_t lut, muxcy, muxcy_down=NULL; edif_joint_t jnt; unsigned idx, inputs, lut4_cnt; if (ivl_logic_type(net) == IVL_LO_OR) { true_out = edif_cellref_create(edf, cell_1); false_out = edif_cellref_create(edf, cell_0); } else { true_out = edif_cellref_create(edf, cell_0); false_out = edif_cellref_create(edf, cell_1); } inputs = ivl_logic_pins(net) - 1; lut4_cnt = (inputs-1)/4; for (idx = 0 ; idx < lut4_cnt ; idx += 1) { muxcy = edif_cellref_create(edf, cell_muxcy_l); lut = edif_cellref_create(edf, cell_lut4); edif_cellref_pstring(lut, "INIT", "0001"); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, lut, LUT_O); edif_add_to_joint(jnt, muxcy, MUXCY_S); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, true_out, 0); edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx*4+1+0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx*4+1+1)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx*4+1+2)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx*4+1+3)); edif_add_to_joint(jnt, lut, LUT_I3); if (idx > 0) { jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxcy, MUXCY_CI); edif_add_to_joint(jnt, muxcy_down, MUXCY_O); } else { jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxcy, MUXCY_CI); edif_add_to_joint(jnt, false_out, 0); } muxcy_down = muxcy; } muxcy = edif_cellref_create(edf, cell_muxcy); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, true_out, 0); edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxcy, MUXCY_CI); edif_add_to_joint(jnt, muxcy_down, MUXCY_O); switch (ivl_logic_pins(net) - 1 - lut4_cnt*4) { case 1: lut = edif_cellref_create(edf, xilinx_cell_inv(xlib)); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+0)); edif_add_to_joint(jnt, lut, BUF_I); break; case 2: lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); edif_cellref_pstring(lut, "INIT", "1"); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+1)); edif_add_to_joint(jnt, lut, LUT_I1); break; case 3: lut = edif_cellref_create(edf, xilinx_cell_lut3(xlib)); edif_cellref_pstring(lut, "INIT", "01"); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+1)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+2)); edif_add_to_joint(jnt, lut, LUT_I2); break; case 4: lut = edif_cellref_create(edf, cell_lut4); edif_cellref_pstring(lut, "INIT", "0001"); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+1)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+2)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, lut4_cnt*4+1+3)); edif_add_to_joint(jnt, lut, LUT_I3); break; default: assert(0); } jnt = edif_joint_create(edf); edif_add_to_joint(jnt, lut, LUT_O); edif_add_to_joint(jnt, muxcy, MUXCY_S); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, muxcy, MUXCY_O); } /* * Pick off the cases where there is a Virtex specific implementation * that is better than the generic Xilinx implementation. Route the * remaining to the base xilinx_logic implementation. */ void virtex_logic(ivl_net_logic_t net) { /* Nothing I can do if the user expresses a specific opinion. The cellref attribute forces me to let the base xilinx_logic take care of it. */ if (ivl_logic_attr(net, "cellref")) { xilinx_logic(net); return; } switch (ivl_logic_type(net)) { case IVL_LO_OR: case IVL_LO_NOR: if (ivl_logic_pins(net) <= 5) { xilinx_logic(net); } else { virtex_or_wide(net); } break; default: xilinx_logic(net); break; } } void virtex_generic_dff(ivl_lpm_t net) { unsigned idx; ivl_nexus_t aclr = ivl_lpm_async_clr(net); ivl_nexus_t aset = ivl_lpm_async_set(net); ivl_nexus_t sclr = ivl_lpm_sync_clr(net); ivl_nexus_t sset = ivl_lpm_sync_set(net); const char*abits = 0; if (aset) { ivl_expr_t avalue = ivl_lpm_aset_value(net); assert(avalue); abits = ivl_expr_bits(avalue); assert(abits); } /* XXXX Can't handle both synchronous and asynchronous clear. */ assert( ! (aclr && sclr) ); /* XXXX Can't handle synchronous set at all. */ assert( ! sset ); for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { edif_cellref_t obj; ivl_nexus_t nex; edif_joint_t jnt; /* If there is a preset, then select an FDCPE instead of an FDCE device. */ if (aset && (abits[idx] == '1')) { obj = edif_cellref_create(edf, xilinx_cell_fdcpe(xlib)); } else if (aclr) { obj = edif_cellref_create(edf, xilinx_cell_fdce(xlib)); } else if (sclr) { obj = edif_cellref_create(edf, xilinx_cell_fdre(xlib)); } else { obj = edif_cellref_create(edf, xilinx_cell_fdce(xlib)); } jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, obj, FDCE_Q); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, obj, FDCE_D); jnt = edif_joint_of_nexus(edf, ivl_lpm_clk(net)); edif_add_to_joint(jnt, obj, FDCE_C); if ( (nex = ivl_lpm_enable(net)) ) { jnt = edif_joint_of_nexus(edf, nex); edif_add_to_joint(jnt, obj, FDCE_CE); } if (aclr) { jnt = edif_joint_of_nexus(edf, aclr); edif_add_to_joint(jnt, obj, FDCE_CLR); } else if (sclr) { jnt = edif_joint_of_nexus(edf, sclr); edif_add_to_joint(jnt, obj, FDCE_CLR); } if (aset) { if (abits[idx] == '1') { jnt = edif_joint_of_nexus(edf, aset); edif_add_to_joint(jnt, obj, FDCE_PRE); } else { assert(aclr == 0); jnt = edif_joint_of_nexus(edf, aset); edif_add_to_joint(jnt, obj, FDCE_CLR); } } } } /* * This method handles both == and != operators, the identity * comparison operators. * * If the identity compare is applied to small enough input vectors, * it is shoved into a single LUT. Otherwise, it is strung out into a * row of LUT devices chained together by carry muxes. The output of * the comparison is the output of the last mux. * * When the compare is small, a LUT is generated with the appropriate * truth table to cause an == or != result. * * When the compare is too wide for a single LUT, then it is made into * a chain connected by a string of carry mux devices. Each LUT * implements == for up to two pairs of bits, even if the final output * is supposed to be !=. The LUT output is connected to an associated * MUX select input. The CO output of each muxcy is passed up to the * next higher order bits of the compare. * * For identity == compare, a != output from the LUT selects the DI * input of the muxcy, generating a 0 output that is passed up. Since * the next higher muxcy now gets a 0 input to both DI and CI, the * output of the next higher muxcy is guaranteed to be 0, and so on to * the final output of the carry chain. If the output from a LUT is ==, * then the CI input of the muxcy is selected and the truth of this * level depends on lower order bits. The least significant muxcy is * connected to GND and VCC so that its CO follows the least * significant LUT. * * Identity != is the same as == except that the output is * inverted. To get that effect without putting an inverter on the * output of the top muxcy pin CO (which would cost a LUT) the DI * inputs are all connected to VCC instead of GND, and the CI of the * least significant muxcy is connected to GND instead of VCC. The LUT * expressions for the chained compare are configured for ==, with the * changed CI/DI inputs performing the inversion. */ void virtex_eq(ivl_lpm_t net) { edif_cellref_t lut, mux, mux_prev; edif_joint_t jnt, jnt_di; unsigned idx; /* True if I'm implementing CMP_EQ instead of CMP_NE */ int eq = 1; assert(ivl_lpm_width(net) >= 1); if (ivl_lpm_type(net) == IVL_LPM_CMP_NE) eq = 0; switch (ivl_lpm_width(net)) { case 1: lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); edif_cellref_pstring(lut, "INIT", eq? "9" : "6"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); return; case 2: lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib)); edif_cellref_pstring(lut, "INIT", eq? "9009" : "6FF6"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 1)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 1)); edif_add_to_joint(jnt, lut, LUT_I3); return; default: { edif_cellref_t di; di = edif_cellref_create(edf, eq? cell_0 : cell_1); jnt_di = edif_joint_create(edf); edif_add_to_joint(jnt_di, di, 0); } mux_prev = 0; for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 2) { int subwid = 2; if ((idx + 1) == ivl_lpm_width(net)) subwid = 1; mux = edif_cellref_create(edf, xilinx_cell_muxcy(xlib)); if (subwid == 2) { lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib)); edif_cellref_pstring(lut, "INIT", "9009"); } else { lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); edif_cellref_pstring(lut, "INIT", "9"); } jnt = edif_joint_create(edf); edif_add_to_joint(jnt, lut, LUT_O); edif_add_to_joint(jnt, mux, MUXCY_S); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, lut, LUT_I1); if (subwid > 1) { jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx+1)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx+1)); edif_add_to_joint(jnt, lut, LUT_I3); } edif_add_to_joint(jnt_di, mux, MUXCY_DI); if (mux_prev) { jnt = edif_joint_create(edf); edif_add_to_joint(jnt, mux, MUXCY_CI); edif_add_to_joint(jnt, mux_prev, MUXCY_O); } else { edif_cellref_t ci; ci = edif_cellref_create(edf, eq? cell_1 : cell_0); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, ci, 0); edif_add_to_joint(jnt, mux, MUXCY_CI); } mux_prev = mux; } jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, mux_prev, MUXCY_O); return; } } /* * Implement hardware for the device (A >= B). We use LUT devices if * it can handle the slices, or carry chain logic if the slices must * span LUT devices. */ void virtex_ge(ivl_lpm_t net) { edif_cellref_t muxcy_prev; edif_cellref_t lut; edif_joint_t jnt; unsigned idx; if (ivl_lpm_width(net) == 1) { /* If the comparator is a single bit, then use a LUT2 with this truth table: Q A B --+---- 1 | 0 0 0 | 0 1 1 | 1 0 1 | 1 1 Connect the A value to I1 and the B value to I0. */ lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); edif_cellref_pstring(lut, "INIT", "D"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I2); return; } /* Handle the case where the device is two slices wide. In this case, we can use a LUT4 to do all the calculation. Use this truth table: Q AA BB --+------ 1 | 00 00 0 | 00 01 0 | 00 10 0 | 00 11 1 | 01 00 1 | 01 01 0 | 01 10 0 | 01 11 1 | 10 00 1 | 10 01 1 | 10 10 0 | 10 11 1 | 11 xx The I3-I0 inputs are A1 A0 B1 B0 in that order. */ assert(ivl_lpm_width(net) >= 2); lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib)); edif_cellref_pstring(lut, "INIT", "F731"); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 1)); edif_add_to_joint(jnt, lut, LUT_I3); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 1)); edif_add_to_joint(jnt, lut, LUT_I1); /* There are only two slices, so this is all we need. */ if (ivl_lpm_width(net) == 2) { jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); return; } /* The general case requires that we make the >= comparator from slices. This is an iterative design. Each slice has the truth table: An Bn | A >= B ------+------- 0 0 | CI 0 1 | 0 1 0 | 1 1 1 | CI The CI for each slice is the output of the compare of the next less significant bits. We get this truth table by connecting a LUT2 to the S input of a MUXCY. When the S input is (1), it propagates its CI. This suggests that the init value for the LUT be "9" (XNOR). When the MUXCY S input is 0, it propagates a local input. We connect to that input An, and we get the desired and complete truth table for a slice. This iterative definition needs to terminate at the least significant bits. In fact, we have a non-iterative was to deal with the two least significant slices. We take the output of the LUT4 device for the least significant bits, and use that to generate the initial CI for the chain. */ muxcy_prev = edif_cellref_create(edf, xilinx_cell_muxcy_l(xlib)); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, lut, LUT_O); edif_add_to_joint(jnt, muxcy_prev, MUXCY_S); { edif_cellref_t p0 = edif_cellref_create(edf, cell_0); edif_cellref_t p1 = edif_cellref_create(edf, cell_1); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, p0, 0); edif_add_to_joint(jnt, muxcy_prev, MUXCY_DI); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, p1, 0); edif_add_to_joint(jnt, muxcy_prev, MUXCY_CI); } for (idx = 2 ; idx < ivl_lpm_width(net) ; idx += 1) { edif_cellref_t muxcy; lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); muxcy = edif_cellref_create(edf, xilinx_cell_muxcy(xlib)); edif_cellref_pstring(lut, "INIT", "9"); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, lut, LUT_O); edif_add_to_joint(jnt, muxcy, MUXCY_S); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxcy, MUXCY_CI); edif_add_to_joint(jnt, muxcy_prev, MUXCY_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, lut, LUT_I0); edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, lut, LUT_I1); muxcy_prev = muxcy; } jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, muxcy_prev, MUXCY_O); } /* * A 4-input N-wide mux can be made on Virtex devices using MUXF5 and * LUT devices. The MUXF5 selects a LUT device (and is connected to * S[1]) and the LUT devices, connected to S[0], select the input. */ static void virtex_mux4(ivl_lpm_t net) { unsigned idx; assert(ivl_lpm_selects(net) == 2); for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { edif_joint_t jnt; edif_cellref_t lut01; edif_cellref_t lut23; edif_cellref_t muxf5; lut01 = edif_cellref_create(edf, xilinx_cell_lut3(xlib)); edif_cellref_pstring(lut01, "INIT", "CA"); lut23 = edif_cellref_create(edf, xilinx_cell_lut3(xlib)); edif_cellref_pstring(lut23, "INIT", "CA"); muxf5 = edif_cellref_create(edf, xilinx_cell_muxf5(xlib)); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 0, idx)); edif_add_to_joint(jnt, lut01, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 1, idx)); edif_add_to_joint(jnt, lut01, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 2, idx)); edif_add_to_joint(jnt, lut23, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 3, idx)); edif_add_to_joint(jnt, lut23, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net, 0)); edif_add_to_joint(jnt, lut01, LUT_I2); edif_add_to_joint(jnt, lut23, LUT_I2); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxf5, MUXF_I0); edif_add_to_joint(jnt, lut01, LUT_O); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxf5, MUXF_I1); edif_add_to_joint(jnt, lut23, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, muxf5, MUXF_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net, 1)); edif_add_to_joint(jnt, muxf5, MUXF_S); } } void virtex_mux(ivl_lpm_t net) { switch (ivl_lpm_selects(net)) { case 2: virtex_mux4(net); break; default: xilinx_mux(net); break; } } /* * This function generates ADD/SUB devices for Virtex devices, * based on the documented implementations of ADD8/ADD16, etc., from * the Libraries Guide. * * Each slice of the ADD/SUB device is made from a LUT2 device, an * XORCY device that mixes with the LUT2 to make a full adder, and a * MUXCY_L to propagate the carry. The most significant slice does not * have a carry to propagate, so has no MUXCY_L. * * If the device is a wide adder, then the LUT2 devices are configured * to implement an XOR function and a zero is pumped into the least * significant carry input. * * If the device is really an adder, then the input is turned into an * XNOR, which takes a 1-s complement of the B input. Pump a 1 into * the LSB carry input to finish converting the B input into the 2s * complement. */ void virtex_add(ivl_lpm_t net) { const char*ha_init = 0; edif_cellref_t lut, xorcy, muxcy, pad; edif_joint_t jnt; unsigned idx; if (ivl_lpm_width(net) < 2) { xilinx_add(net); return; } switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: ha_init = "6"; break; case IVL_LPM_SUB: ha_init = "9"; break; default: assert(0); } assert(ivl_lpm_width(net) > 1); lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); xorcy = edif_cellref_create(edf, xilinx_cell_xorcy(xlib)); muxcy = edif_cellref_create(edf, xilinx_cell_muxcy_l(xlib)); edif_cellref_pstring(lut, "INIT", ha_init); /* The bottom carry-in takes a constant that primes the add or subtract. */ switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: pad = edif_cellref_create(edf, cell_0); break; case IVL_LPM_SUB: pad = edif_cellref_create(edf, cell_1); break; default: assert(0); } jnt = edif_joint_create(edf); edif_add_to_joint(jnt, pad, 0); edif_add_to_joint(jnt, muxcy, MUXCY_CI); edif_add_to_joint(jnt, xorcy, XORCY_CI); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, xorcy, XORCY_O); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, xorcy, XORCY_LI); edif_add_to_joint(jnt, muxcy, MUXCY_S); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); for (idx = 1 ; idx < ivl_lpm_width(net) ; idx += 1) { edif_cellref_t muxcy0 = muxcy; lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); xorcy = edif_cellref_create(edf, xilinx_cell_xorcy(xlib)); edif_cellref_pstring(lut, "INIT", ha_init); /* If this is the last bit, then there is no further propagation in the carry chain, and I can skip the carry mux MUXCY. */ if ((idx+1) < ivl_lpm_width(net)) muxcy = edif_cellref_create(edf, xilinx_cell_muxcy_l(xlib)); else muxcy = 0; jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxcy0, MUXCY_O); edif_add_to_joint(jnt, xorcy, XORCY_CI); if (muxcy) edif_add_to_joint(jnt, muxcy, MUXCY_CI); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, xorcy, XORCY_O); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, xorcy, XORCY_LI); if (muxcy) edif_add_to_joint(jnt, muxcy, MUXCY_S); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, lut, LUT_I0); if (muxcy) edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, lut, LUT_I1); } } const struct device_s d_virtex_edif = { virtex_show_header, xilinx_show_footer, xilinx_show_scope, xilinx_pad, virtex_logic, virtex_generic_dff, virtex_eq, virtex_eq, virtex_ge, 0, /* show_cmp_gt */ virtex_mux, virtex_add, virtex_add, xilinx_shiftl, 0 /* show_shiftr */ }; iverilog-10_1/tgt-fpga/d-virtex2.c000066400000000000000000000050711265551621300170460ustar00rootroot00000000000000/* * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "device.h" # include "fpga_priv.h" # include "edif.h" # include "generic.h" # include "xilinx.h" # include # include # include /* * This is a table of cell types that are accessible via the cellref * attribute to a gate. */ const static struct edif_xlib_celltable virtex2_celltable[] = { { "BUFG", xilinx_cell_bufg }, { "MULT_AND", xilinx_cell_mult_and }, { 0, 0} }; /* * The show_header function is called before any of the devices of the * netlist are scanned. * * In this function, we look at the ports of the root module to decide * if they are to be made into ports. Modules that have PAD attributes * are *not* to be used as ports, they will be connected to special * PAD devices instead. */ static void virtex2_show_header(ivl_design_t des) { const char*part_str = 0; xilinx_common_header(des); xlib = edif_xlibrary_create(edf, "VIRTEX2"); edif_xlibrary_set_celltable(xlib, virtex2_celltable); if ( (part_str = ivl_design_flag(des, "part")) && (part_str[0] != 0) ) { edif_pstring(edf, "PART", part_str); } cell_0 = edif_xcell_create(xlib, "GND", 1); edif_cell_portconfig(cell_0, 0, "GROUND", IVL_SIP_OUTPUT); cell_1 = edif_xcell_create(xlib, "VCC", 1); edif_cell_portconfig(cell_1, 0, "VCC", IVL_SIP_OUTPUT); } const struct device_s d_virtex2_edif = { virtex2_show_header, xilinx_show_footer, xilinx_show_scope, xilinx_pad, virtex_logic, virtex_generic_dff, virtex_eq, virtex_eq, virtex_ge, 0, /* show_cmp_gt */ virtex_mux, virtex_add, virtex_add, xilinx_shiftl, /* show_shiftl */ 0 /* show_shiftr */ }; iverilog-10_1/tgt-fpga/device.h000066400000000000000000000055271265551621300164740ustar00rootroot00000000000000#ifndef IVL_device_H #define IVL_device_H /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include /* * This code generator supports a variety of device types. It does * this by keeping a device "driver" structure for each device * type. The device structure contains pointers to functions that emit * the proper XNF for a given type of device. * * If a device supports a method, the function pointer is filled in * with a pointer to the proper function. * * If a device does not support the method, then the pointer is null. */ typedef const struct device_s* device_t; struct device_s { /* These methods draw leading and trailing format text. */ void (*show_header)(ivl_design_t des); void (*show_footer)(ivl_design_t des); /* Draw scopes marked by ivl_synthesis_cell */ void (*show_cell_scope)(ivl_scope_t net); /* Draw pads connected to the specified signal. */ void (*show_pad)(ivl_signal_t sig, const char*str); /* Draw basic logic devices. */ void (*show_logic)(ivl_net_logic_t net); /* This method emits a D type Flip-Flop */ void (*show_dff)(ivl_lpm_t net); /* These methods show various comparators */ void (*show_cmp_eq)(ivl_lpm_t net); void (*show_cmp_ne)(ivl_lpm_t net); void (*show_cmp_ge)(ivl_lpm_t net); void (*show_cmp_gt)(ivl_lpm_t net); /* This method draws MUX devices */ void (*show_mux)(ivl_lpm_t net); /* This method draws ADD devices */ void (*show_add)(ivl_lpm_t net); void (*show_sub)(ivl_lpm_t net); /* These methods draw SHIFT devices */ void (*show_shiftl)(ivl_lpm_t net); void (*show_shiftr)(ivl_lpm_t net); /* Multipliers */ void (*show_mult)(ivl_lpm_t net); /* Constants */ void (*show_constant)(ivl_net_const_t net); }; /* * Return the device_t cookie given the name of the architecture. If * the device is not found, return 0. * * This function is used if the user specifies the architecture * explicitly, with the -parch=name flag. */ extern device_t device_from_arch(const char*arch); #endif /* IVL_device_H */ iverilog-10_1/tgt-fpga/edif.c000066400000000000000000000370161265551621300161350ustar00rootroot00000000000000/* * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "edif.h" # include # include # include # include "ivl_alloc.h" typedef enum property_e { PRP_NONE = 0, PRP_STRING, PRP_INTEGER } property_t; struct cellref_property_ { const char*name; property_t ptype; union { const char*str; long num; } value_; struct cellref_property_*next; }; struct edif_s { const char*name; /* List the ports of the design. */ unsigned nports; struct __cell_port*ports; /* All the external libraries attached to me. */ edif_xlibrary_t xlibs; /* list the cellref instances. */ edif_cellref_t celref; /* The root instance has cellref properties as well. */ struct cellref_property_*property; /* Keep a list of all the nexa */ struct edif_joint_s*nexa; }; struct edif_xlibrary_s { /* Name of this library. */ const char*name; /* The cells that are contained in this library. */ struct edif_cell_s*cells; /* point to the optional celltable. */ const struct edif_xlib_celltable*celltable; /* used to list libraries in an edif_t. */ struct edif_xlibrary_s*next; }; struct __cell_port { const char*name; const char*ename; struct cellref_property_*property; ivl_signal_port_t dir; }; struct edif_cell_s { const char*name; edif_xlibrary_t xlib; unsigned nports; struct __cell_port*ports; struct cellref_property_*property; struct edif_cell_s*next; }; struct edif_cellref_s { struct edif_cell_s* cell; unsigned u; struct cellref_property_*property; struct edif_cellref_s* next; }; struct joint_cell_ { struct edif_cellref_s*cell; unsigned port; struct joint_cell_*next; }; struct edif_joint_s { const char*name; struct joint_cell_*links; struct edif_joint_s*next; }; static int is_edif_name(const char*text) { static const char*edif_name_chars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; return (strspn(text, edif_name_chars) == strlen(text)); } edif_t edif_create(const char*design_name, unsigned nports) { edif_t edf = malloc(sizeof(struct edif_s)); edf->name = design_name; edf->nports= nports; edf->ports = nports? calloc(nports, sizeof(struct __cell_port)) : 0; edf->celref= 0; edf->xlibs = 0; edf->property = 0; edf->nexa = 0; return edf; } void edif_portconfig(edif_t edf, unsigned idx, const char*name, ivl_signal_port_t dir) { assert(idx < edf->nports); edf->ports[idx].name = name; if (is_edif_name(name)) { edf->ports[idx].ename = 0; } else { char buf[16]; sprintf(buf, "PORT%u", idx); edf->ports[idx].ename = strdup(buf); } edf->ports[idx].dir = dir; } void edif_port_to_joint(edif_joint_t jnt, edif_t edf, unsigned port) { struct joint_cell_* jc = malloc(sizeof(struct joint_cell_)); jc->cell = 0; jc->port = port; jc->next = jnt->links; jnt->links = jc; } void edif_pstring(edif_t edf, const char*name, const char*value) { struct cellref_property_*prp = malloc(sizeof(struct cellref_property_)); prp->name = name; prp->ptype = PRP_STRING; prp->value_.str = value; prp->next = edf->property; edf->property = prp; } edif_xlibrary_t edif_xlibrary_create(edif_t edf, const char*name) { edif_xlibrary_t xlib = malloc(sizeof(struct edif_xlibrary_s)); xlib->name = name; xlib->cells = 0; xlib->celltable = 0; xlib->next = edf->xlibs; edf->xlibs = xlib; return xlib; } void edif_xlibrary_set_celltable(edif_xlibrary_t xlib, const struct edif_xlib_celltable*tab) { assert(xlib->celltable == 0); xlib->celltable = tab; } edif_cell_t edif_xlibrary_findcell(edif_xlibrary_t xlib, const char*cell_name) { const struct edif_xlib_celltable*tcur; edif_cell_t cur; for (cur = xlib->cells ; cur ; cur = cur->next) { if (strcmp(cell_name, cur->name) == 0) return cur; } if (xlib->celltable == 0) return 0; for (tcur = xlib->celltable ; tcur->cell_name ; tcur += 1) if (strcmp(cell_name, tcur->cell_name) == 0) { return (tcur->cell_func)(xlib); } return 0; } edif_cell_t edif_xlibrary_scope_cell(edif_xlibrary_t xlib, ivl_scope_t scope) { unsigned port_count, idx; edif_cell_t cur; /* Check to see if the cell is already somehow defined. */ cur = edif_xlibrary_findcell(xlib, ivl_scope_tname(scope)); if (cur) return cur; /* Count the ports of the scope. */ port_count = 0; for (idx = 0 ; idx < ivl_scope_sigs(scope) ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(scope, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; port_count += 1; } cur = edif_xcell_create(xlib, ivl_scope_tname(scope), port_count); port_count = 0; for (idx = 0 ; idx < ivl_scope_sigs(scope) ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(scope, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; edif_cell_portconfig(cur, port_count, ivl_signal_basename(sig), ivl_signal_port(sig)); port_count += 1; } return cur; } edif_cell_t edif_xcell_create(edif_xlibrary_t xlib, const char*name, unsigned nports) { unsigned idx; edif_cell_t cell = malloc(sizeof(struct edif_cell_s)); cell->name = name; cell->xlib = xlib; cell->nports = nports; cell->ports = calloc(nports, sizeof(struct __cell_port)); cell->property = 0; for (idx = 0 ; idx < nports ; idx += 1) { cell->ports[idx].name = "?"; cell->ports[idx].dir = IVL_SIP_NONE; cell->ports[idx].property = 0; } cell->next = xlib->cells; xlib->cells = cell; return cell; } void edif_cell_portconfig(edif_cell_t cell, unsigned idx, const char*name, ivl_signal_port_t dir) { assert(idx < cell->nports); cell->ports[idx].name = name; cell->ports[idx].dir = dir; } void edif_cell_port_pstring(edif_cell_t cell, unsigned idx, const char*name, const char*value) { struct cellref_property_*prp = malloc(sizeof(struct cellref_property_)); prp->name = name; prp->ptype = PRP_STRING; prp->value_.str = value; prp->next = cell->ports[idx].property; cell->ports[idx].property = prp; } unsigned edif_cell_port_byname(edif_cell_t cell, const char*name) { unsigned idx = 0; for (idx = 0 ; idx < cell->nports ; idx += 1) if (strcmp(name, cell->ports[idx].name) == 0) break; return idx; } void edif_cell_pstring(edif_cell_t cell, const char*name, const char*value) { struct cellref_property_*prp = malloc(sizeof(struct cellref_property_)); prp->name = name; prp->ptype = PRP_STRING; prp->value_.str = value; prp->next = cell->property; cell->property = prp; } void edif_cell_pinteger(edif_cell_t cell, const char*name, int value) { struct cellref_property_*prp = malloc(sizeof(struct cellref_property_)); prp->name = name; prp->ptype = PRP_INTEGER; prp->value_.num = value; prp->next = cell->property; cell->property = prp; } edif_cellref_t edif_cellref_create(edif_t edf, edif_cell_t cell) { static unsigned u_number = 0; edif_cellref_t ref = malloc(sizeof(struct edif_cellref_s)); u_number += 1; assert(cell); assert(edf); ref->u = u_number; ref->cell = cell; ref->property = 0; ref->next = edf->celref; edf->celref = ref; return ref; } void edif_cellref_pstring(edif_cellref_t ref, const char*name, const char*value) { struct cellref_property_*prp = malloc(sizeof(struct cellref_property_)); prp->name = name; prp->ptype = PRP_STRING; prp->value_.str = value; prp->next = ref->property; ref->property = prp; } void edif_cellref_pinteger(edif_cellref_t ref, const char*name, int value) { struct cellref_property_*prp = malloc(sizeof(struct cellref_property_)); prp->name = name; prp->ptype = PRP_INTEGER; prp->value_.num = value; prp->next = ref->property; ref->property = prp; } edif_joint_t edif_joint_create(edif_t edf) { edif_joint_t jnt = malloc(sizeof(struct edif_joint_s)); jnt->name = 0; jnt->links = 0; jnt->next = edf->nexa; edf->nexa = jnt; return jnt; } edif_joint_t edif_joint_of_nexus(edif_t edf, ivl_nexus_t nex) { void*tmp = ivl_nexus_get_private(nex); edif_joint_t jnt; if (tmp == 0) { jnt = edif_joint_create(edf); ivl_nexus_set_private(nex, jnt); return jnt; } jnt = (edif_joint_t) tmp; return jnt; } void edif_joint_rename(edif_joint_t jnt, const char*name) { assert(jnt->name == 0); jnt->name = name; } void edif_add_to_joint(edif_joint_t jnt, edif_cellref_t cell, unsigned port) { struct joint_cell_* jc = malloc(sizeof(struct joint_cell_)); jc->cell = cell; jc->port = port; jc->next = jnt->links; jnt->links = jc; } static void fprint_property(FILE*fd, const struct cellref_property_*prp) { fprintf(fd, "(property %s ", prp->name); switch (prp->ptype) { case PRP_NONE: break; case PRP_STRING: fprintf(fd, "(string \"%s\")", prp->value_.str); break; case PRP_INTEGER: fprintf(fd, "(integer %ld)", prp->value_.num); break; } fprintf(fd, ")"); } /* * This function takes all the data structures that have been * assembled by the code generator, and writes them into an EDIF * formatted file. */ void edif_print(FILE*fd, edif_t edf) { edif_xlibrary_t xlib; edif_cell_t cell; edif_cellref_t ref; edif_joint_t jnt; struct cellref_property_*prp; unsigned idx; fprintf(fd, "(edif %s\n", edf->name); fprintf(fd, " (edifVersion 2 0 0)\n"); fprintf(fd, " (edifLevel 0)\n"); fprintf(fd, " (keywordMap (keywordLevel 0))\n"); fprintf(fd, " (status\n"); fprintf(fd, " (written\n"); fprintf(fd, " (timeStamp 0 0 0 0 0 0)\n"); fprintf(fd, " (author \"unknown\")\n"); fprintf(fd, " (program \"Icarus Verilog/fpga.tgt\")))\n"); fflush(fd); for (xlib = edf->xlibs ; xlib ; xlib = xlib->next) { fprintf(fd, " (external %s " "(edifLevel 0) " "(technology (numberDefinition))\n", xlib->name); for (cell = xlib->cells ; cell ; cell = cell->next) { fprintf(fd, " (cell %s (cellType GENERIC)\n", cell->name); fprintf(fd, " (view net\n" " (viewType NETLIST)\n" " (interface"); for (idx = 0 ; idx < cell->nports ; idx += 1) { struct __cell_port*pp = cell->ports + idx; fprintf(fd, "\n (port %s", pp->name); switch (pp->dir) { case IVL_SIP_INPUT: fprintf(fd, " (direction INPUT)"); break; case IVL_SIP_OUTPUT: fprintf(fd, " (direction OUTPUT)"); break; case IVL_SIP_INOUT: fprintf(fd, " (direction INOUT)"); break; default: break; } for (prp = pp->property ; prp ; prp=prp->next) { fprintf(fd, " "); fprint_property(fd, prp); } fprintf(fd, ")"); } for (prp = cell->property ; prp ; prp = prp->next) { fprintf(fd, "\n "); fprint_property(fd, prp); } fprintf(fd, ")))\n"); } fprintf(fd, " )\n"); /* terminate (external ...) sexp */ } fflush(fd); /* Write out the library header */ fprintf(fd, " (library DESIGN\n"); fprintf(fd, " (edifLevel 0)\n"); fprintf(fd, " (technology (numberDefinition))\n"); /* The root module is a cell in the library. */ fprintf(fd, " (cell %s\n", edf->name); fprintf(fd, " (cellType GENERIC)\n"); fprintf(fd, " (view net\n"); fprintf(fd, " (viewType NETLIST)\n"); fprintf(fd, " (interface\n"); for (idx = 0 ; idx < edf->nports ; idx += 1) { fprintf(fd, " (port "); if (edf->ports[idx].ename == 0) fprintf(fd, "%s ", edf->ports[idx].name); else fprintf(fd, "(rename %s \"%s\") ", edf->ports[idx].ename, edf->ports[idx].name); switch (edf->ports[idx].dir) { case IVL_SIP_INPUT: fprintf(fd, "(direction INPUT)"); break; case IVL_SIP_OUTPUT: fprintf(fd, "(direction OUTPUT)"); break; case IVL_SIP_INOUT: fprintf(fd, "(direction INOUT)"); break; default: break; } fprintf(fd, ")\n"); } fprintf(fd, " )\n"); /* end the (interface ) sexp */ fflush(fd); fprintf(fd, " (contents\n"); /* Display all the instances. */ for (ref = edf->celref ; ref ; ref = ref->next) { assert(ref->cell); fprintf(fd, "(instance U%u (viewRef net " "(cellRef %s (libraryRef %s)))", ref->u, ref->cell->name, ref->cell->xlib->name); for (prp = ref->property ; prp ; prp = prp->next) { fprintf(fd, " "); fprint_property(fd, prp); } fprintf(fd, ")\n"); } fflush(fd); /* Display all the joints. */ idx = 0; for (jnt = edf->nexa ; jnt ; jnt = jnt->next, idx += 1) { struct joint_cell_*jc; fprintf(fd, "(net "); if (jnt->name != 0) fprintf(fd, "(rename N%u \"%s\")", idx, jnt->name); else fprintf(fd, "N%u", idx); fprintf(fd, " (joined"); for (jc = jnt->links ; jc ; jc = jc->next) { if (jc->cell) { fprintf(fd, " (portRef %s (instanceRef U%u))", jc->cell->cell->ports[jc->port].name, jc->cell->u); } else { /* Reference to a port of the main cell. */ if (edf->ports[jc->port].ename) fprintf(fd, " (portRef %s)", edf->ports[jc->port].ename); else fprintf(fd, " (portRef %s)", edf->ports[jc->port].name); } } fprintf(fd, "))\n"); } fprintf(fd, " )\n"); /* end the (contents...) sexp */ fprintf(fd, " )\n"); /* end the (view ) sexp */ fprintf(fd, " )\n"); /* end the (cell ) sexp */ fprintf(fd, " )\n"); /* end the (library DESIGN) sexp */ /* Make an instance of the defined object */ fprintf(fd, " (design %s\n", edf->name); fprintf(fd, " (cellRef %s (libraryRef DESIGN))\n", edf->name); for (prp = edf->property ; prp ; prp = prp->next) { fprintf(fd, " "); fprint_property(fd, prp); fprintf(fd, "\n"); } fprintf(fd, " )\n"); fprintf(fd, ")\n"); fflush(fd); } iverilog-10_1/tgt-fpga/edif.h000066400000000000000000000224701265551621300161400ustar00rootroot00000000000000#ifndef IVL_edif_H #define IVL_edif_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include /* * These types and functions support the task of generating and * writing out an EDIF 2 0 0 netlist. These functions work by * supporting the creation of an in-core netlist of the design, then * writing the netlist out all at once. The library manages cells with * ports, but does not otherwise interpret cells. They have no * contents. * * The general structure of netlist creation is as follows: * * Create a netlist with edif_create(); * This creates an edif object that represents the design. The * design is given a name, and that name becomes the name of the * single cell that this netlist handles. * * Add ports to the root with edif_portconfig * The design may, if it is a macro to be included in a larger * design, include ports. These are discovered by looking for port * signals in the root module. * * Declare external libraries with edif_xlibrary_create * Normally, this is the single technology library that contains * the primitive cells that the code generator intends to * use. The library is given a name, such as VIRTEX or whatever * the implementation tools expect. Cells are attached to the * library later. An edif netlist may include multiple external * references. * * Declare primitives with edif_xcell_create and edif_cell_portconfig. * These functions create CELL TYPES that are attached to an * external library. The libraries are created by * edif_xlibrary_create. * * Cells can be created at any time before their first use. It * therefore makes the most sense to not create the cell until it * is certain that they are needed by the design. * * Create instances and join them up * The edif_cellref_t objects represent instances of cells, and * are the devices of the generated netlist. These cellrefs are * connected together by the use of edif_joint_t joints. The * joints can be created from ivl_nexus_t objects, or from their * own ether. This instantiating of cells and joining them * together that is the most fun. It is the technology specific * stuff that the code generator does. * * Finally, print the result with edif_print(fd); * This function writes the netlist in memory to an EDIF file on * the stdio stream specified. * * This library is intended to be used once, to build up a netlist and * print it. All the names that are taken as const char* should be * somehow made permanent by the caller. Either they are constant * strings, or they are strduped as necessary to make them * permanent. The library will not duplicate them. */ /* TYPE DECLARATIONS */ /* This represents the entire EDIF design. You only need one of these to hold everything. */ typedef struct edif_s* edif_t; /* Each external library of the design gets one of these. */ typedef struct edif_xlibrary_s* edif_xlibrary_t; /* This represents a type of cell. */ typedef struct edif_cell_s* edif_cell_t; /* A cellref is an *instance* of a cell. */ typedef struct edif_cellref_s* edif_cellref_t; /* This represents a generic joint. Cell ports are connected by being associated with a joint. These can be bound to an ivl_nexus_t object, of stand along. */ typedef struct edif_joint_s* edif_joint_t; /* This structure defines a table that can be attached to an xlibrary to incorporate black-box cells to the library. The cell_name is the name that may be passed to the edif_xlibrary_findcell function, and the function pointer points to a function that creates the cell and defines ports for it. A real celltable is terminated by an entry with a null pointer for the cell_name. */ struct edif_xlib_celltable { const char*cell_name; edif_cell_t (*cell_func)(edif_xlibrary_t xlib); }; /* FUNCTIONS */ /* Start a new EDIF design. The design_name will be used as the name of the top-mode module of the design. */ extern edif_t edif_create(const char*design_name, unsigned nports); /* macro ports to the design are handled by this library similar to cells. The user creates ports with this function. This function configures the sole "port" of the cell with the name and dir passed in. The direction, in this case, is the *interface* direction. */ extern void edif_portconfig(edif_t edf, unsigned idx, const char*name, ivl_signal_port_t dir); /* This is like edif_add_to_joint, but works with the edif port. */ extern void edif_port_to_joint(edif_joint_t jnt, edif_t edf, unsigned port); /* The design may have properties attached to it. These properties will be attached to the instance declared in the footer of the EDIF file. */ extern void edif_pstring(edif_t edf, const char*name, const char*value); /* Create an external library and attach it to the edif design. This will lead to a (external ...) declaration of cells that can be used by the design. */ extern edif_xlibrary_t edif_xlibrary_create(edif_t edf, const char*name); extern void edif_xlibrary_set_celltable(edif_xlibrary_t lib, const struct edif_xlib_celltable*table); /* External libraries can be searched for existing cells, given a string name. This function searches for the cell by name, and returns it. */ extern edif_cell_t edif_xlibrary_findcell(edif_xlibrary_t lib, const char*cell_name); /* Similar to the above, but it gets the information it needs from the ivl_scope_t object. */ extern edif_cell_t edif_xlibrary_scope_cell(edif_xlibrary_t xlib, ivl_scope_t scope); /* Create a new cell, attached to the external library. Specify the number of ports that the cell has. The edif_cell_portconfig function is then used to assign name and direction to each of the ports. The cell has a number of pins that are referenced by their number from 0 to nports-1. You need to remember the pin numbers for the named ports for use when joining that pin to an edif_joint_t. Cellrefs get their port characteristics from the cell that they are created from. So the pinouts of cellrefs match the pinout of the associated cell. */ extern edif_cell_t edif_xcell_create(edif_xlibrary_t, const char*name, unsigned nports); extern void edif_cell_portconfig(edif_cell_t cell, unsigned idx, const char*name, ivl_signal_port_t dir); /* Attach a property to a cell port. */ extern void edif_cell_port_pstring(edif_cell_t cell, unsigned idx, const char*name, const char*value); /* Cells may have properties attached to them. These properties are included in the library declaration for the cell, instead of the cell instances. */ extern void edif_cell_pstring(edif_cell_t cell, const char*name, const char*value); extern void edif_cell_pinteger(edif_cell_t cell, const char*name, int value); /* Ports of cells are normally referenced by their port number. If you forget what that number is, this function can look it up by name. */ extern unsigned edif_cell_port_byname(edif_cell_t cell, const char*name); /* Create and instance from a cell. The instance refers to the cell, which is a type, and contains pips for pins. */ extern edif_cellref_t edif_cellref_create(edif_t edf, edif_cell_t cell); /* Instances can have properties attached to them. The name and value given here are turned into a (property (string "val")) expression attached to the instance. Examples of string properties commonly attached to cellref devices include such things as the INIT= to initialize LUT cells in FPGA devices. */ extern void edif_cellref_pstring(edif_cellref_t ref, const char*name, const char*value); extern void edif_cellref_pinteger(edif_cellref_t ref, const char*name, int value); /* This function gets the joint associated with a nexus. This will create a joint if necessary. */ extern edif_joint_t edif_joint_of_nexus(edif_t edf, ivl_nexus_t nex); /* For linking cells outside the ivl netlist, this function creates an anonymous joint. */ extern edif_joint_t edif_joint_create(edif_t edf); /* Renaming a joint causes it to take on a name when external tools view the EDIF file. */ extern void edif_joint_rename(edif_joint_t jnt, const char*name); /* Given a joint, this function adds the cell reference. */ extern void edif_add_to_joint(edif_joint_t jnt, edif_cellref_t cell, unsigned port); /* * Print the entire design. This should only be done after the design * is completely assembled. */ extern void edif_print(FILE*fd, edif_t design); #endif /* IVL_edif_H */ iverilog-10_1/tgt-fpga/fpga-s.conf000066400000000000000000000001471265551621300171010ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle -t:dll flag:DLL=fpga.tgt iverilog-10_1/tgt-fpga/fpga.c000066400000000000000000000075611265551621300161450ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * This is the FPGA target module. */ # include # include # include "fpga_priv.h" # include /* This is the opened xnf file descriptor. It is the output that this code generator writes to. */ FILE*xnf = 0; const char*part = 0; const char*arch = 0; device_t device = 0; int scope_has_attribute(ivl_scope_t s, const char *name) { int i; for (i=0; ikey,name) == 0) return 1; } return 0; } static int show_process(ivl_process_t net, void*x) { ivl_scope_t scope = ivl_process_scope(net); /* Ignore processes that are within scopes that are cells. The cell_scope will generate a cell to represent the entire scope. */ if (scope_has_attribute(scope, "ivl_synthesis_cell")) return 0; fprintf(stderr, "fpga target: unsynthesized behavioral code\n"); return 0; } static void show_pads(ivl_scope_t scope) { unsigned idx; if (device->show_pad == 0) return; for (idx = 0 ; idx < ivl_scope_sigs(scope) ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(scope, idx); const char*pad; if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; pad = ivl_signal_attr(sig, "PAD"); if (pad == 0) continue; assert(device->show_pad); device->show_pad(sig, pad); } } static void show_constants(ivl_design_t des) { unsigned idx; if (device->show_constant == 0) return; for (idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) { ivl_net_const_t con = ivl_design_const(des, idx); device->show_constant(con); } } /* * This is the main entry point that ivl uses to invoke me, the code * generator. */ int target_design(ivl_design_t des) { ivl_scope_t root = ivl_design_root(des); const char*path = ivl_design_flag(des, "-o"); xnf = fopen(path, "w"); if (xnf == 0) { perror(path); return -1; } part = ivl_design_flag(des, "part"); if (part && (part[0] == 0)) part = 0; arch = ivl_design_flag(des, "arch"); if (arch && (arch[0] == 0)) arch = 0; if (arch == 0) arch = "lpm"; device = device_from_arch(arch); if (device == 0) { fprintf(stderr, "Unknown architecture arch=%s\n", arch); return -1; } /* Call the device driver to generate the netlist header. */ device->show_header(des); /* Catch any behavioral code that is left, and write warnings that it is not supported. */ ivl_design_process(des, show_process, 0); /* Get the pads from the design, and draw them to connect to the associated signals. */ show_pads(root); /* Scan the scopes, looking for gates to draw into the output netlist. */ show_scope_gates(root, 0); show_constants(des); /* Call the device driver to close out the file. */ device->show_footer(des); fclose(xnf); xnf = 0; return 0; } iverilog-10_1/tgt-fpga/fpga.conf000066400000000000000000000001471265551621300166410ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle -t:dll flag:DLL=fpga.tgt iverilog-10_1/tgt-fpga/fpga.txt000066400000000000000000000142531265551621300165360ustar00rootroot00000000000000 FPGA LOADABLE CODE GENERATOR FOR Icarus Verilog Copyright 2001 Stephen Williams The FPGA code generator supports a variety of FPGA devices, writing XNF or EDIF depending on the target. You can select the architecture of the device, and the detailed part name. The architecture is used to select library primitives, and the detailed part name is written into the generated file for the use of downstream tools. INVOKING THE FPGA TARGET The code generator is invoked with the -tfpga flag to iverilog. It understands the part= and the arch= parameters, which can be set with the -p flag of iverilog: iverilog -parch=virtex -ppart=v50-pq240-6 -tfpga foo.vl This example selects the Virtex architecture, and give the detailed part number as v50-pq240-6. The output is written into a.out unless a different output file is specified with the -o flag. The following is a list of architecture types that this code generator supports. * arch=lpm This is a device independent format, where the gates are device types as defined by the LPM 2 1 0 specification. Some backend tools may take this format, or users may write interface libraries to connect these netlists to the device in question. * arch=generic-edif (obsolete) This is generic EDIF code. It doesn't necessarily work because the external library is not available to the code generator. But, what it does is generate generic style gates that a portability library can map to target gates if desired. * arch=generic-xnf (obsolete) If this is selected, then the output is formatted as an XNF file, suitable for most any type of device. The devices that it emits are generic devices from the unified library. Some devices are macros, you may need to further resolve the generated XNF to get working code for your part. * arch=virtex If this is selected, then the output is formatted as an EDIF 200 file, suitable for Virtex class devices. This is supposed to know that you are targeting a Virtex part, so can generate primitives instead of using external macros. It includes the VIRTEX internal library, and should work properly for any Virtex part. * arch=virtex2 If this is selected, then the output is EDIF 2 0 0 suitable for Virtex-II and Virtex-II Pro devices. It uses the VIRTEX2 library, but is very similar to the Virtex target. XNF ROOT PORTS NOTE: As parts are moved over to EDIF format, XNF support will be phased out. Current Xilinx implementation tools will accept EDIF format files even for the older parts, and non-Xilinx implementation tools accept nothing else. When the output format is XNF, the code generator will generate "SIG" records for the signals that are ports of the root module. The name is declared as an external pin that this macro makes available. The name given to the macro pin is generated from the base name of the signal. If the signal is one bit wide, then the pin name is exactly the module port name. If the port is a vector, then the pin number is given as a vector. For example, the module: module main(out, in); output out; input [2:0] in; [...] endmodule leads to these SIG, records: SIG, main/out, PIN=out SIG, main/in<2>, PIN=in2 SIG, main/in<1>, PIN=in1 SIG, main/in<0>, PIN=in0 EDIF ROOT PORTS The EDIF format is more explicit about the interface into an EDIF file. The code generator uses that control to generate an explicit interface definition into the design. (This is *not* the same as the PADS of a part.) The generated EDIF interface section contains port definitions, including the proper direction marks. With the (rename ...) s-exp in EDIF, it is possible to assign arbitrary text to port names. The EDIF code generator therefore does not resort to the mangling that is needed for the XNF target. The base name of the signal that is an input or output is used as the name of the port, complete with the proper case. However, since the ports are single bit ports, the name of vectors includes the string "[0]" where the number is the bit number. For example, the module: module main(out, in); output out; input [2:0] in; [...] endmodule creates these ports: out OUTPUT in[0] INPUT in[1] INPUT in[2] INPUT Target tools, including Xilinx Foundation tools, understand the [] characters in the name and recollect the signals into a proper bus when presenting the vector to the user. PADS AND PIN ASSIGNMENT The ports of a root module may be assigned to specific pins, or to a generic pad. If a signal (that is a port) has a PAD attribute, then the value of that attribute is a list of locations, one for each bit of the signal, that specifies the pin for each bit of the signal. For example: module main( (* PAD = "P10" *) output out, (* PAD = "P20,P21,P22" *) input [2:0] in); [...] endmodule In this example, port ``out'' is assigned to pin 10, and port ``in'' is assigned to pins 20-22. If the architecture supports it, a pin number of 0 means let the back end tools choose a pin. The format of the pin number depends on the architecture family being targeted, so for example Xilinx family devices take the name that is associated with the "LOC" attribute. NOTE: If a module port is assigned to a pin (and therefore attached to a PAD) then it is *not* connected to a port of the EDIF file. This is because the PAD (and possibly IBUF or OBUF) would become an extra driver to the port. An error. SPECIAL DEVICES The code generator supports the "cellref" attribute attached to logic devices to cause specific device types be generated, instead of the usual device that the code generator might generate. For example, to get a clock buffer out of a Verilog buf: buf my_gbuf(out, in); $attribute(my_buf, "cellref", "GBUF:O,I"); The "cellref" attribute tells the code generator to use the given cell. The syntax of the value is: :,... The cell type is the name of the library part to use. The pin names are the names of the type in the library, in the order that the logic device pins are connected. COMPILING WITH XILINX FOUNDATION Compile a single-file design with command line tools like so: % iverilog -parch=virtex -o foo.edf foo.vl % edif2ngd foo.edf foo.ngo % ngdbuild -p v50-pq240 foo.ngo foo.ngd % map -o map.ncd foo.ngd % par -w map.ncd foo.ncd iverilog-10_1/tgt-fpga/fpga_priv.h000066400000000000000000000031451265551621300172040ustar00rootroot00000000000000#ifndef IVL_fpga_priv_H #define IVL_fpga_priv_H /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "device.h" /* This is the opened xnf file descriptor. It is the output that this code generator writes to, whether the format is XNF or EDIF. */ extern FILE*xnf; extern int show_scope_gates(ivl_scope_t net, void*x); extern device_t device; extern const char*part; extern const char*arch; /* * Attribute lookup, should this be provided in ivl_target.h? */ int scope_has_attribute(ivl_scope_t s, const char *name); /* * These are mangle functions. */ extern void xnf_mangle_logic_name(ivl_net_logic_t net, char*buf, size_t nbuf); extern void xnf_mangle_lpm_name(ivl_lpm_t net, char*buf, size_t nbuf); extern const char*xnf_mangle_nexus_name(ivl_nexus_t net); #endif /* IVL_fpga_priv_H */ iverilog-10_1/tgt-fpga/gates.c000066400000000000000000000104431265551621300163240ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "fpga_priv.h" # include static void show_cell_scope(ivl_scope_t scope) { if (device->show_cell_scope == 0) { fprintf(stderr, "fpga.tgt: ivl_synthesis_cell(scope)" " not supported by this target.\n"); return; } device->show_cell_scope(scope); } static void show_gate_logic(ivl_net_logic_t net) { if (device->show_logic == 0) { fprintf(stderr, "fpga.tgt: IVL LOGIC not supported" " by this target.\n"); return; } assert(device->show_logic); device->show_logic(net); } static void show_gate_lpm(ivl_lpm_t net) { switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: if (device->show_add == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_ADD not supported" " by this target.\n"); return; } device->show_add(net); break; case IVL_LPM_SUB: if (device->show_sub == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_SUB not supported" " by this target.\n"); return; } device->show_sub(net); break; case IVL_LPM_CMP_EQ: if (device->show_cmp_eq == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_CMP_EQ not supported" " by this target.\n"); return; } device->show_cmp_eq(net); break; case IVL_LPM_CMP_NE: if (device->show_cmp_ne == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_CMP_NE not supported" " by this target.\n"); return; } device->show_cmp_ne(net); break; case IVL_LPM_CMP_GE: if (device->show_cmp_ge == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_CMP_GE not supported" " by this target.\n"); return; } device->show_cmp_ge(net); break; case IVL_LPM_CMP_GT: if (device->show_cmp_gt == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_CMP_GT not supported" " by this target.\n"); return; } device->show_cmp_gt(net); break; case IVL_LPM_FF: if (device->show_dff == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_FF not supported" " by this target.\n"); return; } device->show_dff(net); break; case IVL_LPM_MUX: if (device->show_mux == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_MUX not supported" " by this target.\n"); return; } device->show_mux(net); break; case IVL_LPM_MULT: if (device->show_mult == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_MULT not supported" " by this target.\n"); return; } device->show_mult(net); break; case IVL_LPM_SHIFTL: if (device->show_shiftl == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_SHIFTL not supported" " by this target.\n"); return; } device->show_shiftl(net); break; case IVL_LPM_SHIFTR: if (device->show_shiftr == 0) { fprintf(stderr, "fpga.tgt: IVL_LPM_SHIFTR not supported" " by this target.\n"); return; } device->show_shiftr(net); break; default: fprintf(stderr, "fpga.tgt: unknown LPM type %d\n", ivl_lpm_type(net)); break; } } int show_scope_gates(ivl_scope_t net, void*x) { unsigned idx; if (scope_has_attribute(net, "ivl_synthesis_cell")) { show_cell_scope(net); return 0; } for (idx = 0 ; idx < ivl_scope_logs(net) ; idx += 1) show_gate_logic(ivl_scope_log(net, idx)); for (idx = 0 ; idx < ivl_scope_lpms(net) ; idx += 1) show_gate_lpm(ivl_scope_lpm(net, idx)); return ivl_scope_children(net, show_scope_gates, 0); } iverilog-10_1/tgt-fpga/generic.c000066400000000000000000000020301265551621300166260ustar00rootroot00000000000000/* * Copyright (c) 2003 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "generic.h" edif_t edf = 0; edif_xlibrary_t xlib = 0; edif_cell_t cell_0 = 0; edif_cell_t cell_1 = 0; edif_cell_t cell_ipad = 0; edif_cell_t cell_opad = 0; edif_cell_t cell_iopad = 0; iverilog-10_1/tgt-fpga/generic.h000066400000000000000000000026551265551621300166500ustar00rootroot00000000000000#ifndef IVL_generic_H #define IVL_generic_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "edif.h" extern edif_t edf; extern edif_xlibrary_t xlib; /* * The cell_* variables below are the various kinds of devices that * this family supports as primitives. If the cell type is used at * least once, then the edif_cell_t is non-zero and will also be * included in the library declaration. The constants underneath are * pin assignments for the cell. */ extern edif_cell_t cell_0; extern edif_cell_t cell_1; extern edif_cell_t cell_ipad; extern edif_cell_t cell_opad; extern edif_cell_t cell_iopad; #endif /* IVL_generic_H */ iverilog-10_1/tgt-fpga/iverilog-fpga.man000066400000000000000000000133641265551621300203120ustar00rootroot00000000000000.TH iverilog-fpga 1 "$Date: 2004/10/04 01:10:57 $" Version "$Date: 2004/10/04 01:10:57 $" .SH NAME iverilog-fpga - FPGA code generator for Icarus Verilog .SH SYNOPSIS .B iverilog -tfpga [iverilog-options] sourcefile .SH DESCRIPTION .PP The FPGA code generator supports a variety of FPGA devices, writing EDIF output depending on the target. You can select the architecture of the device, and the detailed part name. The architecture is used to select library primitives, and the detailed part name is written into the generated file for the use of downstream tools. The code generator is invoked with the \-tfpga flag to iverilog. It understands the part= and the arch= parameters, which can be set with the \-p flag of iverilog: iverilog \-parch=virtex \-ppart=v50\-pq240\-6 \-tfpga foo.vl This example selects the Virtex architecture, and give the detailed part number as v50\-pq240\-6. The output is written into a.out unless a different output file is specified with the \-o flag. .SH OPTIONS \fIiverilog \-tfpga\fP accepts the following options: .TP 8 .B -parch=\fIfamily\fP The \fIfamily\fP setting further specifies the target device family. See FPGA FAMILIES below. .TP 8 .B -ppart=\fIdevice\fP This specifies a specific device in the form of a detailed part number. The format of this number is defined by the part vendor. In most cases, the device string is taken literally and written as is to the EDIF output. .SH "FPGA FAMILIES" The following is a list of architecture types that this code generator supports. .TP 8 .B lpm This is a device independent format, where the gates are device types as defined by the LPM 2 1 0 specification. Some backend tools may take this format, or users may write interface libraries to connect these netlists to the device in question. The \fBlpm\fP family is the default if no other is specified. .TP 8 .B virtex If this is selected, then the output is formatted as an EDIF 2 0 0 file, suitable for Virtex class devices. This is supposed to know that you are targeting a Virtex part, so can generate primitives instead of using external macros. It includes the VIRTEX internal library, and should work properly for any Virtex part. .TP 8 .B virtex2 If this is selected, then the output is EDIF 2 0 0 suitable for Virtex\-II and Virtex\-II Pro devices. It uses the VIRTEX2 library, but is very similar to the Virtex target. .SH "EDIF ROOT PORTS" The EDIF format is explicit about the interface into an EDIF file. The code generator uses that control to generate an explicit interface definition into the design. (This is *not* the same as the PADS of a part.) The generated EDIF interface section contains port definitions, including the proper direction marks. With the (rename ...) s\-exp in EDIF, it is possible to assign arbitrary text to port names. The EDIF code generator therefore does not resort to the mangling that is needed for internal symbols. The base name of the signal that is an input or output is used as the name of the port, complete with the proper case. However, since the ports are single bit ports, the name of vectors includes the string "[0]" where the number is the bit number. For example, the module: .nf module main(out, in); output out; input [2:0] in; [...] endmodule .fi creates these ports: .nf out OUTPUT in[0] INPUT in[1] INPUT in[2] INPUT .fi Target tools, including Xilinx Foundation tools, understand the [] characters in the name and recollect the signals into a proper bus when presenting the vector to the user. .SH "PADS AND PIN ASSIGNMENT" The ports of a root module may be assigned to specific pins, or to a generic pad. If a signal (that is a port) has a PAD attribute, then the value of that attribute is a list of locations, one for each bit of the signal, that specifies the pin for each bit of the signal. For example: .nf module main( (* PAD = "P10" *) output out, (* PAD = "P20,P21,P22" *) input [2:0] in); [...] endmodule .fi In this example, port ``out'' is assigned to pin 10, and port ``in'' is assigned to pins 20\-22. If the architecture supports it, a pin number of 0 means let the back end tools choose a pin. The format of the pin number depends on the architecture family being targeted, so for example Xilinx family devices take the name that is associated with the "LOC" attribute. NOTE: If a module port is assigned to a pin (and therefore attached to a PAD) then it is *not* connected to a port of the EDIF file. This is because the PAD (and possibly IBUF or OBUF) would become an extra driver to the port. An error. .SH "SPECIAL DEVICES" The code generator supports the "cellref" attribute attached to logic devices to cause specific device types be generated, instead of the usual device that the code generator might generate. For example, to get a clock buffer out of a Verilog buf: .nf buf my_gbuf(out, in); $attribute(my_buf, "cellref", "GBUF:O,I"); .fi The "cellref" attribute tells the code generator to use the given cell. The syntax of the value is: .nf :,... .fi The cell type is the name of the library part to use. The pin names are the names of the type in the library, in the order that the logic device pins are connected. .SH EXAMPLES .TB 8 .I COMPILING WITH XILINX FOUNDATION/ISE Compile a single-file design with command line tools like so: .nf % iverilog \-parch=virtex \-o foo.edf foo.vl % edif2ngd foo.edf foo.ngo % ngdbuild \-p v50\-pq240 foo.ngo foo.ngd % map \-o map.ncd foo.ngd % par \-w map.ncd foo.ncd .fi .SH "AUTHOR" .nf Steve Williams (steve@icarus.com) .SH SEE ALSO iverilog(1), .BR "" .SH COPYRIGHT .nf Copyright \(co 2003 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 iverilog-10_1/tgt-fpga/mangle.c000066400000000000000000000052371265551621300164710ustar00rootroot00000000000000/* * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "fpga_priv.h" # include # include # include "ivl_alloc.h" static size_t xnf_mangle_scope_name(ivl_scope_t net, char*buf, size_t nbuf) { unsigned cnt = 0; ivl_scope_t parent = ivl_scope_parent(net); if (parent) { cnt = xnf_mangle_scope_name(parent, buf, nbuf); buf += cnt; nbuf -= cnt; *buf++ = '/'; nbuf -= 1; cnt += 1; } strcpy(buf, ivl_scope_basename(net)); cnt += strlen(buf); return cnt; } void xnf_mangle_logic_name(ivl_net_logic_t net, char*buf, size_t nbuf) { size_t cnt = xnf_mangle_scope_name(ivl_logic_scope(net), buf, nbuf); buf[cnt++] = '/'; strcpy(buf+cnt, ivl_logic_basename(net)); } void xnf_mangle_lpm_name(ivl_lpm_t net, char*buf, size_t nbuf) { size_t cnt = xnf_mangle_scope_name(ivl_lpm_scope(net), buf, nbuf); buf[cnt++] = '/'; strcpy(buf+cnt, ivl_lpm_basename(net)); } /* * Nexus names are used in pin records to connect things together. It * almost doesn't matter what the nexus name is, but for readability * we choose a name that is close to the nexus name. This function * converts the existing name to a name that XNF can use. * * For speed, this function saves the calculated string into the real * nexus by using the private pointer. Every nexus is used at least * twice, so this cuts the mangling time in half at least. */ const char* xnf_mangle_nexus_name(ivl_nexus_t net) { char*name = ivl_nexus_get_private(net); char*cp; if (name != 0) { return name; } name = malloc(strlen(ivl_nexus_name(net)) + 1); strcpy(name, ivl_nexus_name(net)); for (cp = name ; *cp ; cp += 1) switch (*cp) { case '.': *cp = '/'; break; default: break; } ivl_nexus_set_private(net, name); return name; } iverilog-10_1/tgt-fpga/tables.c000066400000000000000000000032721265551621300164750ustar00rootroot00000000000000/* * Copyright (c) 2001 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "fpga_priv.h" # include # include extern const struct device_s d_generic; extern const struct device_s d_generic_edif; extern const struct device_s d_lpm_edif; extern const struct device_s d_virtex_edif; extern const struct device_s d_virtex2_edif; const struct device_table_s { const char*name; device_t driver; } device_table[] = { { "generic-edif", &d_generic_edif }, { "generic-xnf", &d_generic }, { "lpm", &d_lpm_edif }, { "virtex", &d_virtex_edif }, { "virtex2", &d_virtex2_edif }, { 0, 0 } }; device_t device_from_arch(const char*arch) { unsigned idx; assert(arch); for (idx = 0 ; device_table[idx].name ; idx += 1) { if (strcmp(arch, device_table[idx].name) == 0) return device_table[idx].driver; } return 0; } iverilog-10_1/tgt-fpga/xilinx.c000066400000000000000000000650101265551621300165340ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve at icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "edif.h" # include "generic.h" # include "xilinx.h" # include "fpga_priv.h" # include # include # include # include "ivl_alloc.h" edif_cell_t xilinx_cell_buf(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "BUF", 2); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_bufe(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "BUFE", 3); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); edif_cell_portconfig(cell, BUF_T, "E", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_bufg(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "BUFG", 2); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_buft(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "BUFT", 3); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); edif_cell_portconfig(cell, BUF_T, "T", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_ibuf(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "IBUF", 2); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_inv(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "INV", 2); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_muxf5(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "MUXF5", 4); edif_cell_portconfig(cell, MUXF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, MUXF_I0, "I0", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXF_I1, "I1", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXF_S, "S", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_muxf6(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "MUXF6", 4); edif_cell_portconfig(cell, MUXF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, MUXF_I0, "I0", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXF_I1, "I1", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXF_S, "S", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_obuf(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell) return cell; cell = edif_xcell_create(xlib, "OBUF", 2); edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_lut2(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "LUT2", 3); edif_cell_portconfig(cell, LUT_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, LUT_I0, "I0", IVL_SIP_INPUT); edif_cell_portconfig(cell, LUT_I1, "I1", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_lut3(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "LUT3", 4); edif_cell_portconfig(cell, LUT_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, LUT_I0, "I0", IVL_SIP_INPUT); edif_cell_portconfig(cell, LUT_I1, "I1", IVL_SIP_INPUT); edif_cell_portconfig(cell, LUT_I2, "I2", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_lut4(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "LUT4", 5); edif_cell_portconfig(cell, LUT_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, LUT_I0, "I0", IVL_SIP_INPUT); edif_cell_portconfig(cell, LUT_I1, "I1", IVL_SIP_INPUT); edif_cell_portconfig(cell, LUT_I2, "I2", IVL_SIP_INPUT); edif_cell_portconfig(cell, LUT_I3, "I3", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_fdce(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "FDCE", 5); edif_cell_portconfig(cell, FDCE_Q, "Q", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, FDCE_D, "D", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_C, "C", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_CE, "CE", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_CLR,"CLR", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_fdcpe(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "FDCPE", 6); edif_cell_portconfig(cell, FDCE_Q, "Q", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, FDCE_D, "D", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_C, "C", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_CE, "CE", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_CLR,"CLR", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_PRE,"PRE", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_fdre(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "FDRE", 5); edif_cell_portconfig(cell, FDCE_Q, "Q", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, FDCE_D, "D", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_C, "C", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_CE, "CE", IVL_SIP_INPUT); edif_cell_portconfig(cell, FDCE_CLR,"R", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_mult_and(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "MULT_AND", 3); edif_cell_portconfig(cell, MULT_AND_LO, "LO", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, MULT_AND_I0, "I0", IVL_SIP_INPUT); edif_cell_portconfig(cell, MULT_AND_I1, "I1", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_muxcy(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "MUXCY", 4); edif_cell_portconfig(cell, MUXCY_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, MUXCY_DI, "DI", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXCY_CI, "CI", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXCY_S, "S", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_muxcy_l(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "MUXCY_L", 4); edif_cell_portconfig(cell, MUXCY_O, "LO", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, MUXCY_DI, "DI", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXCY_CI, "CI", IVL_SIP_INPUT); edif_cell_portconfig(cell, MUXCY_S, "S", IVL_SIP_INPUT); return cell; } edif_cell_t xilinx_cell_xorcy(edif_xlibrary_t xlib) { static edif_cell_t cell = 0; if (cell != 0) return cell; cell = edif_xcell_create(xlib, "XORCY", 3); edif_cell_portconfig(cell, XORCY_O, "O", IVL_SIP_OUTPUT); edif_cell_portconfig(cell, XORCY_CI, "CI", IVL_SIP_INPUT); edif_cell_portconfig(cell, XORCY_LI, "LI", IVL_SIP_INPUT); return cell; } /* * This function does a lot of the stuff common to the header * functions of various Xilinx families. This includes creating the edf * object that holds the netlist. */ void xilinx_common_header(ivl_design_t des) { unsigned idx; ivl_scope_t root = ivl_design_root(des); unsigned sig_cnt = ivl_scope_sigs(root); unsigned nports = 0, pidx; /* Count the ports I'm going to use. */ for (idx = 0 ; idx < sig_cnt ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(root, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; if (ivl_signal_attr(sig, "PAD") != 0) continue; nports += ivl_signal_pins(sig); } edf = edif_create(ivl_scope_basename(root), nports); pidx = 0; for (idx = 0 ; idx < sig_cnt ; idx += 1) { edif_joint_t jnt; ivl_signal_t sig = ivl_scope_sig(root, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; if (ivl_signal_attr(sig, "PAD") != 0) continue; if (ivl_signal_pins(sig) == 1) { edif_portconfig(edf, pidx, ivl_signal_basename(sig), ivl_signal_port(sig)); assert(ivl_signal_pins(sig) == 1); jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, 0)); edif_port_to_joint(jnt, edf, pidx); } else { const char*name = ivl_signal_basename(sig); ivl_signal_port_t dir = ivl_signal_port(sig); char buf[128]; unsigned bit; for (bit = 0 ; bit < ivl_signal_pins(sig) ; bit += 1) { const char*tmp; sprintf(buf, "%s[%u]", name, bit); tmp = strdup(buf); edif_portconfig(edf, pidx+bit, tmp, dir); jnt = edif_joint_of_nexus(edf,ivl_signal_pin(sig,bit)); edif_port_to_joint(jnt, edf, pidx+bit); } } pidx += ivl_signal_pins(sig); } assert(pidx == nports); } void xilinx_show_footer(ivl_design_t des) { unsigned idx; for (idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) { unsigned pin; ivl_net_const_t net = ivl_design_const(des, idx); const char*val = ivl_const_bits(net); for (pin = 0 ; pin < ivl_const_pins(net) ; pin += 1) { edif_joint_t jnt; edif_cellref_t pad; jnt = edif_joint_of_nexus(edf, ivl_const_pin(net, pin)); switch (val[pin]) { case '0': pad = edif_cellref_create(edf, cell_0); break; case '1': pad = edif_cellref_create(edf, cell_1); break; default: assert(0); break; } edif_add_to_joint(jnt, pad, 0); } } edif_print(xnf, edf); } /* * Make (or retrieve) a cell in the external library that reflects the * scope with its ports. */ void xilinx_show_scope(ivl_scope_t scope) { edif_cell_t cell; edif_cellref_t ref; unsigned port, idx; cell = edif_xlibrary_scope_cell(xlib, scope); ref = edif_cellref_create(edf, cell); for (idx = 0 ; idx < ivl_scope_sigs(scope) ; idx += 1) { edif_joint_t jnt; ivl_signal_t sig = ivl_scope_sig(scope, idx); if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; port = edif_cell_port_byname(cell, ivl_signal_basename(sig)); jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, 0)); edif_add_to_joint(jnt, ref, port); } } void xilinx_pad(ivl_signal_t sig, const char*str) { unsigned idx; char**pins; if (cell_ipad == 0) { cell_ipad = edif_xcell_create(xlib, "IPAD", 1); edif_cell_portconfig(cell_ipad, 0, "IPAD", IVL_SIP_OUTPUT); } if (cell_opad == 0) { cell_opad = edif_xcell_create(xlib, "OPAD", 1); edif_cell_portconfig(cell_opad, 0, "OPAD", IVL_SIP_INPUT); } if (cell_iopad == 0) { cell_iopad = edif_xcell_create(xlib, "IOPAD", 1); edif_cell_portconfig(cell_iopad, 0, "IOPAD", IVL_SIP_INOUT); } /* Collect an array of pin assignments from the attribute string passed in as str. The format is a comma separated list of location names. */ pins = calloc(ivl_signal_pins(sig), sizeof(char*)); for (idx = 0 ; idx < ivl_signal_pins(sig) ; idx += 1) { const char*tmp = strchr(str, ','); if (tmp == 0) tmp = str+strlen(str); pins[idx] = malloc(tmp-str+1); strncpy(pins[idx], str, tmp-str); pins[idx][tmp-str] = 0; if (*tmp != 0) tmp += 1; str = tmp; } /* Now go through the pins of the signal, creating pads and bufs and joining them to the signal nexus. */ for (idx = 0 ; idx < ivl_signal_pins(sig) ; idx += 1) { edif_joint_t jnt; edif_cellref_t pad, buf; const char*name_str = ivl_signal_basename(sig); if (ivl_signal_pins(sig) > 1) { char name_buf[128]; sprintf(name_buf, "%s[%u]", name_str, idx); name_str = strdup(name_buf); } switch (ivl_signal_port(sig)) { case IVL_SIP_INPUT: pad = edif_cellref_create(edf, cell_ipad); buf = edif_cellref_create(edf, xilinx_cell_ibuf(xlib)); jnt = edif_joint_create(edf); edif_joint_rename(jnt, name_str); edif_add_to_joint(jnt, pad, 0); edif_add_to_joint(jnt, buf, BUF_I); jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, idx)); edif_add_to_joint(jnt, buf, BUF_O); break; case IVL_SIP_OUTPUT: pad = edif_cellref_create(edf, cell_opad); buf = edif_cellref_create(edf, xilinx_cell_obuf(xlib)); jnt = edif_joint_create(edf); edif_joint_rename(jnt, name_str); edif_add_to_joint(jnt, pad, 0); edif_add_to_joint(jnt, buf, BUF_O); jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, idx)); edif_add_to_joint(jnt, buf, BUF_I); break; case IVL_SIP_INOUT: pad = edif_cellref_create(edf, cell_iopad); jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, idx)); edif_add_to_joint(jnt, pad, 0); break; default: assert(0); } if (pins[idx]) edif_cellref_pstring(pad, "LOC", pins[idx]); } /* Don't free the allocated pad name strings. The edif_cellref_pstring function attached the string to the LOC attribute, so the reference is permanent. */ free(pins); } /* * This function handles the case where the user specifies the cell to * use by attribute. */ static void edif_cellref_logic(ivl_net_logic_t net, const char*def) { char*str = strdup(def); char*pins; edif_cell_t cell; edif_cellref_t ref; unsigned idx; pins = strchr(str, ':'); assert(pins); *pins++ = 0; /* Locate the cell in the library, lookup by name. */ cell = edif_xlibrary_findcell(xlib, str); assert(cell); ref = edif_cellref_create(edf, cell); for (idx = 0 ; idx < ivl_logic_pins(net) ; idx += 1) { char*tmp; edif_joint_t jnt; unsigned port; assert(pins); tmp = strchr(pins,','); if (tmp != 0) *tmp++ = 0; else tmp = 0; port = edif_cell_port_byname(cell, pins); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx)); edif_add_to_joint(jnt, ref, port); pins = tmp; } free(str); } static void lut_logic(ivl_net_logic_t net, const char*init3, const char*init4, const char*init5) { edif_cellref_t lut = NULL; /* initialization shuts up gcc -Wall */ edif_joint_t jnt; const char* init = NULL; /* ditto */ assert(ivl_logic_pins(net) <= 5); assert(ivl_logic_pins(net) >= 3); switch (ivl_logic_pins(net)) { case 3: lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); init = init3; break; case 4: lut = edif_cellref_create(edf, xilinx_cell_lut3(xlib)); init = init4; break; case 5: lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib)); init = init5; break; } edif_cellref_pstring(lut, "INIT", init); switch (ivl_logic_pins(net)) { case 5: jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 4)); edif_add_to_joint(jnt, lut, LUT_I3); case 4: jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 3)); edif_add_to_joint(jnt, lut, LUT_I2); case 3: jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2)); edif_add_to_joint(jnt, lut, LUT_I1); } jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); } void xilinx_logic(ivl_net_logic_t net) { edif_cellref_t obj; edif_joint_t jnt; { const char*cellref_attribute = ivl_logic_attr(net, "cellref"); if (cellref_attribute != 0) { edif_cellref_logic(net, cellref_attribute); return; } } switch (ivl_logic_type(net)) { case IVL_LO_BUF: case IVL_LO_BUFZ: assert(ivl_logic_pins(net) == 2); obj = edif_cellref_create(edf, xilinx_cell_buf(xlib)); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, obj, BUF_O); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, obj, BUF_I); break; case IVL_LO_BUFIF0: /* The Xilinx BUFT devices is a BUF that adds a T input. The output is tri-stated if the T input is 1. In other words, it acts just like bufif0. */ assert(ivl_logic_pins(net) == 3); obj = edif_cellref_create(edf, xilinx_cell_buft(xlib)); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, obj, BUF_O); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, obj, BUF_I); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2)); edif_add_to_joint(jnt, obj, BUF_T); break; case IVL_LO_BUFIF1: /* The Xilinx BUFE devices is a BUF that adds an enable input. The output is tri-stated if the E input is 0. In other words, it acts just like bufif1. */ assert(ivl_logic_pins(net) == 3); obj = edif_cellref_create(edf, xilinx_cell_bufe(xlib)); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, obj, BUF_O); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, obj, BUF_I); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2)); edif_add_to_joint(jnt, obj, BUF_T); break; case IVL_LO_NOT: assert(ivl_logic_pins(net) == 2); obj = edif_cellref_create(edf, xilinx_cell_inv(xlib)); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0)); edif_add_to_joint(jnt, obj, BUF_O); jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1)); edif_add_to_joint(jnt, obj, BUF_I); break; case IVL_LO_AND: assert(ivl_logic_pins(net) <= 5); assert(ivl_logic_pins(net) >= 3); lut_logic(net, "8", "80", "8000"); break; case IVL_LO_NOR: assert(ivl_logic_pins(net) <= 5); assert(ivl_logic_pins(net) >= 3); lut_logic(net, "1", "01", "0001"); break; case IVL_LO_OR: assert(ivl_logic_pins(net) <= 5); assert(ivl_logic_pins(net) >= 3); lut_logic(net, "E", "FE", "FFFE"); break; case IVL_LO_XNOR: assert(ivl_logic_pins(net) <= 5); assert(ivl_logic_pins(net) >= 3); lut_logic(net, "9", "69", "9669"); break; case IVL_LO_XOR: assert(ivl_logic_pins(net) <= 5); assert(ivl_logic_pins(net) >= 3); lut_logic(net, "6", "96", "6996"); break; default: fprintf(stderr, "UNSUPPORTED LOGIC TYPE: %d\n", ivl_logic_type(net)); break; } } /* * A fully generic Xilinx MUX is implemented entirely from LUT * devices. */ void xilinx_mux(ivl_lpm_t net) { unsigned idx; assert(ivl_lpm_selects(net) == 1); /* A/B Mux devices are made from LUT3 devices. I0 is connected to A, I1 to B, and I2 to the Select input. Create as many as are needed to implement the requested width. S B A | Q ------+-- 0 0 0 | 0 0 0 1 | 1 0 1 0 | 0 0 1 1 | 1 1 0 0 | 0 1 0 1 | 0 1 1 0 | 1 1 1 1 | 1 INIT = "CA" */ for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) { edif_cellref_t lut; edif_joint_t jnt; lut = edif_cellref_create(edf, xilinx_cell_lut3(xlib)); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 0, idx)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 1, idx)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net, 0)); edif_add_to_joint(jnt, lut, LUT_I2); edif_cellref_pstring(lut, "INIT", "CA"); } } /* * Any Xilinx device works with this adder. * Generic Xilinx add only works for single bit slices. */ void xilinx_add(ivl_lpm_t net) { const char*ha_init = 0; switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: ha_init = "6"; break; case IVL_LPM_SUB: ha_init = "9"; break; default: assert(0); } /* If this is a single bit wide, then generate only a half-adder. Normally this is an XOR, but if this is a SUB then it is an XNOR. */ if (ivl_lpm_width(net) == 1) { edif_cellref_t lut; edif_joint_t jnt; lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); edif_cellref_pstring(lut, "INIT", ha_init); return; } assert(0); } /* * The left shift is implemented as a matrix of MUX2_1 devices. The * matrix has as many rows as the device width, and a column for each * select. */ void xilinx_shiftl(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned nsel = 0; unsigned sdx, qdx; edif_cellref_t* cells; edif_cellref_t**table; edif_cellref_t pad0_cell; edif_joint_t pad0; /* First, find out how many select inputs we really need. We can only use the selects that are enough to shift out the entire width of the device. The excess can be used as an enable for the last column. When disabled, the last column emits zeros. */ while (nsel < ivl_lpm_selects(net)) { unsigned swid; nsel += 1; swid = 1 << nsel; if (swid >= width) break; } assert(nsel > 0); /* Allocate a matrix of edif_cellref_t variables. A devices will be addressed by the expression table[sdx][qdx]; This should make the algorithm code easier to read. */ cells = calloc(nsel * width, sizeof(edif_cellref_t)); table = calloc(nsel, sizeof(edif_cellref_t*)); for (sdx = 0 ; sdx < nsel ; sdx += 1) table[sdx] = cells + sdx*width; /* Make a 0 valued pad bit. I will use this for all the shift in values that are beyond the input. */ pad0_cell = edif_cellref_create(edf, cell_0); pad0 = edif_joint_create(edf); edif_add_to_joint(pad0, pad0_cell, 0); /* The LUT matrix is columns of devices, with the last column a LUT4 devices. The extra input of the LUT4s in the last column are used as an enable to collect all the excess select inputs. */ /* Allocate the LUT devices of the matrix, and connect the select inputs to I2 of all the devices of the column. */ for (sdx = 0 ; sdx < nsel ; sdx += 1) { const char*init_string = 0; ivl_nexus_t nex = ivl_lpm_select(net,sdx); edif_joint_t sdx_jnt = edif_joint_of_nexus(edf, nex); edif_cell_t lut; if (((sdx+1) == nsel) && (nsel < ivl_lpm_selects(net))) { lut = xilinx_cell_lut4(xlib); init_string = "00CA"; } else { lut = xilinx_cell_lut3(xlib); init_string = "CA"; } for (qdx = 0 ; qdx < width ; qdx += 1) { table[sdx][qdx] = edif_cellref_create(edf, lut); edif_add_to_joint(sdx_jnt, table[sdx][qdx], LUT_I2); edif_cellref_pstring(table[sdx][qdx], "INIT", init_string); } } /* Connect the inputs of the SHIFTL device to the column 0 LUT inputs. The slice on the low end shifts in a 0 for a select input. */ for (qdx = 0 ; qdx < width ; qdx += 1) { ivl_nexus_t nex0; edif_joint_t jnt0; edif_joint_t jnt1; nex0 = ivl_lpm_data(net,qdx); jnt0 = edif_joint_of_nexus(edf, nex0); if (qdx > 0) { ivl_nexus_t nex1; nex1 = ivl_lpm_data(net,qdx-1); jnt1 = edif_joint_of_nexus(edf, nex1); } else { jnt1 = pad0; } edif_add_to_joint(jnt0, table[0][qdx], LUT_I0); edif_add_to_joint(jnt1, table[0][qdx], LUT_I1); } /* Make the inner connections between LUT devices. Each column connects to the previous column, shifted by the power of the column value. If the shifted input falls off the end, then pad with zero. */ for (sdx = 1 ; sdx < nsel ; sdx += 1) { for (qdx = 0 ; qdx < width ; qdx += 1) { unsigned shift = 1 << sdx; edif_joint_t jnt0 = edif_joint_create(edf); edif_joint_t jnt1 = (qdx >= shift) ? edif_joint_create(edf) : pad0; edif_add_to_joint(jnt0, table[sdx][qdx], LUT_I0); edif_add_to_joint(jnt1, table[sdx][qdx], LUT_I1); edif_add_to_joint(jnt0, table[sdx-1][qdx], LUT_O); if (qdx >= shift) edif_add_to_joint(jnt1, table[sdx-1][qdx-shift], LUT_O); } } /* Connect the output of the last column to the output of the SHIFTL device. */ for (qdx = 0 ; qdx < width ; qdx += 1) { ivl_nexus_t nex = ivl_lpm_q(net,qdx); edif_joint_t jnt = edif_joint_of_nexus(edf, nex); edif_add_to_joint(jnt, table[nsel-1][qdx], LUT_O); } /* Connect the excess select inputs to the enable inputs of the LUT4 devices in the last column. */ if (nsel < ivl_lpm_selects(net)) { edif_joint_t jnt; /* XXXX Only support 1 excess bit for now. */ assert((nsel + 1) == ivl_lpm_selects(net)); jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net,nsel)); for (qdx = 0 ; qdx < width ; qdx += 1) edif_add_to_joint(jnt, table[nsel-1][qdx], LUT_I3); } free(cells); free(table); } iverilog-10_1/tgt-fpga/xilinx.h000066400000000000000000000104511265551621300165400ustar00rootroot00000000000000#ifndef IVL_xilinx_H #define IVL_xilinx_H /* * Copyright (c) 2003-2014 Stephen Williams (steve at icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This header file includes XILINX library support functions. They * manage the creation and reference of cells from the library. Use * the xilinx_cell_* functions to get an edif_cell_t from the * library. The function will create the cell in the library if * needed, or will return the existing cell if it was already called. */ # include "edif.h" /* === BUF Devices === */ /* Buffer types of devices have the BUF_O and BUF_I pin assignments. The BUF, INV, and certain specialized devices fit in this category. */ extern edif_cell_t xilinx_cell_buf (edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_bufe(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_bufg(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_buft(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_inv (edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_ibuf(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_obuf(edif_xlibrary_t xlib); #define BUF_O 0 #define BUF_I 1 /* Only bufe and buft buffers have this input. */ #define BUF_T 2 /* === LUT Devices === */ /* Most Xilinx devices have LUT2/3/4 devices that take, respectively, 2, 3 or 4 inputs. All forms have a single bit output. Also, the real behavior of the device will need to be specified by an INIT parameter string. */ extern edif_cell_t xilinx_cell_lut2(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_lut3(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_lut4(edif_xlibrary_t xlib); #define LUT_O 0 #define LUT_I0 1 #define LUT_I1 2 #define LUT_I2 3 #define LUT_I3 4 /* === Flip-Flop Devices === */ /* * These are flip-flops of various sort, but similar pinouts. */ extern edif_cell_t xilinx_cell_fdce(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_fdcpe(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_fdre(edif_xlibrary_t xlib); #define FDCE_Q 0 #define FDCE_C 1 #define FDCE_D 2 #define FDCE_CE 3 #define FDCE_CLR 4 #define FDCE_PRE 5 /* === Virtex/Virtex2 Carry Chain Logic === */ extern edif_cell_t xilinx_cell_mult_and(edif_xlibrary_t xlib); #define MULT_AND_LO 0 #define MULT_AND_I0 1 #define MULT_AND_I1 2 extern edif_cell_t xilinx_cell_muxcy(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_muxcy_l(edif_xlibrary_t xlib); #define MUXCY_O 0 #define MUXCY_DI 1 #define MUXCY_CI 2 #define MUXCY_S 3 extern edif_cell_t xilinx_cell_xorcy(edif_xlibrary_t xlib); #define XORCY_O 0 #define XORCY_CI 1 #define XORCY_LI 2 /* === Virtex/Virtex2 MUX devices */ extern edif_cell_t xilinx_cell_muxf5(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_muxf6(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_muxf7(edif_xlibrary_t xlib); extern edif_cell_t xilinx_cell_muxf8(edif_xlibrary_t xlib); #define MUXF_O 0 #define MUXF_I0 1 #define MUXF_I1 2 #define MUXF_S 3 /* === Inheritable Methods === */ extern void virtex_logic(ivl_net_logic_t net); extern void virtex_generic_dff(ivl_lpm_t net); extern void virtex_eq(ivl_lpm_t net); extern void virtex_ge(ivl_lpm_t net); extern void virtex_mux(ivl_lpm_t net); extern void virtex_add(ivl_lpm_t net); extern void xilinx_common_header(ivl_design_t des); extern void xilinx_show_footer(ivl_design_t des); extern void xilinx_show_scope(ivl_scope_t scope); extern void xilinx_pad(ivl_signal_t, const char*str); extern void xilinx_logic(ivl_net_logic_t net); extern void xilinx_mux(ivl_lpm_t net); extern void xilinx_add(ivl_lpm_t net); extern void xilinx_shiftl(ivl_lpm_t net); #endif /* IVL_xilinx_H */ iverilog-10_1/tgt-null/000077500000000000000000000000001265551621300151105ustar00rootroot00000000000000iverilog-10_1/tgt-null/Makefile.in000066400000000000000000000054101265551621300171550ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = null.o all: dep null.tgt check: all clean: rm -rf *.o dep null.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-null/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif null.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl$(suffix)/null.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/null.conf $(libdir)/ivl$(suffix)/null-s.conf $(libdir)/ivl$(suffix)/null.tgt: ./null.tgt $(INSTALL_PROGRAM) ./null.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/null.tgt" $(libdir)/ivl$(suffix)/null.conf: $(srcdir)/null.conf $(INSTALL_DATA) $(srcdir)/null.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/null.conf" $(libdir)/ivl$(suffix)/null-s.conf: $(srcdir)/null-s.conf $(INSTALL_DATA) $(srcdir)/null-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/null-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/null.tgt" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/null.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/null-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-null/cppcheck.sup000066400000000000000000000002711265551621300174210ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:null.c:50 // target_query() unusedFunction:null.c:57 iverilog-10_1/tgt-null/null-s.conf000066400000000000000000000001011265551621300171610ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules flag:DLL=null.tgt iverilog-10_1/tgt-null/null.c000066400000000000000000000042301265551621300162250ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "version_base.h" # include "version_tag.h" # include "config.h" # include /* * This is a null target module. It does nothing. */ # include "ivl_target.h" static const char*version_string = "Icarus Verilog NULL Code Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; int target_design(ivl_design_t des) { (void)des; /* Parameter is not used. */ return 0; } const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } iverilog-10_1/tgt-null/null.conf000066400000000000000000000000221265551621300167230ustar00rootroot00000000000000flag:DLL=null.tgt iverilog-10_1/tgt-pal/000077500000000000000000000000001265551621300147125ustar00rootroot00000000000000iverilog-10_1/tgt-pal/Makefile.in000066400000000000000000000044561265551621300167700ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = imain.o dump_final.o emit_jed.o enables.o fit_log.o fit_reg.o pads.o all: dep pal.tgt check: all clean: rm -rf *.o dep pal.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-pal/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@CYGWIN@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif pal.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -lipal install: all installdirs $(libdir)/ivl/pal.tgt $(libdir)/ivl/pal.tgt: ./pal.tgt $(INSTALL_PROGRAM) ./pal.tgt "$(DESTDIR)$(libdir)/ivl/pal.tgt" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)/$(libdir)/ivl" uninstall: rm -f "$DESTDIR)$(libdir)/ivl/pal.tgt" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-pal/cppcheck.sup000066400000000000000000000002161265551621300172220ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:imain.c:59 iverilog-10_1/tgt-pal/dump_final.c000066400000000000000000000032471265551621300172020ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include void dump_final_design(FILE*out) { unsigned idx; for (idx = 0 ; idx < pins ; idx += 1) { struct pal_bind_s*pin = bind_pin + idx; if (bind_pin[idx].sop) { fprintf(out, "Output pin %u:\n", idx+1); fprintf(out, " pin nexus=%s\n", pin->nexus? ivl_nexus_name(pin->nexus) : ""); fprintf(out, " pin enable=%s\n", pin->enable ? ivl_logic_name(pin->enable) : "1"); if (pin->reg) fprintf(out, " pin ff=%s.%s.q%u\n", ivl_scope_name(ivl_lpm_scope(pin->reg)), ivl_lpm_basename(pin->reg), pin->reg_q); else fprintf(out, " pin ff=*.q%u\n", pin->reg_q); } else { fprintf(out, "Input pin %u:\n", idx+1); fprintf(out, " pin nexus=%s\n", pin->nexus? ivl_nexus_name(pin->nexus) : ""); } } } iverilog-10_1/tgt-pal/emit_jed.c000066400000000000000000000064521265551621300166450ustar00rootroot00000000000000/* * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include # include # include # include "ivl_alloc.h" static void draw_macrocell_modes(FILE*jfd) { unsigned idx; unsigned cfuses; unsigned mode, mcnt; for (idx = 0 ; idx < pins ; idx += 1) { char*str; unsigned ffirst, flast, tmp; struct pal_bind_s*cur = bind_pin + idx; if (cur->sop == 0) continue; cfuses = pal_sop_cfuses(cur->sop); mcnt = 1 << pal_sop_cfuses(cur->sop); mode = 0; for (mode = 0 ; mode < mcnt ; mode += 1) { pal_sop_set_mode(cur->sop, mode); if (cur->reg && !pal_sop_is_register(cur->sop)) continue; if (!cur->reg && pal_sop_is_register(cur->sop)) continue; if (cur->sop_inv && !pal_sop_is_invert(cur->sop)) continue; if (!cur->sop_inv && pal_sop_is_invert(cur->sop)) continue; break; } assert(mode < mcnt); ffirst = pal_sop_cfuse(cur->sop, 0); flast = pal_sop_cfuse(cur->sop, 0); for (tmp = 1 ; tmp < cfuses ; tmp += 1) { unsigned f = pal_sop_cfuse(cur->sop, tmp); if (f < ffirst) ffirst = f; if (f > flast) flast = f; } assert(flast == (ffirst + cfuses - 1)); str = malloc(cfuses+1); str[cfuses] = 0; for (tmp = 0 ; tmp < cfuses ; tmp += 1) { if (mode & (1 << (cfuses-tmp-1))) str[pal_sop_cfuse(cur->sop, tmp)-ffirst] = '1'; else str[pal_sop_cfuse(cur->sop, tmp)-ffirst] = '0'; } fprintf(jfd, "L%05u %s* Note: ", ffirst, str); if (cur->nexus) fprintf(jfd, "%s ", ivl_nexus_name(cur->nexus)); { int pin = pal_sop_pin(cur->sop); if (pin > 0) fprintf(jfd, "pin %d: ", pin); } if (pal_sop_is_register(cur->sop)) fprintf(jfd, "sop)) fprintf(jfd, ", invert"); fprintf(jfd, "> *\n"); free(str); } } int emit_jedec(const char*path) { FILE*jfd; jfd = fopen(path, "w"); if (jfd == 0) { fprintf(stderr, "unable to open ``%s'' for output.\n", path); return -1; } fprintf(jfd, "\002This file created by Icarus Verilog/PAL\n"); fprintf(jfd, "*\n"); fprintf(jfd, "QF%u* Number of fuses*\n", pal_fuses(pal)); fprintf(jfd, "F0* Note: Default fuse set to 0*\n"); fprintf(jfd, "G0* Note: Security fuse NOT blown.*\n"); draw_macrocell_modes(jfd); fclose(jfd); return 0; } iverilog-10_1/tgt-pal/enables.c000066400000000000000000000036251265551621300164750ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "ivl_target.h" # include # include "priv.h" /* * Given a pin index, look at the nexus for a bufif device that is * driving it, if any. Save that device in the enable slot for the * cell. We'll try to fit it later. */ static void absorb_pad_enable(unsigned idx) { unsigned ndx; ivl_nexus_t nex = bind_pin[idx].nexus; for (ndx = 0 ; ndx < ivl_nexus_ptrs(nex) ; ndx += 1) { unsigned pin; ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, ndx); ivl_net_logic_t log = ivl_nexus_ptr_log(ptr); if (log == 0) continue; pin = ivl_nexus_ptr_pin(ptr); assert(pin == 0); switch (ivl_logic_type(log)) { case IVL_LO_BUFIF0: case IVL_LO_BUFIF1: assert(bind_pin[idx].enable == 0); bind_pin[idx].enable = log; break; default: } } } void absorb_pad_enables(void) { unsigned idx; for (idx = 0 ; idx < pins ; idx += 1) { if (bind_pin[idx].sop == 0) continue; if (bind_pin[idx].nexus == 0) continue; absorb_pad_enable(idx); } } iverilog-10_1/tgt-pal/fit_log.c000066400000000000000000000070311265551621300165020ustar00rootroot00000000000000/* * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "ivl_target.h" # include # include # include # include "priv.h" # include "ivl_alloc.h" /* * By the time we get here, all the flip-flops have been placed in * macrocells, and enables attached to them. So all that's left is to * work backwards from each macrocell, making terms and sum-of-terms * from the asynchronous logic until we get to the table inputs. * * A product term is a null terminated array of ivl_nexus_t * objects. An expression is a null terminated array of terms. */ static void dump_expr(term_t**expr, const char*title) { unsigned idx; fprintf(stderr, "expression for %s:\n", title); for (idx = 0 ; expr[idx] ; idx += 1) { term_t*tp; fprintf(stderr, " term %u:\n", idx); for (tp = expr[idx] ; tp->nex ; tp += 1) fprintf(stderr, " %c%s\n", tp->inv? '~' : ' ', ivl_nexus_name(tp->nex)); } } static term_t** build_expr(ivl_nexus_t nex) { term_t* term = calloc(2, sizeof(term_t)); term_t**expr = calloc(2, sizeof(term_t*)); unsigned idx; assert(nex); expr[0] = term; term[0].inv = 0; term[0].nex = nex; /* First look to see if I'm connected to an input pin. If so, then this expression is done. */ for (idx = 0 ; idx < pins ; idx += 1) { struct pal_bind_s*pin = bind_pin + idx; if ((nex == pin->nexus) && (pin->sop == 0)) { return expr; } } fprintf(stderr, "sorry, I give up on nexus %s\n", ivl_nexus_name(nex)); return 0; } int fit_logic(void) { unsigned idx; for (idx = 0 ; idx < pins ; idx += 1) { ivl_nexus_t cell; struct pal_bind_s*pin = bind_pin + idx; if (pin->sop == 0) continue; cell = pin->nexus; if (cell == 0) continue; /* If there is an enable, then this is a bufifX or a notifX. Build the expression for the enable, and guess that the input to the cell is actually the input to the enable. */ if (pin->enable) { ivl_nexus_t en_nex = ivl_logic_pin(pin->enable, 2); assert(cell == ivl_logic_pin(pin->enable, 0)); cell = ivl_logic_pin(pin->enable, 1); assert(cell); pin->enable_ex = build_expr(en_nex); dump_expr(pin->enable_ex, ivl_nexus_name(en_nex)); } /* If there is a reg, then the input to the cell is really the D input to the ff. */ if (pin->reg) { assert(cell == ivl_lpm_q(pin->reg, pin->reg_q)); cell = ivl_lpm_data(pin->reg, pin->reg_q); } assert(cell); /* Here we are. Generate the sum-of-products for the input. */ pin->sop_ex = build_expr(cell); dump_expr(pin->sop_ex, ivl_nexus_name(cell)); } return 0; } iverilog-10_1/tgt-pal/fit_reg.c000066400000000000000000000065331265551621300165040ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "ivl_target.h" # include # include # include "priv.h" /* * The fit_registers function scans all the scopes for flip-flop * devices to be assigned to macrocells. First look to see if the * device is connected to a PAD directly or through a bufif device. If * not, then just pick a free macrocell and drop it there. */ static int scan_ff_q(ivl_lpm_t ff, unsigned q); int fit_registers(ivl_scope_t scope, void*x) { int rc; unsigned idx; unsigned lpms; /* Scan child scopes first... */ rc = ivl_scope_children(scope, fit_registers, 0); if (rc != 0) return rc; /* Scan the current scope for flip-flop devices. Pass the devices we find to the scan_ff_q function to assign to a macrocell. */ lpms = ivl_scope_lpms(scope); for (idx = 0 ; idx < lpms ; idx += 1) { ivl_lpm_t lpm = ivl_scope_lpm(scope, idx); unsigned wid, q; if (ivl_lpm_type(lpm) != IVL_LPM_FF) continue; wid = ivl_lpm_width(lpm); for (q = 0 ; q < wid ; q += 1) { rc = scan_ff_q(lpm, q); if (rc != 0) return rc; } } return 0; } /* * This is the part that actually assigns the single bit of a single * flip-flop to a single macrocell. */ int scan_ff_q(ivl_lpm_t ff, unsigned q) { unsigned idx; ivl_nexus_t nex; nex = ivl_lpm_q(ff, q); /* First, look to see if the Q is already connected to a pin or an enable. If I find such a connection, then immediately finish. */ for (idx = 0 ; idx < pins ; idx += 1) { struct pal_bind_s*pin = bind_pin + idx; if (pin->sop == 0) continue; if (pin->enable) { if (nex == ivl_logic_pin(pin->enable, 1)) { pin->reg = ff; pin->reg_q = q; return 0; } } else if (pin->nexus == nex) { pin->reg = ff; pin->reg_q = q; return 0; } } /* There is no pin connection, so try setting this to an unbound sop cell. We know that a sop is unbound if there are no enables, nexus or ff devices connected to it. */ for (idx = 0 ; idx < pins ; idx += 1) { struct pal_bind_s*pin = bind_pin + idx; if (pin->sop == 0) continue; if (pin->enable || pin->nexus || pin->reg) continue; /* Found one. Put the reg here. Leave the nexus empty so that the code generator knows to disable the pin. */ pin->reg = ff; pin->reg_q = q; return 0; } fprintf(stderr, "No room for this ff.\n"); error_count += 1; return 0; } iverilog-10_1/tgt-pal/imain.c000066400000000000000000000075121265551621300161600ustar00rootroot00000000000000/* * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * This module generates a PAL that implements the design. */ # include "priv.h" # include # include # include # include "ivl_alloc.h" extern void dump_final_design(FILE*out); /* * As processing proceeds, this variable is incremented as errors are * encountered. This allows the code generator to give up if it * detects errors deep within recursive functions. */ unsigned pal_errors = 0; /* * This is the pal device that the user asked for. */ pal_t pal = 0; /* * These variables are the global pin assignment array. Everything * operates to build this up. */ unsigned pins = 0; struct pal_bind_s* bind_pin = 0; /* * This is the main entry point that Icarus Verilog calls to generate * code for a pal. */ int target_design(ivl_design_t des) { unsigned idx; const char*part; ivl_scope_t root; /* Get the part type from the design, using the "part" key. Given the part type, try to open the pal description so that we can figure out the device. */ part = ivl_design_flag(des, "part"); if ((part == 0) || (*part == 0)) { fprintf(stderr, "error: part must be specified. Specify a\n"); fprintf(stderr, " : type with the -fpart= option.\n"); return -1; } pal = pal_alloc(part); if (pal == 0) { fprintf(stderr, "error: %s is not a valid part type.\n", part); return -1; } assert(pal); pins = pal_pins(pal); assert(pins > 0); /* Allocate the pin array, ready to start assigning resources. */ bind_pin = calloc(pins, sizeof (struct pal_bind_s)); /* Connect all the macrocells that drive pins to the pin that they drive. This doesn't yet look at the design, but is initializing the bind_pin array with part information. */ for (idx = 0 ; idx < pal_sops(pal) ; idx += 1) { pal_sop_t sop = pal_sop(pal, idx); int spin = pal_sop_pin(sop); if (spin == 0) continue; assert(spin > 0); bind_pin[spin-1].sop = sop; } /* Get pin assignments from the user. This is the first and most constrained step. Everything else must work around the results of these bindings. */ root = ivl_design_root(des); get_pad_bindings(root, 0); if (pal_errors) { fprintf(stderr, "PAD assignment failed.\n"); pal_free(pal); return -1; } /* Run through the assigned output pins and absorb the output enables that are connected to them. */ absorb_pad_enables(); /* Scan all the registers, and assign them to macro-cells. */ root = ivl_design_root(des); fit_registers(root, 0); if (pal_errors) { fprintf(stderr, "Register fitting failed.\n"); pal_free(pal); return -1; } fit_logic(); if (pal_errors) { fprintf(stderr, "Logic fitting failed.\n"); pal_free(pal); return -1; } dump_final_design(stdout); emit_jedec(ivl_design_flag(des, "-o")); pal_free(pal); return 0; } iverilog-10_1/tgt-pal/pads.c000066400000000000000000000040571265551621300160130ustar00rootroot00000000000000/* * Copyright (c) 2000 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include # include # include /* * This function scans the netlist for all the pin assignments that * are fixed by a PAD attribute. Search the scopes recursively, * looking for signals that may have PAD attributes. */ int get_pad_bindings(ivl_scope_t net, void*x) { unsigned idx; int rc = ivl_scope_children(net, get_pad_bindings, 0); if (rc) return rc; for (idx = 0 ; idx < ivl_scope_sigs(net) ; idx += 1) { ivl_signal_t sig; const char*pad; int pin; sig = ivl_scope_sig(net, idx); pad = ivl_signal_attr(sig, "PAD"); if (pad == 0) continue; pin = strtol(pad+1, 0, 10); if ((pin == 0) || (pin > pins)) { printf("%s: Invalid PAD assignment: %s\n", ivl_signal_name(sig), pad); error_count += 1; continue; } assert(ivl_signal_pins(sig) == 1); if (bind_pin[pin-1].nexus) { if (bind_pin[pin-1].nexus != ivl_signal_pin(sig, 0)) { printf("%s: Unconnected signals share pin %d\n", ivl_signal_name(sig), pin); error_count += 1; } continue; } bind_pin[pin-1].nexus = ivl_signal_pin(sig, 0); } return 0; } iverilog-10_1/tgt-pal/priv.h000066400000000000000000000057751265551621300160610ustar00rootroot00000000000000#ifndef IVL_priv_H #define IVL_priv_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include extern pal_t pal; extern unsigned error_count; /* * A device has an array of pins, that are bound to the netlist either * by attribute or by random lookup. The bind_pin table keeps track of * pin allocations. * * Each cell also has attached to it an expression that calculates * results from an input. That expression is represented by a sum of * product terms. A product term is an array of term_t objects, * terminated by a term will a null nex pointers. A sum, then, is an * array of pointers to term_t arrays, terminated by a null pointer. */ typedef struct term_s { int inv; ivl_nexus_t nex; } term_t; /* * This structure describes a target device pin. If the pin is not * controlled by the pal (i.e. it is a power pin) then the sop field * is null. Otherwise, the sop in the macrocell that controls the pin. * * If the pin has an enable, then the sop for the enable function is * stored here as well. * * This structure for collecting the PAL design assumes that all the * macrocells are associated with pins, or are enables for other * pins. * * The bind_pin array is the complete description of the target as it * is accumulated. */ struct pal_bind_s { /* This is the netlist connection for the pin. */ ivl_nexus_t nexus; /* If the pin is an output, this is is sop that drives it. */ pal_sop_t sop; /* If the output has an enable, this is it, along with the single term that activates it. */ ivl_net_logic_t enable; term_t **enable_ex; /* If there is a register here, this is it. */ ivl_lpm_t reg; unsigned reg_q; /* The input to the cell is this expression. */ term_t **sop_ex; /* These are the SOP flags that I believe I need. */ unsigned sop_inv : 1; }; extern unsigned pins; extern struct pal_bind_s* bind_pin; /* * These are various steps in the fitting process. */ extern int get_pad_bindings(ivl_scope_t net, void*x); extern void absorb_pad_enables(void); extern int fit_registers(ivl_scope_t scope, void*x); extern int fit_logic(void); extern int emit_jedec(const char*path); #endif /* IVL_priv_H */ iverilog-10_1/tgt-pcb/000077500000000000000000000000001265551621300147025ustar00rootroot00000000000000iverilog-10_1/tgt-pcb/Makefile.in000066400000000000000000000067101265551621300167530ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ CC = @CC@ CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ YACC = @YACC@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ O = pcb.o scope.o show_netlist.o show_pcb.o footprint.o fp.o fp_lex.o all: dep pcb.tgt check: all clean: rm -f fp.cc fp.h fp.output fp_lex.cc rm -rf *.o dep pcb.tgt distclean: clean rm -f Makefile config.log rm -f stamp-pcb_config-h pcb_config.h cppcheck: $(O:.o=.cc) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-pcb/$@ dep: mkdir dep stamp-pcb_config-h: $(srcdir)/pcb_config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=tgt-pcb/pcb_config.h pcb_config.h: stamp-pcb_config-h %.o: %.cc $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep fp_lex.o: fp_lex.cc fp.h fp_lex.cc: $(srcdir)/fp.lex $(LEX) -s -ofp_lex.cc $(srcdir)/fp.lex fp.cc: $(srcdir)/fp.y $(YACC) --verbose -t -p fp -d -o $@ $< fp.h: fp.cc mv fp.cc.h $@ 2>/dev/null || mv fp.hh $@ ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif pcb.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl$(suffix)/pcb.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/pcb.conf $(libdir)/ivl$(suffix)/pcb-s.conf $(libdir)/ivl$(suffix)/pcb.tgt: ./pcb.tgt $(INSTALL_PROGRAM) ./pcb.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.tgt" $(libdir)/ivl$(suffix)/pcb.conf: $(srcdir)/pcb.conf $(INSTALL_DATA) $(srcdir)/pcb.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.conf" $(libdir)/ivl$(suffix)/pcb-s.conf: $(srcdir)/pcb-s.conf $(INSTALL_DATA) $(srcdir)/pcb-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.tgt" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-pcb/cppcheck.sup000066400000000000000000000002711265551621300172130ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:pcb.cc:52 // target_query() unusedFunction:pcb.cc:84 iverilog-10_1/tgt-pcb/footprint.cc000066400000000000000000000052251265551621300172410ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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 */ # include "version_base.h" # include "version_tag.h" # include "pcb_config.h" # include "pcb_priv.h" # include "fp_api.h" # include # include using namespace std; map footprints; static int check_footprint(element_data_t*elem); /* * Scan the element list and collect footprints needed. */ int load_footprints(void) { for (map::const_iterator cur = element_list.begin() ; cur != element_list.end() ; ++ cur) { check_footprint(cur->second); } return 0; } /* * The fpparse function calls back the callback_fp_element function * for each Element that it parses. The check_footprint function * stores in the cur_footprint variable the name of the footprint that * we are trying to find in the file. The callback uses that name to * store the Element into the footprints map. */ static string cur_footprint = ""; void callback_fp_element(const struct fp_element_t&cur_elem) { assert(cur_footprint != ""); footprints[cur_footprint] = cur_elem; cur_footprint = ""; } static int check_footprint(element_data_t*elem) { if (elem->footprint == "") { cerr << "No footprint defined for \"" << elem->description << "\"." << endl; return -1; } map::iterator match = footprints.find(elem->footprint); if (match != footprints.end()) return 0; string fpname = elem->footprint + ".fp"; cur_footprint = elem->footprint; int rc = parse_fp_file(fpname); if (rc != 0) { cerr << "parse_fp_file(" << fpname << ") returns rc=" << rc << endl; return rc; } match = footprints.find(elem->footprint); if (match == footprints.end()) { cerr << "Unable to locate footprint " << elem->footprint << "." << endl; return -2; } return 0; } iverilog-10_1/tgt-pcb/fp.lex000066400000000000000000000037521265551621300160300ustar00rootroot00000000000000%option prefix="fp" %option never-interactive %option noinput %option nounput %option noyywrap %{ /* * Copyright (C) 2011-2013 Stephen Williams (steve@icarus.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include "fp_api.h" # include "fp.h" # define YY_DECL int yylex(YYSTYPE*yylvalp, YYLTYPE*yyllocp) %} SPACE [ \t\f\r] %% /* Skip comment lines */ "#".* { ; } /* Skip white space */ {SPACE} { ; } "\n" { yyllocp->first_line += 1; } "Element" { return K_ELEMENT; } "Pad" { return K_PAD; } "0x"[0-9a-fA-F]+ { yylvalp->integer = strtoul(yytext+2,0,10); return INTEGER; } "0"[0-7]* { yylvalp->integer = strtoul(yytext,0,8); return INTEGER; } [1-9][0-9]* { yylvalp->integer = strtoul(yytext,0,10); return INTEGER; } "\""[^\"]*"\"" { size_t len = strlen(yytext)-2; char*tmp = new char[len+1]; memcpy(tmp, yytext+1, len); tmp[len] = 0; yylvalp->text = tmp; return STRING; } /* Isolated characters are tokens */ . { return yytext[0]; } %% void init_fp_lexor(FILE*fd) { yyrestart(fd); } void destroy_fp_lexor() { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif } iverilog-10_1/tgt-pcb/fp.y000066400000000000000000000112471265551621300155060ustar00rootroot00000000000000 %pure-parser %parse-param {const char*file_path} %{ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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 */ # include "fp_api.h" # include "pcb_priv.h" # include # include using namespace std; /* Recent version of bison expect that the user supply a YYLLOC_DEFAULT macro that makes up a yylloc value from existing values. I need to supply an explicit version to account for the text field, that otherwise won't be copied. */ # define YYLLOC_DEFAULT(Current, Rhs, N) do { \ (Current).first_line = (Rhs)[1].first_line; \ } while (0) static void yyerror(YYLTYPE*, const char*, const char*msg) { fprintf(stderr, "%s\n", msg); } extern void init_fp_lexor(FILE*fd); extern void destroy_fp_lexor(); extern int fplex(union YYSTYPE*yylvalp, YYLTYPE*yylloc); static fp_pad_t cur_pad; static fp_element_t cur_element; %} %union { long integer; char*text; }; %token STRING %token INTEGER %token K_ELEMENT %token K_PAD %type integer %% file_items : file_items file_item | file_item ; file_item : element ; element : K_ELEMENT element_header '(' element_items ')' { callback_fp_element(cur_element); } ; element_header : '[' INTEGER STRING STRING STRING integer integer integer integer integer integer STRING ']' { cur_element.nflags = $2; cur_element.description = $3; delete[]$3; cur_element.name = $4; delete[]$4; cur_element.value = $5; delete[]$5; cur_element.mx = $6; cur_element.my = $7; cur_element.tx = $8; cur_element.ty = $9; cur_element.tdir = $10; cur_element.tscale = $11; cur_element.tsflags = $12; delete[]$12; cur_element.pads.clear(); } | '[' error ']' { errormsg(@2, "Error in element header\n"); yyerrok; cur_element.nflags = 0; cur_element.description = ""; cur_element.name = ""; cur_element.value = ""; cur_element.mx = 0; cur_element.my = 0; cur_element.tx = 0; cur_element.ty = 0; cur_element.tdir = 0; cur_element.tscale = 0; cur_element.tsflags = ""; cur_element.pads.clear(); } ; element_items : element_items element_item | element_item ; element_item : pad { cur_element.pads[cur_pad.name] = cur_pad; } ; integer : INTEGER { $$ = $1; } | '-' INTEGER { $$ = -$2; } ; pad : K_PAD '[' integer integer integer integer integer integer integer STRING STRING STRING ']' { cur_pad.rx1 = $3; cur_pad.ry1 = $4; cur_pad.rx2 = $5; cur_pad.ry2 = $6; cur_pad.thickness = $7; cur_pad.clearance = $8; cur_pad.mask = $9; cur_pad.name = $10; delete[]$10; cur_pad.number = $11; delete[]$11; cur_pad.sflags = $12; delete[]$12; } | K_PAD '[' error ']' { errormsg(@3, "Error in pad header\n"); yyerrok; cur_pad.rx1 = 0; cur_pad.ry1 = 0; cur_pad.rx2 = 0; cur_pad.ry2 = 0; cur_pad.thickness = 0; cur_pad.clearance = 0; cur_pad.mask = 0; cur_pad.name = ""; cur_pad.number = ""; cur_pad.sflags = ""; } ; %% static string parse_file_path; int parse_fp_errors = 0; int parse_fp_sorrys = 0; void errormsg(const YYLTYPE&loc, const char*fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%u: error: ", parse_file_path.c_str(), loc.first_line); vfprintf(stderr, fmt, ap); va_end(ap); parse_fp_errors += 1; } void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%u: sorry: ", parse_file_path.c_str(), loc.first_line); vfprintf(stderr, fmt, ap); va_end(ap); parse_fp_sorrys += 1; } int parse_fp_file(const string&file_path) { FILE*fd = fopen(file_path.c_str(), "r"); if (fd == 0) { perror(file_path.c_str()); return -1; } parse_file_path = file_path; parse_fp_errors = 0; parse_fp_sorrys = 0; init_fp_lexor(fd); int rc = yyparse(file_path.c_str()); fclose(fd); destroy_fp_lexor(); return rc; } iverilog-10_1/tgt-pcb/fp_api.h000066400000000000000000000044021265551621300163110ustar00rootroot00000000000000#ifndef IVL_fp_api_H #define IVL_fp_api_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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 */ # include /* * This is the interface function that the user invokes to parse a * footprint file. The argument is the path to the element. */ extern int parse_fp_file(const std::string&file_path); /* * The yyltype supports the passing of detailed source file location * information between the lexical analyzer and the parser. Defining * YYLTYPE compels the lexor to use this type and not something other. */ struct yyltype { unsigned first_line; yyltype() { first_line = 1; } }; # define YYLTYPE struct yyltype /* * Use this function during parse to generate error messages. The "loc" * is the location of the token that triggered the error, and the fmt * is printf-style format. */ extern void errormsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); extern void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); extern void callback_fp_element(const struct fp_element_t&); /* * Set this to a non-zero value to enable parser debug output. */ //extern int yydebug; /* * The parser counts the errors that is handed in the parse_errors * variable. For a clean compile, this value should not change. (The * caller sets its initial value.) The sorrys are the count of * unsupported constructs that are encountered. */ //extern int parse_errors; extern int parse_fp_sorrys; #endif /* IVL_fp_api_H */ iverilog-10_1/tgt-pcb/pcb-s.conf000066400000000000000000000001371265551621300165560ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=pcb.tgt iverilog-10_1/tgt-pcb/pcb.cc000066400000000000000000000053571265551621300157670ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This is a PCB target module. */ # include "version_base.h" # include "version_tag.h" # include "pcb_config.h" # include # include # include # include "pcb_priv.h" # include "ivl_target.h" static const char*version_string = "Icarus Verilog PCB Netlist Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (c) 2011 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; int target_design(ivl_design_t des) { ivl_scope_t*root_scopes; unsigned nroot; unsigned idx; int rc = 0; const char*pcb_path = ivl_design_flag(des, "-o"); ivl_design_roots(des, &root_scopes, &nroot); for (idx = 0 ; idx < nroot ; idx += 1) { int tmp = scan_scope(root_scopes[idx]); if (tmp != 0) { rc = tmp; break; } } load_footprints(); assert(pcb_path); show_pcb(pcb_path); const char*net_path = ivl_design_flag(des, "netlist"); if (net_path != 0) { printf("Send netlist to %s\n", net_path); show_netlist(net_path); } return rc; } const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } iverilog-10_1/tgt-pcb/pcb.conf000066400000000000000000000000601265551621300163110ustar00rootroot00000000000000functor:cprop functor:nodangle flag:DLL=pcb.tgt iverilog-10_1/tgt-pcb/pcb_config.h.in000066400000000000000000000020151265551621300175470ustar00rootroot00000000000000#ifndef IVL_pcb_config_H #define IVL_pcb_config_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # undef HAVE_STDINT_H # undef HAVE_INTTYPES_H # undef _LARGEFILE_SOURCE # undef _LARGEFILE64_SOURCE #endif /* IVL_pcb_config_H */ iverilog-10_1/tgt-pcb/pcb_priv.h000066400000000000000000000045501265551621300166630ustar00rootroot00000000000000#ifndef IVL_pcb_priv_H #define IVL_pcb_priv_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "ivl_target.h" extern int scan_scope(ivl_scope_t scope); /* * This nexus_list is a list of all the nets that the scan_scope * collects, wrapped up into a list. The show_netlist dumps that list * as a netlist. */ struct nexus_data { std::string name; std::set pins; }; extern std::list nexus_list; /* * The element_list is a collection of all the elements that were * located by the scope scan. The key is the refdes for the element, * and the value is the element_data_t structure that describes that * element. */ struct element_data_t { std::string description; std::string value; std::string footprint; }; extern std::map element_list; extern int load_footprints(void); struct fp_pad_t { long rx1, ry1; long rx2, ry2; int thickness; int clearance; int mask; std::string name; std::string number; std::string sflags; }; struct fp_element_t { long nflags; std::string description; std::string name; std::string value; long mx, my; long tx, ty; int tdir; int tscale; std::string tsflags; std::map pads; }; extern std::map footprints; extern void show_netlist(const char*net_path); extern void show_pcb(const char*pcb_path); #endif /* IVL_pcb_priv_H */ iverilog-10_1/tgt-pcb/scope.cc000066400000000000000000000152151265551621300163260ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pcb_priv.h" # include # include # include # include using namespace std; list nexus_list; map element_list; struct attr_value { ivl_attribute_type_t type; string str; long num; }; static void sheet_box(ivl_scope_t scope, const map&attrs); static void black_box(ivl_scope_t scope, const map&attrs); static string wire_name(ivl_signal_t sig); int scan_scope(ivl_scope_t scope) { int black_box_flag = 0; map attrs; // Scan the attributes, looking in particular for the // black_box attribute. While we are at it, save the collected // attributes into a map that we can pass on to the processing // functions. for (unsigned idx = 0 ; idx < ivl_scope_attr_cnt(scope) ; idx += 1) { ivl_attribute_t attr = ivl_scope_attr_val(scope, idx); string attr_key = attr->key; if (attr_key == "ivl_black_box") { // Ah hah, this is a black box. black_box_flag = 1; } else { struct attr_value val; val.type = attr->type; switch (val.type) { case IVL_ATT_VOID: break; case IVL_ATT_STR: val.str = attr->val.str; break; case IVL_ATT_NUM: val.num = attr->val.num; break; } attrs[attr_key] = val; } } // If this scope is a black box, then process it // so. Otherwise, process it as a sheet, which will recurse. if (black_box_flag) { black_box(scope, attrs); } else { sheet_box(scope, attrs); } return 0; } extern "C" int child_scan_fun(ivl_scope_t scope, void*) { scan_scope(scope); return 0; } void sheet_box(ivl_scope_t scope, const map&) { printf("Sheet %s...\n", ivl_scope_name(scope)); unsigned sigs = ivl_scope_sigs(scope); for (unsigned idx = 0 ; idx < sigs ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(scope, idx); printf(" Wire %s\n", ivl_signal_basename(sig)); assert(ivl_signal_array_count(sig) == 1); ivl_nexus_t nex = ivl_signal_nex(sig, 0); struct nexus_data*nex_data = reinterpret_cast (ivl_nexus_get_private(nex)); if (nex_data == 0) { nex_data = new nexus_data; nex_data->name = wire_name(sig); nexus_list.push_back(nex_data); ivl_nexus_set_private(nex, nex_data); } } ivl_scope_children(scope, child_scan_fun, 0); } /* * A black box is a component. Do not process the contents, other than * to get at the ports that we'll attach to the netlist. */ static void black_box(ivl_scope_t scope, const map&attrs) { assert(ivl_scope_type(scope) == IVL_SCT_MODULE); printf(" Component %s is %s\n", ivl_scope_name(scope), ivl_scope_tname(scope)); // The refdes for the object is by default the name of // the instance. If the user attaches a refdes // attribute, then use that instead. string refdes = ivl_scope_basename(scope); map::const_iterator aptr = attrs.find("refdes"); if (aptr != attrs.end()) { assert(aptr->second.type == IVL_ATT_STR); refdes = aptr->second.str; } element_data_t*elem_data = new element_data_t; // Scan the parameters of the module for any values that may // be of interest to the PCB element. unsigned params = ivl_scope_params(scope); for (unsigned idx = 0 ; idx < params ; idx += 1) { ivl_parameter_t par = ivl_scope_param(scope, idx); string name = ivl_parameter_basename(par); if (name == "description") { ivl_expr_t exp = ivl_parameter_expr(par); switch (ivl_expr_type(exp)) { case IVL_EX_STRING: elem_data->description = ivl_expr_string(exp); break; default: assert(0); } } else if (name == "value") { ivl_expr_t exp = ivl_parameter_expr(par); switch (ivl_expr_type(exp)) { case IVL_EX_STRING: elem_data->value = ivl_expr_string(exp); break; default: assert(0); } } else if (name == "footprint") { ivl_expr_t exp = ivl_parameter_expr(par); switch (ivl_expr_type(exp)) { case IVL_EX_STRING: elem_data->footprint = ivl_expr_string(exp); break; default: assert(0); } } } // If there is a "description" attribute for the device, then // use that in place of the parameter. if ( (aptr = attrs.find("description")) != attrs.end() ) { assert(aptr->second.type == IVL_ATT_STR); elem_data->description = aptr->second.str; } // Get the "value" attribute for the device. if ( (aptr = attrs.find("value")) != attrs.end() ) { switch (aptr->second.type) { case IVL_ATT_VOID: break; case IVL_ATT_STR: elem_data->value = aptr->second.str; break; case IVL_ATT_NUM: assert(0); break; } } // Look for the ports of the black box and make sure they are // attached to signals. Attach the port as a pin wired to a net. unsigned sigs = ivl_scope_sigs(scope); for (unsigned idx = 0 ; idx < sigs ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(scope, idx); ivl_signal_port_t sip = ivl_signal_port(sig); // Skip signals that are not ports. if (sip == IVL_SIP_NONE) continue; assert(ivl_signal_array_count(sig) == 1); ivl_nexus_t nex = ivl_signal_nex(sig, 0); struct nexus_data*nex_data = reinterpret_cast(ivl_nexus_get_private(nex)); assert(nex_data); string pindes = ivl_signal_basename(sig); string pin = refdes + "-" + pindes; nex_data->pins.insert(pin); printf(" port %s\n", ivl_signal_basename(sig)); } element_data_t*&eptr = element_list[refdes]; assert(eptr == 0); eptr = elem_data; } static string wire_name(ivl_signal_t sig) { string res = ivl_signal_basename(sig); return res; } iverilog-10_1/tgt-pcb/show_netlist.cc000066400000000000000000000027361265551621300177430ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pcb_priv.h" # include # include using namespace std; void show_netlist(const char*net_path) { assert(net_path); FILE*fnet = fopen(net_path, "w"); if (fnet == 0) { perror(net_path); return; } for (list::iterator cur = nexus_list.begin() ; cur != nexus_list.end() ; ++ cur) { nexus_data*curp = *cur; fprintf(fnet, "%s", curp->name.c_str()); for (set::const_iterator cp = curp->pins.begin() ; cp != curp->pins.end() ; ++ cp) { fprintf(fnet, " %s", cp->c_str()); } fprintf(fnet, "\n"); } fclose(fnet); } iverilog-10_1/tgt-pcb/show_pcb.cc000066400000000000000000000062261265551621300170230ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "pcb_config.h" # include "pcb_priv.h" # include # include using namespace std; static void show_pcb_header(FILE*fpcb) { fprintf(fpcb, "PCB[\"\" 400000 220000]\n"); fprintf(fpcb, "Grid[100.0 0 0 1]\n"); } static void show_pcb_element(FILE*fpcb, const string&refdes, element_data_t*elem); void show_pcb(const char*pcb_path) { assert(pcb_path); FILE*fpcb = fopen(pcb_path, "w"); if (fpcb == 0) { perror(pcb_path); return; } show_pcb_header(fpcb); // Draw the collected elements for (map::const_iterator cur = element_list.begin() ; cur != element_list.end() ; ++ cur) { show_pcb_element(fpcb, cur->first, cur->second); } fclose(fpcb); } static void show_pcb_element(FILE*fpcb, const string&refdes, element_data_t*elem) { string descr = elem->description; const string&value = elem->value; if (elem->footprint == "") { fprintf(fpcb, "Element[\"\" \"%s\" \"%s\" \"%s\"", descr.c_str(), refdes.c_str(), value.c_str()); // Mark-X Mark-Y fprintf(fpcb, " 0 0"); // Text-X Text-Y text-direction Text-scale Text-flags fprintf(fpcb, " 0 0 0 100 \"\""); fprintf(fpcb, "]\n"); // Fill in the contents of the element. Should get this // from a library. fprintf(fpcb, "(\n"); fprintf(fpcb, ")\n"); return; } fp_element_t&foot = footprints[elem->footprint]; if (descr == "") descr = foot.description; fprintf(fpcb, "Element[0x%lx \"%s\" \"%s\" \"%s\"", foot.nflags, descr.c_str(), refdes.c_str(), value.c_str()); fprintf(fpcb, " %ld %ld", foot.mx, foot.my); fprintf(fpcb, " %ld %ld %d %d \"%s\"", foot.tx, foot.ty, foot.tdir, foot.tscale, foot.tsflags.c_str()); fprintf(fpcb, "]\n(\n"); for (map::const_iterator cur = foot.pads.begin() ; cur != foot.pads.end() ; ++ cur) { fprintf(fpcb, "Pad[%ld %ld %ld %ld %d %d %d \"%s\" \"%s\" \"%s\"]\n", cur->second.rx1, cur->second.ry1, cur->second.rx2, cur->second.ry2, cur->second.thickness, cur->second.clearance, cur->second.mask, cur->second.name.c_str(), cur->second.number.c_str(), cur->second.sflags.c_str()); } fprintf(fpcb, ")\n"); } iverilog-10_1/tgt-sizer/000077500000000000000000000000001265551621300152725ustar00rootroot00000000000000iverilog-10_1/tgt-sizer/Makefile.in000066400000000000000000000055041265551621300173430ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ O = sizer.o scan_lpms.o scan_logs.o all: dep sizer.tgt check: all clean: rm -rf *.o dep sizer.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.cc) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-sizer/$@ dep: mkdir dep %.o: %.cc $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif sizer.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl$(suffix)/sizer.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/sizer.conf $(libdir)/ivl$(suffix)/sizer-s.conf $(libdir)/ivl$(suffix)/sizer.tgt: ./sizer.tgt $(INSTALL_PROGRAM) ./sizer.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.tgt" $(libdir)/ivl$(suffix)/sizer.conf: $(srcdir)/sizer.conf $(INSTALL_DATA) $(srcdir)/sizer.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.conf" $(libdir)/ivl$(suffix)/sizer-s.conf: $(srcdir)/sizer-s.conf $(INSTALL_DATA) $(srcdir)/sizer-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.tgt" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-sizer/cppcheck.sup000066400000000000000000000002751265551621300176070ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:sizer.cc:76 // target_query() unusedFunction:sizer.cc:65 iverilog-10_1/tgt-sizer/scan_logs.cc000066400000000000000000000032141265551621300175510ustar00rootroot00000000000000/* * Copyright (c) 2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sizer_priv.h" void scan_logs_gates(ivl_scope_t, ivl_net_logic_t log, struct sizer_statistics&stats) { unsigned wid = ivl_logic_width(log); stats.gate_count += wid; } void scan_logs(ivl_scope_t scope, struct sizer_statistics&stats) { for (unsigned idx = 0 ; idx < ivl_scope_logs(scope) ; idx += 1) { ivl_net_logic_t log = ivl_scope_log(scope, idx); switch (ivl_logic_type(log)) { // These logic gate types don't really exist in a // mapped design. case IVL_LO_BUFZ: break; case IVL_LO_AND: case IVL_LO_OR: case IVL_LO_XOR: case IVL_LO_NAND: case IVL_LO_NOR: case IVL_LO_XNOR: case IVL_LO_BUF: case IVL_LO_NOT: scan_logs_gates(scope, log, stats); break; default: stats.log_bytype[ivl_logic_type(log)] += 1; break; } } } iverilog-10_1/tgt-sizer/scan_lpms.cc000066400000000000000000000110121265551621300175530ustar00rootroot00000000000000/* * Copyright (c) 2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sizer_priv.h" using namespace std; /* * Count each bit of flip-flops. It is clear and obvious how these * come out, so no need to make alternate counts as well. */ static void scan_lpms_ff(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { ivl_nexus_t out = ivl_lpm_q(lpm); unsigned wid = get_nexus_width(out); stats.flop_count += wid; } /* * Count adders as 2m gates. * Also keep a count of adders by width, just out of curiosity. */ static void scan_lpms_add(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { unsigned wid = ivl_lpm_width(lpm); stats.adder_count[wid] += 1; stats.gate_count += 2*wid; } /* * Count equality comparator as 2m gates. * Also keep a count of comparators by width, just out of curiosity. */ static void scan_lpms_equality(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { unsigned wid = ivl_lpm_width(lpm); stats.equality_count[wid] += 1; stats.gate_count += 2*wid; } static void scan_lpms_equality_wild(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { unsigned wid = ivl_lpm_width(lpm); stats.equality_wc_count[wid] += 1; stats.gate_count += 2*wid; } /* * Count magnitude comparators as 2m gates. * Also keep a count of comparators by width, just out of curiosity. */ static void scan_lpms_magnitude(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { unsigned wid = ivl_lpm_width(lpm); stats.magnitude_count[wid] += 1; stats.gate_count += 2*wid; } /* * Count mux devices as 2m gates. * Also count the mux slices of various select sizes. */ static void scan_lpms_mux(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { // For now, don't generate statistics for wide mux devices. if (ivl_lpm_size(lpm) > 2) { stats.lpm_bytype[ivl_lpm_type(lpm)] += 1; return; } // The "width" of a mux is the number of 1-bit slices. unsigned wid = ivl_lpm_width(lpm); // Count the slices of the various width of muxes. stats.mux_count[2] += wid; stats.gate_count += 2*wid; } /* * Count reduction gates (wide input gates) as 1m gates. */ static void scan_lpms_reduction(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { unsigned wid = ivl_lpm_width(lpm); stats.gate_count += wid; } void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats) { for (unsigned idx = 0 ; idx < ivl_scope_lpms(scope) ; idx += 1) { ivl_lpm_t lpm = ivl_scope_lpm(scope,idx); switch (ivl_lpm_type(lpm)) { // Part select nodes don't actually take up // hardware. These represent things like bundle // manipulations, which are done in routing. case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_REPEAT: case IVL_LPM_SUBSTITUTE: break; case IVL_LPM_ADD: scan_lpms_add(scope, lpm, stats); break; case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_NEE: scan_lpms_equality(scope, lpm, stats); break; case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: scan_lpms_equality_wild(scope, lpm, stats); break; case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: scan_lpms_magnitude(scope, lpm, stats); break; // D-Type flip-flops. case IVL_LPM_FF: scan_lpms_ff(scope, lpm, stats); break; case IVL_LPM_MUX: scan_lpms_mux(scope, lpm, stats); break; case IVL_LPM_RE_AND: case IVL_LPM_RE_NAND: case IVL_LPM_RE_OR: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_XNOR: scan_lpms_reduction(scope, lpm, stats); break; default: stats.lpm_bytype[ivl_lpm_type(lpm)] += 1; break; } } } iverilog-10_1/tgt-sizer/sizer-s.conf000066400000000000000000000001411265551621300175310ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=sizer.tgt iverilog-10_1/tgt-sizer/sizer.cc000066400000000000000000000204431265551621300167400ustar00rootroot00000000000000/* * Copyright (c) 2014,2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "version_base.h" # include "version_tag.h" # include "config.h" # include "sizer_priv.h" # include # include # include using namespace std; /* * This is a null target module. It does nothing. */ static const char*version_string = "Icarus Verilog SIZER Statistics Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (c) 2014,2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; int sizer_errors = 0; FILE*sizer_out = 0; static int process_scan_fun(ivl_process_t net, void*raw); static void emit_sizer_scope(ivl_design_t des, ivl_scope_t model, struct sizer_statistics&stats); static void show_stats(struct sizer_statistics&stats); /* * This is called by the ivl core to get version information from the * loadable code generator. */ const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } /* * This is the main entry point from the IVL core. */ int target_design(ivl_design_t des) { const char*sizer_path = ivl_design_flag(des, "-o"); sizer_out = fopen(sizer_path, "wt"); assert(sizer_out); // Detect processes and dispatch them. ivl_design_process(des, &process_scan_fun, 0); // Locate the root scopes for the design. ivl_scope_t*roots; unsigned nroots; ivl_design_roots(des, &roots, &nroots); // Process all the root scopes. It is possible that there are // multiple root scopes, we will give isolated numbers for // each and keep then separate. for (unsigned idx = 0 ; idx < nroots ; idx += 1) { if (ivl_scope_type(roots[idx]) != IVL_SCT_MODULE) { fprintf(stderr, "SIZER: The root scope %s must be a module.\n", ivl_scope_basename(roots[idx])); sizer_errors += 1; continue; } struct sizer_statistics stats; emit_sizer_scope(des, roots[idx], stats); fprintf(sizer_out, "**** TOTALS\n"); show_stats(stats); } return sizer_errors; } /* * Processes are not collected into scopes, but we should not have any * left anyhow. Give error messages for all the processes that we find * to be remaining. */ static int process_scan_fun(ivl_process_t net, void* /*raw*/) { for (unsigned idx = 0 ; idx < ivl_process_attr_cnt(net) ; idx += 1) { ivl_attribute_t att = ivl_process_attr_val(net, idx); // If synthesis is explicitly turned off for this // process, then we just ignore it. if (strcmp(att->key, "ivl_synthesis_off") == 0) return 0; } fprintf(stderr, "%s:%u: SIZER: Processes not synthesized for statistics.\n", ivl_process_file(net), ivl_process_lineno(net)); sizer_errors += 1; return 0; } static void emit_sizer_scope(ivl_design_t des, ivl_scope_t scope, struct sizer_statistics&stats) { fprintf(sizer_out, "**** module/scope: %s\n", ivl_scope_name(scope)); scan_logs(scope, stats); scan_lpms(scope, stats); show_stats(stats); for (size_t idx = 0 ; idx < ivl_scope_childs(scope) ; idx += 1) { ivl_scope_t child = ivl_scope_child(scope,idx); struct sizer_statistics child_stats; emit_sizer_scope(des, child, child_stats); stats += child_stats; } } static void show_stats(struct sizer_statistics&stats) { fprintf(sizer_out, " Flip-Flops : %u\n", stats.flop_count); fprintf(sizer_out, " Logic Gates : %u\n", stats.gate_count); for (map::const_iterator cur = stats.adder_count.begin() ; cur != stats.adder_count.end() ; ++ cur) { fprintf(sizer_out, " ADDER[%u]: %u units\n", cur->first, cur->second); } for (map::const_iterator cur = stats.equality_count.begin() ; cur != stats.equality_count.end() ; ++ cur) { fprintf(sizer_out, " EQUALITY[%u]: %u units\n", cur->first, cur->second); } for (map::const_iterator cur = stats.equality_wc_count.begin() ; cur != stats.equality_wc_count.end() ; ++ cur) { fprintf(sizer_out, " EQUALITY_WC[%u]: %u units\n", cur->first, cur->second); } for (map::const_iterator cur = stats.magnitude_count.begin() ; cur != stats.magnitude_count.end() ; ++ cur) { fprintf(sizer_out, " MAGNITUDE[%u]: %u units\n", cur->first, cur->second); } for (map::const_iterator cur = stats.mux_count.begin() ; cur != stats.mux_count.end() ; ++ cur) { fprintf(sizer_out, " MUX[%u]: %u slices\n", cur->first, cur->second); } // These are diagnostic outputs for when more detail is needed. for (map::const_iterator cur = stats.lpm_bytype.begin() ; cur != stats.lpm_bytype.end() ; ++ cur) { fprintf(sizer_out, " LPM[%d]: %u unaccounted\n", cur->first, cur->second); } for (map::const_iterator cur = stats.log_bytype.begin() ; cur != stats.log_bytype.end() ; ++ cur) { fprintf(sizer_out, " LOG[%d]: %u unaccounted\n", cur->first, cur->second); } } unsigned get_nexus_width(ivl_nexus_t nex) { for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig) return ivl_signal_width(sig); } fprintf(stderr, "SIZER: Unable to find width of nexus?!\n"); sizer_errors += 1; return 0; } struct sizer_statistics& sizer_statistics::operator += (const sizer_statistics&that) { flop_count += that.flop_count; gate_count += that.gate_count; for (map::const_iterator cur = that.adder_count.begin() ; cur != that.adder_count.end() ; ++ cur) adder_count[cur->first] += cur->second; for (map::const_iterator cur = that.equality_count.begin() ; cur != that.equality_count.end() ; ++ cur) equality_count[cur->first] += cur->second; for (map::const_iterator cur = that.equality_wc_count.begin() ; cur != that.equality_wc_count.end() ; ++ cur) equality_wc_count[cur->first] += cur->second; for (map::const_iterator cur = that.magnitude_count.begin() ; cur != that.magnitude_count.end() ; ++ cur) magnitude_count[cur->first] += cur->second; for (map::const_iterator cur = that.mux_count.begin() ; cur != that.mux_count.end() ; ++ cur) mux_count[cur->first] += cur->second; for (map::const_iterator cur = that.lpm_bytype.begin() ; cur != that.lpm_bytype.end() ; ++ cur) lpm_bytype[cur->first] += cur->second; for (map::const_iterator cur = that.log_bytype.begin() ; cur != that.log_bytype.end() ; ++ cur) log_bytype[cur->first] += cur->second; return *this; } iverilog-10_1/tgt-sizer/sizer.conf000066400000000000000000000001411265551621300172710ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=sizer.tgt iverilog-10_1/tgt-sizer/sizer_priv.h000066400000000000000000000041651265551621300176450ustar00rootroot00000000000000#ifndef IVL_sizer_priv_H #define IVL_sizer_priv_H /* * Copyright (c) 2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "ivl_target.h" # include # include struct sizer_statistics { // These are the accumulated global statistics unsigned flop_count; unsigned gate_count; // Count adders of various dimension std::map adder_count; // count equality comparators std::map equality_count; // count equality (with wildcard) comparators std::map equality_wc_count; // Count magnitude comparators std::map magnitude_count; // Count mux's of various dimension std::map mux_count; // Different kinds of nodes that we have not accounted for std::map lpm_bytype; std::map log_bytype; inline sizer_statistics() { flop_count = 0; gate_count = 0; } struct sizer_statistics& operator += (const struct sizer_statistics&that); }; extern int sizer_errors; extern FILE*sizer_out; extern void scan_logs(ivl_scope_t scope, struct sizer_statistics&stats); extern void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats); extern unsigned get_nexus_width(ivl_nexus_t nex); #endif /* IVL_sizer_priv_H */ iverilog-10_1/tgt-stub/000077500000000000000000000000001265551621300151135ustar00rootroot00000000000000iverilog-10_1/tgt-stub/Makefile.in000066400000000000000000000054311265551621300171630ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = stub.o classes.o constant.o enumerate.o expression.o statement.o switches.o types.o all: dep stub.tgt check: all clean: rm -rf *.o dep stub.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-stub/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif stub.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl$(suffix)/stub.tgt \ $(libdir)/ivl$(suffix)/stub.conf $(libdir)/ivl$(suffix)/stub-s.conf $(libdir)/ivl$(suffix)/stub.tgt: ./stub.tgt $(INSTALL_PROGRAM) ./stub.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.tgt" $(libdir)/ivl$(suffix)/stub.conf: stub.conf $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.conf" $(libdir)/ivl$(suffix)/stub-s.conf: stub-s.conf $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/stub-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.tgt" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/stub-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-stub/classes.c000066400000000000000000000023041265551621300167130ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include void show_class(ivl_type_t net) { fprintf(out, " class %s\n", ivl_type_name(net)); for (int idx = 0 ; idx < ivl_type_properties(net) ; idx += 1) { fprintf(out, " "); show_net_type(ivl_type_prop_type(net,idx)); fprintf(out, " %s\n", ivl_type_prop_name(net,idx)); } } iverilog-10_1/tgt-stub/constant.c000066400000000000000000000030431265551621300171100ustar00rootroot00000000000000/* * Copyright (c) 2013-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include # include # include void show_constant(ivl_net_const_t net) { assert(net); fprintf(out, "constant "); switch (ivl_const_type(net)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: { const char*bits = ivl_const_bits(net); unsigned idx; assert(bits); for (idx = 0 ; idx < ivl_const_width(net) ; idx += 1) fprintf(out, "%c", bits[idx]); } break; case IVL_VT_REAL: fprintf(out, "%f", ivl_const_real(net)); break; default: fprintf(out, ""); break; } fprintf(out, " at %s:%u\n", ivl_const_file(net), ivl_const_lineno(net)); } iverilog-10_1/tgt-stub/cppcheck.sup000066400000000000000000000002751265551621300174300ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:stub.c:1774 // target_query() unusedFunction:stub.c:1844 iverilog-10_1/tgt-stub/enumerate.c000066400000000000000000000025621265551621300172510ustar00rootroot00000000000000/* * Copyright (c) 2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include void show_enumerate(ivl_enumtype_t net) { unsigned idx; fprintf(out, " enumeration %p {\n", net); for (idx = 0 ; idx < ivl_enum_names(net) ; idx += 1) { fprintf(out, " %s = <", ivl_enum_name(net, idx)); const char*bits = ivl_enum_bits(net, idx); size_t bits_len = strlen(bits); size_t bit; for (bit = bits_len ; bit > 0 ; bit -= 1) fputc(bits[bit-1], out); fprintf(out, ">\n"); } fprintf(out, " }\n"); } iverilog-10_1/tgt-stub/expression.c000066400000000000000000000424571265551621300174720ustar00rootroot00000000000000/* * Copyright (c) 2007-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include # include # include static const char*vt_type_string(ivl_expr_t net) { return data_type_string(ivl_expr_value(net)); } static void show_array_expression(ivl_expr_t net, unsigned ind) { ivl_signal_t sig = ivl_expr_signal(net); const char*name = ivl_signal_basename(sig); unsigned width = ivl_signal_width(sig); const char*vt = vt_type_string(net); fprintf(out, "%*sArray: %s, word_count=%u (%u dimensions), width=%u, type=%s\n", ind, "", name, ivl_signal_array_count(sig), ivl_signal_dimensions(sig), width, vt); } static void show_array_pattern_expression(ivl_expr_t net, unsigned ind) { size_t idx; fprintf(out, "%*sArrayPattern (%s): %u expressions\n", ind, "", vt_type_string(net), ivl_expr_parms(net)); for (idx = 0 ; idx < ivl_expr_parms(net) ; idx += 1) { show_expression(ivl_expr_parm(net,idx), ind+4); } } static void show_branch_access_expression(ivl_expr_t net, unsigned ind) { ivl_branch_t bra = ivl_expr_branch(net); ivl_nature_t nature = ivl_expr_nature(net); fprintf(out, "%*s\n", ind, "", bra, ivl_nature_name(nature)); if (ivl_expr_value(net) != IVL_VT_REAL) { fprintf(out, "%*sERROR: Expecting type IVL_VT_REAL, got %s\n", ind, "", vt_type_string(net)); stub_errors += 1; } ivl_nexus_t ta = ivl_branch_terminal(bra, 0); ivl_nexus_t tb = ivl_branch_terminal(bra, 1); ivl_discipline_t ta_disc = discipline_of_nexus(ta); if (ta_disc == 0) { fprintf(out, "%*sERROR: Source terminal of branch has no discipline\n", ind, ""); stub_errors += 1; return; } ivl_discipline_t tb_disc = discipline_of_nexus(tb); if (ta_disc == 0) { fprintf(out, "%*sERROR: Reference terminal of branch has no discipline\n", ind, ""); stub_errors += 1; return; } if (ta_disc != tb_disc) { fprintf(out, "%*sERROR: Branch terminal disciplines mismatch: %s != %s\n", ind, "", ivl_discipline_name(ta_disc), ivl_discipline_name(tb_disc)); stub_errors += 1; } } static void show_binary_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); ivl_expr_t oper1 = ivl_expr_oper1(net); ivl_expr_t oper2 = ivl_expr_oper2(net); fprintf(out, "%*s<\"%c\" width=%u, %s, type=%s>\n", ind, "", ivl_expr_opcode(net), width, sign, vt); if (oper1) { show_expression(oper1, ind+3); } else { fprintf(out, "%*sERROR: Missing operand 1\n", ind+3, ""); stub_errors += 1; } if (oper2) { show_expression(oper2, ind+3); } else { fprintf(out, "%*sERROR: Missing operand 2\n", ind+3, ""); stub_errors += 1; } switch (ivl_expr_opcode(net)) { case '*': if (ivl_expr_value(net) == IVL_VT_REAL) { if (ivl_expr_width(net) != 1) { fprintf(out, "%*sERROR: Result width incorrect. Expecting 1, got %u\n", ind+3, "", ivl_expr_width(net)); stub_errors += 1; } } else { /* The width of a multiply may be any width. The implicit assumption is that the multiply returns a width that is the sum of the widths of the arguments, that is then truncated to the desired width, never padded. The compiler will automatically take care of sign extensions of arguments, so that the code generator need only generate an UNSIGNED multiply, and the result will come out right. */ unsigned max_width = ivl_expr_width(oper1) + ivl_expr_width(oper2); if (ivl_expr_width(net) > max_width) { fprintf(out, "%*sERROR: Result width to width. Expecting <= %u, got %u\n", ind+3, "", max_width, ivl_expr_width(net)); stub_errors += 1; } } break; default: break; } } static void show_enumtype_expression(ivl_expr_t net, unsigned ind) { fprintf(out, "%*s\n", ind, "", ivl_expr_enumtype(net)); } static void show_function_call(ivl_expr_t net, unsigned ind) { ivl_scope_t def = ivl_expr_def(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); unsigned idx; fprintf(out, "%*s<%s %s function %s with %u arguments (width=%u)>\n", ind, "", vt, sign, ivl_scope_name(def), ivl_expr_parms(net), ivl_expr_width(net)); for (idx = 0 ; idx < ivl_expr_parms(net) ; idx += 1) show_expression(ivl_expr_parm(net,idx), ind+4); } static void show_memory_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); fprintf(out, "%*s\n", ind, "", width); } static void show_new_expression(ivl_expr_t net, unsigned ind) { switch (ivl_expr_value(net)) { case IVL_VT_CLASS: fprintf(out, "%*snew \n", ind, ""); if (ivl_expr_oper1(net)) { fprintf(out, "%*sERROR: class_new expression has a size!\n", ind+3, ""); show_expression(ivl_expr_oper1(net), ind+3); stub_errors += 1; } if (ivl_expr_oper2(net)){ fprintf(out, "%*sERROR: class_new with array element initializer!\n", ind+3, ""); show_expression(ivl_expr_oper2(net), ind+3); stub_errors += 1; } break; case IVL_VT_DARRAY: fprintf(out, "%*snew [] \n", ind, ""); if (ivl_expr_oper1(net)) { show_expression(ivl_expr_oper1(net), ind+3); } else { fprintf(out, "%*sERROR: darray_new missing size expression\n", ind+3, ""); stub_errors += 1; } /* The IVL_EX_NEW expression may include an element initializer. This may be an array pattern or simple expression. */ if (ivl_expr_oper2(net)) { show_expression(ivl_expr_oper2(net), ind+3); } break; default: fprintf(out, "%*snew ERROR: expression type: %s\n", ind+3, "", vt_type_string(net)); stub_errors += 1; break; } } static void show_null_expression(ivl_expr_t net, unsigned ind) { fprintf(out, "%*s\n", ind, ""); if (ivl_expr_value(net) != IVL_VT_CLASS) { fprintf(out, "%*sERROR: null expression must be IVL_VT_CLASS, got %s.\n", ind+3, "", vt_type_string(net)); stub_errors += 1; } } static void show_property_expression(ivl_expr_t net, unsigned ind) { ivl_signal_t sig = ivl_expr_signal(net); const char* pnam = ivl_expr_name(net); const char*signed_flag = ivl_expr_signed(net)? "signed" : "unsigned"; ivl_expr_t index; if (ivl_expr_value(net) == IVL_VT_REAL) { fprintf(out, "%*s\n", ind, "", ivl_signal_basename(sig), pnam); } else if (ivl_expr_value(net) == IVL_VT_STRING) { fprintf(out, "%*s\n", ind, "", ivl_signal_basename(sig), pnam); } else { fprintf(out, "%*s\n", ind, "", ivl_signal_basename(sig), pnam, ivl_expr_width(net), signed_flag); } if ( (index=ivl_expr_oper1(net)) ) { show_expression(index, ind+3); } if (ivl_signal_data_type(sig) != IVL_VT_CLASS) { fprintf(out, "%*sERROR: Property signal must be IVL_VT_CLASS, got %s.\n", ind+3, "", data_type_string(ivl_signal_data_type(sig))); } } static void show_select_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); ivl_expr_t oper1 = ivl_expr_oper1(net); ivl_expr_t oper2 = ivl_expr_oper2(net); if (ivl_expr_value(oper1) == IVL_VT_STRING) { /* If the sub-expression is a STRING, then this is a substring and the code generator will handle it differently. */ fprintf(out, "%*s\n", ind, "", width, width/8); if (width%8 != 0) { fprintf(out, "%*sERROR: Width should be a multiple of 8 bits.\n", ind, ""); stub_errors += 1; } assert(oper1); show_expression(oper1, ind+3); if (oper2) { show_expression(oper2, ind+3); } else { fprintf(out, "%*sERROR: oper2 missing! Pad makes no sense for IVL_VT_STRING expressions.\n", ind+3, ""); stub_errors += 1; } } else if (oper2) { /* If oper2 is present, then it is the base of a part select. The width of the expression defines the range of the part select. */ fprintf(out, "%*s\n", ind, "", width, sign, vt); show_expression(oper1, ind+3); show_expression(oper2, ind+3); } else { /* There is no base expression so this is a pad operation. The sub-expression is padded (signed or unsigned as appropriate) to the expression width. */ fprintf(out, "%*s\n", ind, "", width, sign); show_expression(oper1, ind+3); } } static void show_shallowcopy(ivl_expr_t net, unsigned ind) { ivl_expr_t oper1 = ivl_expr_oper1(net); ivl_expr_t oper2 = ivl_expr_oper2(net); fprintf(out, "%*s\n", ind, ""); show_expression(oper1, ind+3); show_expression(oper2, ind+3); if (ivl_expr_value(oper1) != ivl_expr_value(oper2)) { fprintf(out, "%*sERROR: Shallow copy operand types must match.\n", ind+3,""); stub_errors += 1; } if (ivl_expr_value(oper1)!=IVL_VT_CLASS && ivl_expr_value(oper1)!=IVL_VT_DARRAY) { fprintf(out, "%*sERROR: Operand 1 type is %s\n", ind+3, "", vt_type_string(oper1)); stub_errors += 1; } } static void show_signal_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); ivl_expr_t word = ivl_expr_oper1(net); ivl_signal_t sig = ivl_expr_signal(net); const char*vt_sig = data_type_string(ivl_signal_data_type(sig)); unsigned dimensions = ivl_signal_dimensions(sig); unsigned word_count = ivl_signal_array_count(sig); if (dimensions == 0 && word_count != 1) { fprintf(out, "%*sERROR: Word count = %u for non-array object\n", ind, "", word_count); stub_errors += 1; } fprintf(out, "%*s\n", ind, "", ivl_expr_name(net), word_count, width, sign, vt, vt_sig); /* If the expression refers to a signal array, then there must also be a word select expression, and if the signal is not an array, there must NOT be a word expression. */ if (dimensions == 0 && word != 0) { fprintf(out, "%*sERROR: Unexpected word expression\n", ind+2, ""); stub_errors += 1; } if (dimensions >= 1 && word == 0) { fprintf(out, "%*sERROR: Missing word expression\n", ind+2, ""); stub_errors += 1; } /* If this is not an array, then the expression with must match the signal width. We have IVL_EX_SELECT expressions for casting signal widths. */ if (dimensions == 0 && ivl_signal_width(sig) != width) { fprintf(out, "%*sERROR: Expression width (%u) doesn't match ivl_signal_width(sig)=%u\n", ind+2, "", width, ivl_signal_width(sig)); stub_errors += 1; } if (word != 0) { fprintf(out, "%*sAddress-0 word address:\n", ind+2, ""); show_expression(word, ind+2); } } static void show_ternary_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); fprintf(out, "%*s\n", ind, "", width, sign, vt); show_expression(ivl_expr_oper1(net), ind+4); show_expression(ivl_expr_oper2(net), ind+4); show_expression(ivl_expr_oper3(net), ind+4); if (ivl_expr_width(ivl_expr_oper2(net)) != width) { fprintf(out, "ERROR: Width of TRUE expressions is %u, not %u\n", ivl_expr_width(ivl_expr_oper2(net)), width); stub_errors += 1; } if (ivl_expr_width(ivl_expr_oper3(net)) != width) { fprintf(out, "ERROR: Width of FALSE expressions is %u, not %u\n", ivl_expr_width(ivl_expr_oper3(net)), width); stub_errors += 1; } } static void show_unary_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); char name[8]; switch (ivl_expr_opcode(net)) { default: snprintf(name, sizeof name, "%c", ivl_expr_opcode(net)); break; case 'm': snprintf(name, sizeof name, "abs()"); break; } if (ivl_expr_opcode(net) == '!' && ivl_expr_value(net) == IVL_VT_REAL) { fprintf(out, "%*sERROR: Real argument to unary ! !?\n", ind,""); stub_errors += 1; } fprintf(out, "%*s\n", ind, "", name, width, sign, vt); show_expression(ivl_expr_oper1(net), ind+4); } void show_expression(ivl_expr_t net, unsigned ind) { assert(net); unsigned idx; ivl_parameter_t par = ivl_expr_parameter(net); const ivl_expr_type_t code = ivl_expr_type(net); unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*sized = ivl_expr_sized(net)? "sized" : "unsized"; const char*vt = vt_type_string(net); switch (code) { case IVL_EX_ARRAY: show_array_expression(net, ind); break; case IVL_EX_ARRAY_PATTERN: show_array_pattern_expression(net, ind); break; case IVL_EX_BACCESS: show_branch_access_expression(net, ind); break; case IVL_EX_BINARY: show_binary_expression(net, ind); break; case IVL_EX_CONCAT: fprintf(out, "%*s\n", ind, "", ivl_expr_repeat(net), width, sign, vt); for (idx = 0 ; idx < ivl_expr_parms(net) ; idx += 1) show_expression(ivl_expr_parm(net, idx), ind+3); break; case IVL_EX_ENUMTYPE: show_enumtype_expression(net, ind); break; case IVL_EX_MEMORY: show_memory_expression(net, ind); break; case IVL_EX_NEW: show_new_expression(net, ind); break; case IVL_EX_NULL: show_null_expression(net, ind); break; case IVL_EX_PROPERTY: show_property_expression(net, ind); break; case IVL_EX_NUMBER: { const char*bits = ivl_expr_bits(net); fprintf(out, "%*s 0 ; idx -= 1) fprintf(out, "%c", bits[idx-1]); fprintf(out, ", %s %s %s", sign, sized, vt); if (par != 0) fprintf(out, ", parameter=%s", ivl_parameter_basename(par)); fprintf(out, ">\n"); break; } case IVL_EX_SELECT: show_select_expression(net, ind); break; case IVL_EX_STRING: fprintf(out, "%*s\n", vt); break; case IVL_EX_SFUNC: fprintf(out, "%*s\n", ind, "", ivl_expr_name(net), width, sign, vt, ivl_expr_file(net), ivl_expr_lineno(net)); { unsigned cnt = ivl_expr_parms(net); unsigned jdx; for (jdx = 0 ; jdx < cnt ; jdx += 1) show_expression(ivl_expr_parm(net, jdx), ind+3); } break; case IVL_EX_SIGNAL: show_signal_expression(net, ind); break; case IVL_EX_TERNARY: show_ternary_expression(net, ind); break; case IVL_EX_UNARY: show_unary_expression(net, ind); break; case IVL_EX_UFUNC: show_function_call(net, ind); break; case IVL_EX_REALNUM: { int jdx; union foo { double rv; unsigned char bv[sizeof(double)]; } tmp; tmp.rv = ivl_expr_dvalue(net); fprintf(out, "%*s 0 ; jdx -= 1) fprintf(out, "%02x", tmp.bv[jdx-1]); fprintf(out, ")"); if (par != 0) fprintf(out, ", parameter=%s", ivl_parameter_basename(par)); fprintf(out, ">\n"); } break; case IVL_EX_SHALLOWCOPY: show_shallowcopy(net, ind); break; default: fprintf(out, "%*s\n", ind, "", code); break; } } iverilog-10_1/tgt-stub/priv.h000066400000000000000000000045471265551621300162560ustar00rootroot00000000000000#ifndef IVL_priv_H #define IVL_priv_H /* * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include /* * This is the output file where the generated result should be * written. */ extern FILE*out; /* * Keep a running count of errors that the stub detects. This will be * the error count returned to the ivl_target environment. */ extern int stub_errors; /* * This function finds the vector width of a signal. It relies on the * assumption that all the signal inputs to the nexus have the same * width. The ivl_target API should assert that condition. */ extern unsigned width_of_nexus(ivl_nexus_t nex); extern ivl_variable_type_t type_of_nexus(ivl_nexus_t nex); extern ivl_discipline_t discipline_of_nexus(ivl_nexus_t nex); extern unsigned width_of_type(ivl_type_t net); /* * Test that a given expression is a valid delay expression, and * print an error message if not. */ extern void test_expr_is_delay(ivl_expr_t expr); extern void show_class(ivl_type_t net); extern void show_enumerate(ivl_enumtype_t net); extern void show_constant(ivl_net_const_t net); /* * Show the details of the expression. */ extern void show_expression(ivl_expr_t net, unsigned ind); /* * Show the statement. */ extern void show_statement(ivl_statement_t net, unsigned ind); /* * Show the type of the signal, in one line. */ extern void show_type_of_signal(ivl_signal_t); extern void show_switch(ivl_switch_t net); /* */ extern const char*data_type_string(ivl_variable_type_t vtype); extern void show_net_type(ivl_type_t net_type); #endif /* IVL_priv_H */ iverilog-10_1/tgt-stub/statement.c000066400000000000000000000356101265551621300172700ustar00rootroot00000000000000/* * Copyright (c) 2004-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include /* * If the l-value signal is a darray object, then the ivl_lval_idx() * gets you the array index expression. */ static unsigned show_assign_lval_darray(ivl_lval_t lval, unsigned ind) { ivl_signal_t sig = ivl_lval_sig(lval); assert(sig); fprintf(out, "%*s{name=%s darray width=%u l-value width=%u}\n", ind, "", ivl_signal_name(sig), ivl_signal_width(sig), ivl_lval_width(lval)); if (ivl_lval_idx(lval)) { fprintf(out, "%*sAddress-0 select of ", ind+4, ""); show_type_of_signal(sig); fprintf(out, ":\n"); show_expression(ivl_lval_idx(lval), ind+6); } if (ivl_lval_part_off(lval)) { fprintf(out, "%*sERROR: unexpected Part select expression:\n", ind+4, ""); stub_errors += 1; show_expression(ivl_lval_part_off(lval), ind+8); } return ivl_lval_width(lval); } static unsigned show_assign_lval_class(ivl_lval_t lval, unsigned ind) { ivl_signal_t sig = ivl_lval_sig(lval); int sig_prop = ivl_lval_property_idx(lval); assert(sig); /* If there is no property select, then this l-value is for the class handle itself. */ if (sig_prop < 0) { fprintf(out, "%*s{name=%s class object}\n", ind, "", ivl_signal_name(sig)); if (ivl_lval_width(lval) != 1) { fprintf(out, "%*sERROR: ivl_lval_width should be 1 for class objects\n", ind+4, ""); stub_errors += 1; } return ivl_lval_width(lval); } fprintf(out, "%*s{name=%s. l-value width=%u}\n", ind, "", ivl_signal_name(sig), sig_prop, ivl_lval_width(lval)); if (ivl_lval_idx(lval)) { ivl_expr_t mux = ivl_lval_idx(lval); fprintf(out, "%*sAddress-0 select expression:\n", ind+4, ""); show_expression(mux, ind+6); } return ivl_lval_width(lval); } unsigned width_of_type(ivl_type_t net) { switch (ivl_type_packed_dimensions(net)) { case 0: return 1; case 1: { int msb = ivl_type_packed_msb(net,0); int lsb = ivl_type_packed_lsb(net,0); if (msb > lsb) return msb-lsb+1; else return lsb-msb+1; } default: return 0; } } static ivl_type_t show_assign_lval_nest(ivl_lval_t lval, unsigned ind) { ivl_type_t sub_type; if (ivl_lval_nest(lval)) { fprintf(out, "%*s{nested lval property=%d}\n", ind, "", ivl_lval_property_idx(lval)); sub_type = show_assign_lval_nest(ivl_lval_nest(lval), ind+4); } else { assert(ivl_lval_sig(lval)); ivl_signal_t sig = ivl_lval_sig(lval); fprintf(out, "%*s{name=%s property=%d, signal width=%u l-value width=%u}\n", ind, "", ivl_signal_name(sig), ivl_lval_property_idx(lval), ivl_signal_width(sig), ivl_lval_width(lval)); sub_type = ivl_signal_net_type(sig); } assert(ivl_type_base(sub_type) == IVL_VT_CLASS); ivl_type_t lval_type = ivl_type_prop_type(sub_type, ivl_lval_property_idx(lval)); return lval_type; } static unsigned show_assign_lval(ivl_lval_t lval, unsigned ind) { ivl_lval_t lval_nest = ivl_lval_nest(lval); if (lval_nest) { ivl_type_t net_type = show_assign_lval_nest(lval, ind); return width_of_type(net_type); } ivl_signal_t sig = ivl_lval_sig(lval); assert(sig); /* Special case: target signal is a darray. */ if (ivl_signal_data_type(sig) == IVL_VT_DARRAY) return show_assign_lval_darray(lval, ind); /* Special case: target signal is a class. */ if (ivl_signal_data_type(sig) == IVL_VT_CLASS) return show_assign_lval_class(lval, ind); fprintf(out, "%*s{name=%s signal width=%u l-value width=%u}\n", ind, "", ivl_signal_name(sig), ivl_signal_width(sig), ivl_lval_width(lval)); if (ivl_lval_idx(lval)) { fprintf(out, "%*sAddress-0 select expression:\n", ind+4, ""); show_expression(ivl_lval_idx(lval), ind+6); if (ivl_signal_dimensions(sig) < 1) { fprintf(out, "%*sERROR: Address on signal with " "array dimensions=%u\n", ind+4, "", ivl_signal_dimensions(sig)); stub_errors += 1; } } else if (ivl_signal_array_count(sig) > 1) { fprintf(out, "%*sERROR: Address missing on signal with " "word count=%u\n", ind+4, "", ivl_signal_array_count(sig)); stub_errors += 1; } if (ivl_lval_part_off(lval)) { fprintf(out, "%*sPart select base:\n", ind+4, ""); show_expression(ivl_lval_part_off(lval), ind+8); } return ivl_lval_width(lval); } static void show_stmt_cassign(ivl_statement_t net, unsigned ind) { unsigned idx; unsigned lwid = 0; fprintf(out, "%*sCONTINUOUS ASSIGN \n", ind, "", ivl_stmt_lwidth(net)); for (idx = 0 ; idx < ivl_stmt_lvals(net) ; idx += 1) { lwid += show_assign_lval(ivl_stmt_lval(net, idx), ind+4); } fprintf(out, "%*sTotal expected l-value width: %u bits\n", ind+4, "", lwid); show_expression(ivl_stmt_rval(net), ind+4); } static void show_stmt_delayx(ivl_statement_t net, unsigned ind) { fprintf(out, "%*s#(X) /* calculated delay */\n", ind, ""); show_expression(ivl_stmt_delay_expr(net), ind+4); show_statement(ivl_stmt_sub_stmt(net), ind+2); } static void show_stmt_disable(ivl_statement_t net, unsigned ind) { ivl_scope_t scope = ivl_stmt_call(net); if (scope) { fprintf(out, "%*sdisable %s\n", ind, "", ivl_scope_basename(scope)); } else { fprintf(out, "%*sdisable fork\n", ind, ""); } } static void show_stmt_force(ivl_statement_t net, unsigned ind) { unsigned idx; unsigned lwid = 0; fprintf(out, "%*sforce \n", ind, "", ivl_stmt_lwidth(net)); for (idx = 0 ; idx < ivl_stmt_lvals(net) ; idx += 1) { lwid += show_assign_lval(ivl_stmt_lval(net, idx), ind+4); } fprintf(out, "%*sTotal expected l-value width: %u bits\n", ind+4, "", lwid); show_expression(ivl_stmt_rval(net), ind+4); } static void show_stmt_release(ivl_statement_t net, unsigned ind) { unsigned idx; unsigned lwid = 0; fprintf(out, "%*srelease \n", ind, "", ivl_stmt_lwidth(net)); for (idx = 0 ; idx < ivl_stmt_lvals(net) ; idx += 1) { lwid += show_assign_lval(ivl_stmt_lval(net, idx), ind+4); } fprintf(out, "%*sTotal l-value width: %u bits\n", ind+4, "", lwid); } /* * A trigger statement is the "-> name;" syntax in Verilog, where a * trigger signal is sent to a named event. The trigger statement is * actually a very simple object. */ static void show_stmt_trigger(ivl_statement_t net, unsigned ind) { unsigned cnt = ivl_stmt_nevent(net); unsigned idx; fprintf(out, "%*s->", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_event_t event = ivl_stmt_events(net, idx); fprintf(out, " %s", ivl_event_basename(event)); } /* The compiler should make exactly one target event, so if we find more or less, then print some error text. */ if (cnt != 1) { fprintf(out, " /* ERROR: Expect one target event, got %u */", cnt); } fprintf(out, ";\n"); } /* * The wait statement contains simply an array of events to wait on, * and a sub-statement to execute when an event triggers. */ static void show_stmt_wait(ivl_statement_t net, unsigned ind) { unsigned idx; const char*comma = ""; fprintf(out, "%*s", ind, ""); /* Emit a SystemVerilog wait fork. */ if ((ivl_stmt_nevent(net) == 1) && (ivl_stmt_events(net, 0) == 0)) { assert(ivl_statement_type(ivl_stmt_sub_stmt(net)) == IVL_ST_NOOP); fprintf(out, "wait fork;\n"); return; } fprintf(out, "@("); for (idx = 0 ; idx < ivl_stmt_nevent(net) ; idx += 1) { ivl_event_t evnt = ivl_stmt_events(net, idx); if (evnt == 0) fprintf(out, "%s/*ERROR*/", comma); else fprintf(out, "%s%s.%s", comma, ivl_scope_name(ivl_event_scope(evnt)), ivl_event_basename(evnt)); comma = ", "; } fprintf(out, ")\n"); show_statement(ivl_stmt_sub_stmt(net), ind+4); } void show_statement(ivl_statement_t net, unsigned ind) { unsigned idx; char opcode = 0; unsigned lwid = 0; const ivl_statement_type_t code = ivl_statement_type(net); switch (code) { case IVL_ST_ALLOC: fprintf(out, "%*sallocate automatic storage ...\n", ind, ""); break; case IVL_ST_ASSIGN: opcode = ivl_stmt_opcode(net); if (opcode == 0) opcode = ' '; fprintf(out, "%*sASSIGN opcode=%c\n", ind, "", ivl_stmt_lwidth(net), opcode); for (idx = 0 ; idx < ivl_stmt_lvals(net) ; idx += 1) lwid += show_assign_lval(ivl_stmt_lval(net, idx), ind+4); if (ivl_stmt_delay_expr(net)) show_expression(ivl_stmt_delay_expr(net), idx+4); if (ivl_stmt_rval(net)) show_expression(ivl_stmt_rval(net), ind+4); fprintf(out, "%*sTotal l-value width is %u\n", ind+2, "", lwid); break; case IVL_ST_ASSIGN_NB: fprintf(out, "%*sASSIGN_NB \n", ind, "", ivl_stmt_lwidth(net)); for (idx = 0 ; idx < ivl_stmt_lvals(net) ; idx += 1) show_assign_lval(ivl_stmt_lval(net, idx), ind+4); if (ivl_stmt_delay_expr(net)) { fprintf(out, "%*s\n", ind+4, ""); show_expression(ivl_stmt_delay_expr(net), ind+6); } if (ivl_stmt_rval(net)) show_expression(ivl_stmt_rval(net), ind+4); break; case IVL_ST_BLOCK: { unsigned cnt = ivl_stmt_block_count(net); ivl_scope_t sscope = ivl_stmt_block_scope(net); if (sscope) fprintf(out, "%*sbegin : %s\n", ind, "", ivl_scope_name(sscope)); else fprintf(out, "%*sbegin\n", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); show_statement(cur, ind+4); } fprintf(out, "%*send\n", ind, ""); break; } case IVL_ST_CASEX: case IVL_ST_CASEZ: case IVL_ST_CASER: case IVL_ST_CASE: { unsigned cnt = ivl_stmt_case_count(net); fprintf(out, "%*scase (...) <%u cases>\n", ind, "", cnt); show_expression(ivl_stmt_cond_expr(net), ind+4); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_expr_t ex = ivl_stmt_case_expr(net, idx); ivl_statement_t st = ivl_stmt_case_stmt(net, idx); if (ex == 0) fprintf(out, "%*sdefault\n", ind+4, ""); else show_expression(ex, ind+4); show_statement(st, ind+4); } fprintf(out, "%*sendcase\n", ind, ""); break; } case IVL_ST_CASSIGN: show_stmt_cassign(net, ind); break; case IVL_ST_CONDIT: { ivl_expr_t ex = ivl_stmt_cond_expr(net); ivl_statement_t t = ivl_stmt_cond_true(net); ivl_statement_t f = ivl_stmt_cond_false(net); fprintf(out, "%*sif (...)\n", ind, ""); if (ex) { show_expression(ex, ind+4); } else { fprintf(out, "%*sERROR: Condition expression is NIL;\n", ind+4, ""); stub_errors += 1; } if (t) show_statement(t, ind+4); else fprintf(out, "%*s;\n", ind+4, ""); if (f) { fprintf(out, "%*selse\n", ind, ""); show_statement(f, ind+4); } break; } case IVL_ST_CONTRIB: fprintf(out, "%*sCONTRIBUTION ( <+ )\n", ind, ""); show_expression(ivl_stmt_lexp(net), ind+4); show_expression(ivl_stmt_rval(net), ind+4); break; case IVL_ST_DEASSIGN: fprintf(out, "%*sDEASSIGN \n", ind, "", ivl_stmt_lwidth(net)); for (idx = 0 ; idx < ivl_stmt_lvals(net) ; idx += 1) show_assign_lval(ivl_stmt_lval(net, idx), ind+4); break; case IVL_ST_DELAY: fprintf(out, "%*s#%" PRIu64 "\n", ind, "", ivl_stmt_delay_val(net)); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; case IVL_ST_DELAYX: show_stmt_delayx(net, ind); break; case IVL_ST_DISABLE: show_stmt_disable(net, ind); break; case IVL_ST_DO_WHILE: fprintf(out, "%*sdo\n", ind, ""); show_statement(ivl_stmt_sub_stmt(net), ind+2); fprintf(out, "%*swhile\n", ind, ""); show_expression(ivl_stmt_cond_expr(net), ind+4); break; case IVL_ST_FORCE: show_stmt_force(net, ind); break; case IVL_ST_FORK: { unsigned cnt = ivl_stmt_block_count(net); fprintf(out, "%*sfork\n", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); show_statement(cur, ind+4); } fprintf(out, "%*sjoin\n", ind, ""); break; } case IVL_ST_FORK_JOIN_ANY: { unsigned cnt = ivl_stmt_block_count(net); fprintf(out, "%*sfork\n", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); show_statement(cur, ind+4); } fprintf(out, "%*sjoin_any\n", ind, ""); break; } case IVL_ST_FORK_JOIN_NONE: { unsigned cnt = ivl_stmt_block_count(net); fprintf(out, "%*sfork\n", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); show_statement(cur, ind+4); } fprintf(out, "%*sjoin_none\n", ind, ""); break; } case IVL_ST_FREE: fprintf(out, "%*sfree automatic storage ...\n", ind, ""); break; case IVL_ST_NOOP: fprintf(out, "%*s/* noop */;\n", ind, ""); break; case IVL_ST_RELEASE: show_stmt_release(net, ind); break; case IVL_ST_STASK: { fprintf(out, "%*sCall %s(%u parameters); /* %s:%u */\n", ind, "", ivl_stmt_name(net), ivl_stmt_parm_count(net), ivl_stmt_file(net), ivl_stmt_lineno(net)); for (idx = 0 ; idx < ivl_stmt_parm_count(net) ; idx += 1) if (ivl_stmt_parm(net, idx)) show_expression(ivl_stmt_parm(net, idx), ind+4); break; } case IVL_ST_TRIGGER: show_stmt_trigger(net, ind); break; case IVL_ST_UTASK: fprintf(out, "%*scall task ...\n", ind, ""); break; case IVL_ST_WAIT: show_stmt_wait(net, ind); break; case IVL_ST_WHILE: fprintf(out, "%*swhile\n", ind, ""); show_expression(ivl_stmt_cond_expr(net), ind+4); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; default: fprintf(out, "%*sunknown statement type (%d)\n", ind, "", code); } } iverilog-10_1/tgt-stub/stub-s.conf000066400000000000000000000001401265551621300171720ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=stub.tgt iverilog-10_1/tgt-stub/stub.c000066400000000000000000001424241265551621300162430ustar00rootroot00000000000000/* * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This is a sample target module. All this does is write to the * output file some information about each object handle when each of * the various object functions is called. This can be used to * understand the behavior of the core as it uses a target module. */ # include "version_base.h" # include "version_tag.h" # include "config.h" # include "priv.h" # include # include # include # include # include "ivl_alloc.h" static const char*version_string = "Icarus Verilog STUB Code Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; FILE*out; int stub_errors = 0; static struct udp_define_cell { ivl_udp_t udp; unsigned ref; struct udp_define_cell*next; }*udp_define_list = 0; static void reference_udp_definition(ivl_udp_t udp) { struct udp_define_cell*cur; if (udp_define_list == 0) { udp_define_list = calloc(1, sizeof(struct udp_define_cell)); udp_define_list->udp = udp; udp_define_list->ref = 1; return; } cur = udp_define_list; while (cur->udp != udp) { if (cur->next == 0) { cur->next = calloc(1, sizeof(struct udp_define_cell)); cur->next->udp = udp; cur->next->ref = 1; return; } cur = cur->next; } cur->ref += 1; } /* * This function finds the vector width of a signal. It relies on the * assumption that all the signal inputs to the nexus have the same * width. The ivl_target API should assert that condition. */ unsigned width_of_nexus(ivl_nexus_t nex) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig != 0) { return ivl_signal_width(sig); } } /* ERROR: A nexus should have at least one signal to carry properties like width. */ return 0; } ivl_discipline_t discipline_of_nexus(ivl_nexus_t nex) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex); idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig != 0) { return ivl_signal_discipline(sig); } } /* ERROR: A nexus should have at least one signal to carry properties like the data type. */ return 0; } ivl_variable_type_t type_of_nexus(ivl_nexus_t net) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(net); idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(net, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig != 0) { return ivl_signal_data_type(sig); } } /* ERROR: A nexus should have at least one signal to carry properties like the data type. */ return IVL_VT_NO_TYPE; } const char*data_type_string(ivl_variable_type_t vtype) { const char*vt = "??"; switch (vtype) { case IVL_VT_NO_TYPE: vt = "NO_TYPE"; break; case IVL_VT_VOID: vt = "void"; break; case IVL_VT_BOOL: vt = "bool"; break; case IVL_VT_REAL: vt = "real"; break; case IVL_VT_LOGIC: vt = "logic"; break; case IVL_VT_STRING: vt = "string"; break; case IVL_VT_DARRAY: vt = "darray"; break; case IVL_VT_CLASS: vt = "class"; break; case IVL_VT_QUEUE: vt = "queue"; break; } return vt; } /* * The compiler will check the types of drivers to signals and will * only connect outputs to signals that are compatible. This function * shows the type compatibility that the compiler enforces at the * ivl_target.h level. */ static int check_signal_drive_type(ivl_variable_type_t sig_type, ivl_variable_type_t driver_type) { if (sig_type == IVL_VT_LOGIC && driver_type == IVL_VT_BOOL) return !0; if (sig_type == IVL_VT_LOGIC && driver_type == IVL_VT_LOGIC) return !0; if (sig_type == IVL_VT_BOOL && driver_type == IVL_VT_BOOL) return !0; if (sig_type == driver_type) return !0; return 0; } /* * The compare-like LPM nodes have input widths that match the * ivl_lpm_width() value, and an output width of 1. This function * checks that that is so, and indicates errors otherwise. */ static void check_cmp_widths(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); /* Check that the input widths are as expected. The inputs must be the width of the ivl_lpm_width() for this device, even though the output for this device is 1 bit. */ if (width != width_of_nexus(ivl_lpm_data(net,0))) { fprintf(out, " ERROR: Width of A is %u, not %u\n", width_of_nexus(ivl_lpm_data(net,0)), width); stub_errors += 1; } if (width != width_of_nexus(ivl_lpm_data(net,1))) { fprintf(out, " ERROR: Width of B is %u, not %u\n", width_of_nexus(ivl_lpm_data(net,1)), width); stub_errors += 1; } if (width_of_nexus(ivl_lpm_q(net)) != 1) { fprintf(out, " ERROR: Width of Q is %u, not 1\n", width_of_nexus(ivl_lpm_q(net))); stub_errors += 1; } } static void show_lpm_arithmetic_pins(ivl_lpm_t net) { ivl_nexus_t nex; nex = ivl_lpm_q(net); fprintf(out, " Q: %p\n", nex); nex = ivl_lpm_data(net, 0); fprintf(out, " DataA: %p\n", nex); nex = ivl_lpm_data(net, 1); fprintf(out, " DataB: %p\n", nex); } static void show_lpm_abs(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); ivl_nexus_t nex; fprintf(out, " LPM_ABS %s: \n", ivl_lpm_basename(net), width); nex = ivl_lpm_q(net); fprintf(out, " Q: %p\n", nex); nex = ivl_lpm_data(net, 0); fprintf(out, " D: %p\n", nex); if (nex == 0) { fprintf(out, " ERROR: missing input\n"); stub_errors += 1; return; } if (width_of_nexus(nex) != width) { fprintf(out, " ERROR: D width (%u) is wrong\n", width_of_nexus(nex)); stub_errors += 1; } } static void show_lpm_add(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_ADD %s: \n", ivl_lpm_basename(net), width); show_lpm_arithmetic_pins(net); } static void show_lpm_array(ivl_lpm_t net) { ivl_nexus_t nex; unsigned width = ivl_lpm_width(net); ivl_signal_t array = ivl_lpm_array(net); fprintf(out, " LPM_ARRAY: \n", width, ivl_signal_basename(array)); nex = ivl_lpm_q(net); assert(nex); fprintf(out, " Q: %p\n", nex); nex = ivl_lpm_select(net); assert(nex); fprintf(out, " Address: %p (address width=%u)\n", nex, ivl_lpm_selects(net)); if (width_of_nexus(ivl_lpm_q(net)) != width) { fprintf(out, " ERROR: Data Q width doesn't match " "nexus width=%u\n", width_of_nexus(ivl_lpm_q(net))); stub_errors += 1; } if (ivl_signal_width(array) != width) { fprintf(out, " ERROR: Data width doesn't match " "word width=%u\n", ivl_signal_width(array)); stub_errors += 1; } } static void show_lpm_cast_int(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); ivl_nexus_t q, a; fprintf(out, " LPM_CAST_INT %s: \n", ivl_lpm_basename(net), width); q = ivl_lpm_q(net); a = ivl_lpm_data(net,0); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); if (type_of_nexus(q) == IVL_VT_REAL) { fprintf(out, " ERROR: Data type of Q is %s, expecting !real\n", data_type_string(type_of_nexus(q))); stub_errors += 1; } if (type_of_nexus(a) != IVL_VT_REAL) { fprintf(out, " ERROR: Data type of A is %s, expecting real\n", data_type_string(type_of_nexus(a))); stub_errors += 1; } } static void show_lpm_cast_real(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); ivl_nexus_t q, a; fprintf(out, " LPM_CAST_REAL %s: \n", ivl_lpm_basename(net), width); q = ivl_lpm_q(net); a = ivl_lpm_data(net,0); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); if (type_of_nexus(q) != IVL_VT_REAL) { fprintf(out, " ERROR: Data type of Q is %s, expecting real\n", data_type_string(type_of_nexus(q))); stub_errors += 1; } if (type_of_nexus(a) == IVL_VT_REAL) { fprintf(out, " ERROR: Data type of A is %s, expecting !real\n", data_type_string(type_of_nexus(a))); stub_errors += 1; } } static void show_lpm_divide(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_DIVIDE %s: \n", ivl_lpm_basename(net), width); show_lpm_arithmetic_pins(net); } /* IVL_LPM_CMP_EEQ/EQX/EQZ/NEE * This LPM node supports two-input compare. The output width is * actually always 1, the lpm_width is the expected width of the inputs. */ static void show_lpm_cmp_eeq(ivl_lpm_t net) { const char*str = 0; switch (ivl_lpm_type(net)) { case IVL_LPM_CMP_EEQ: str = "EEQ"; break; case IVL_LPM_CMP_EQX: str = "EQX"; break; case IVL_LPM_CMP_EQZ: str = "EQZ"; break; case IVL_LPM_CMP_NEE: str = "NEE"; break; default: assert(0); break; } unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_CMP_%s %s: \n", str, ivl_lpm_basename(net), width); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); fprintf(out, " B: %p\n", ivl_lpm_data(net,1)); check_cmp_widths(net); } /* IVL_LPM_CMP_GE * This LPM node supports two-input compare. */ static void show_lpm_cmp_ge(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_CMP_GE %s: \n", ivl_lpm_basename(net), width, ivl_lpm_signed(net)? "signed" : "unsigned"); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); fprintf(out, " B: %p\n", ivl_lpm_data(net,1)); check_cmp_widths(net); } /* IVL_LPM_CMP_GT * This LPM node supports two-input compare. */ static void show_lpm_cmp_gt(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_CMP_GT %s: \n", ivl_lpm_basename(net), width, ivl_lpm_signed(net)? "signed" : "unsigned"); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); fprintf(out, " B: %p\n", ivl_lpm_data(net,1)); check_cmp_widths(net); } /* IVL_LPM_CMP_NE * This LPM node supports two-input compare. The output width is * actually always 1, the lpm_width is the expected width of the inputs. */ static void show_lpm_cmp_ne(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_CMP_NE %s: \n", ivl_lpm_basename(net), width); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); fprintf(out, " B: %p\n", ivl_lpm_data(net,1)); check_cmp_widths(net); } /* IVL_LPM_CONCAT, IVL_LPM_CONCATZ * The concat device takes N inputs (N=ivl_lpm_size) and generates * a single output. The total output is known from the ivl_lpm_width * function. The widths of all the inputs are inferred from the widths * of the signals connected to the nexus of the inputs. The compiler * makes sure the input widths add up to the output width. */ static void show_lpm_concat(ivl_lpm_t net) { unsigned idx; unsigned width_sum = 0; unsigned width = ivl_lpm_width(net); const char*z = ivl_lpm_type(net)==IVL_LPM_CONCATZ? "Z" : ""; fprintf(out, " LPM_CONCAT%s %s: \n", z, ivl_lpm_basename(net), width, ivl_lpm_size(net)); fprintf(out, " O: %p\n", ivl_lpm_q(net)); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) { ivl_nexus_t nex = ivl_lpm_data(net, idx); unsigned signal_width = width_of_nexus(nex); fprintf(out, " I%u: %p (width=%u)\n", idx, nex, signal_width); width_sum += signal_width; } if (width_sum != width) { fprintf(out, " ERROR! Got %u bits input, expecting %u!\n", width_sum, width); } } static void show_lpm_ff(ivl_lpm_t net) { ivl_nexus_t nex; char*edge = ivl_lpm_negedge(net) ? "negedge" : "posedge"; unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_FF %s: \n", ivl_lpm_basename(net), edge, width); nex = ivl_lpm_clk(net); fprintf(out, " clk: %p\n", nex); if (width_of_nexus(nex) != 1) { fprintf(out, " clk: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } if (ivl_lpm_enable(net)) { nex = ivl_lpm_enable(net); fprintf(out, " CE: %p\n", nex); if (width_of_nexus(nex) != 1) { fprintf(out, " CE: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } } if (ivl_lpm_async_clr(net)) { nex = ivl_lpm_async_clr(net); fprintf(out, " Aclr: %p\n", nex); if (width_of_nexus(nex) != 1) { fprintf(out, " Aclr: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } } if (ivl_lpm_async_set(net)) { nex = ivl_lpm_async_set(net); fprintf(out, " Aset: %p\n", nex); if (width_of_nexus(nex) != 1) { fprintf(out, " Aset: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } } nex = ivl_lpm_data(net,0); fprintf(out, " D: %p\n", nex); if (width_of_nexus(nex) != width) { fprintf(out, " D: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } nex = ivl_lpm_q(net); fprintf(out, " Q: %p\n", nex); if (width_of_nexus(nex) != width) { fprintf(out, " Q: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } } static void show_lpm_mod(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_MOD %s: \n", ivl_lpm_basename(net), width); show_lpm_arithmetic_pins(net); } /* * The LPM_MULT node has a Q output and two data inputs. The width of * the Q output must be the width of the node itself. */ static void show_lpm_mult(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_MULT %s: \n", ivl_lpm_basename(net), width); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p \n", ivl_lpm_data(net,0), width_of_nexus(ivl_lpm_data(net,0))); fprintf(out, " B: %p \n", ivl_lpm_data(net,1), width_of_nexus(ivl_lpm_data(net,1))); if (width != width_of_nexus(ivl_lpm_q(net))) { fprintf(out, " ERROR: Width of Q is %u, not %u\n", width_of_nexus(ivl_lpm_q(net)), width); stub_errors += 1; } } /* * Show an IVL_LPM_MUX. * * The compiler is supposed to make sure that the Q output and data * inputs all have the width of the device. The ivl_lpm_select input * has its own width. */ static void show_lpm_mux(ivl_lpm_t net) { ivl_nexus_t nex; unsigned idx; unsigned width = ivl_lpm_width(net); unsigned size = ivl_lpm_size(net); ivl_drive_t drive0 = ivl_lpm_drive0(net); ivl_drive_t drive1 = ivl_lpm_drive1(net); fprintf(out, " LPM_MUX %s: \n", ivl_lpm_basename(net), width, size); nex = ivl_lpm_q(net); fprintf(out, " Q: %p \n", nex, drive0, drive1); if (width != width_of_nexus(nex)) { fprintf(out, " Q: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } /* The select input is a vector with the width from the ivl_lpm_selects function. */ nex = ivl_lpm_select(net); fprintf(out, " S: %p \n", nex, ivl_lpm_selects(net)); if (ivl_lpm_selects(net) != width_of_nexus(nex)) { fprintf(out, " S: ERROR: Nexus width is %u\n", width_of_nexus(nex)); stub_errors += 1; } /* The ivl_lpm_size() method give the number of inputs that can be selected from. */ for (idx = 0 ; idx < size ; idx += 1) { nex = ivl_lpm_data(net,idx); fprintf(out, " D%u: %p\n", idx, nex); if (width != width_of_nexus(nex)) { fprintf(out, " D%u: ERROR, Nexus width is %u\n", idx, width_of_nexus(nex)); stub_errors += 1; } } } static void show_lpm_part(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned base = ivl_lpm_base(net); ivl_nexus_t sel = ivl_lpm_data(net,1); const char*part_type_string = ""; switch (ivl_lpm_type(net)) { case IVL_LPM_PART_VP: part_type_string = "VP"; break; case IVL_LPM_PART_PV: part_type_string = "PV"; break; default: break; } fprintf(out, " LPM_PART_%s %s: \n", part_type_string, ivl_lpm_basename(net), width, base, ivl_lpm_signed(net)); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " I: %p\n", ivl_lpm_data(net,0)); if (sel != 0) { fprintf(out, " S: %p\n", sel); if (base != 0) { fprintf(out, " ERROR: Part select has base AND selector\n"); stub_errors += 1; } } /* The compiler must assure that the base plus the part select width fits within the input to the part select. */ switch (ivl_lpm_type(net)) { case IVL_LPM_PART_VP: if (width_of_nexus(ivl_lpm_data(net,0)) < (width+base)) { fprintf(out, " ERROR: Part select is out of range." " Data nexus width=%u, width+base=%u\n", width_of_nexus(ivl_lpm_data(net,0)), width+base); stub_errors += 1; } if (width_of_nexus(ivl_lpm_q(net)) != width) { fprintf(out, " ERROR: Part select input mismatch." " Nexus width=%u, expect width=%u\n", width_of_nexus(ivl_lpm_q(net)), width); stub_errors += 1; } break; case IVL_LPM_PART_PV: if (width_of_nexus(ivl_lpm_q(net)) < (width+base)) { fprintf(out, " ERROR: Part select is out of range." " Target nexus width=%u, width+base=%u\n", width_of_nexus(ivl_lpm_q(net)), width+base); stub_errors += 1; } if (width_of_nexus(ivl_lpm_data(net,0)) != width) { fprintf(out, " ERROR: Part select input mismatch." " Nexus width=%u, expect width=%u\n", width_of_nexus(ivl_lpm_data(net,0)), width); stub_errors += 1; } break; default: assert(0); } } /* * The reduction operators have similar characteristics and are * displayed here. */ static void show_lpm_re(ivl_lpm_t net) { ivl_nexus_t nex; const char*type = "?"; unsigned width = ivl_lpm_width(net); switch (ivl_lpm_type(net)) { case IVL_LPM_RE_AND: type = "AND"; break; case IVL_LPM_RE_NAND: type = "NAND"; break; case IVL_LPM_RE_OR: type = "OR"; break; case IVL_LPM_RE_NOR: type = "NOR"; break; case IVL_LPM_RE_XOR: type = "XOR"; break; case IVL_LPM_RE_XNOR: type = "XNOR"; default: break; } fprintf(out, " LPM_RE_%s: %s \n", type, ivl_lpm_name(net),width); nex = ivl_lpm_q(net); fprintf(out, " Q: %p\n", nex); nex = ivl_lpm_data(net, 0); fprintf(out, " D: %p\n", nex); nex = ivl_lpm_q(net); if (1 != width_of_nexus(nex)) { fprintf(out, " ERROR: Width of Q is %u, expecting 1\n", width_of_nexus(nex)); stub_errors += 1; } nex = ivl_lpm_data(net, 0); if (width != width_of_nexus(nex)) { fprintf(out, " ERROR: Width of input is %u, expecting %u\n", width_of_nexus(nex), width); stub_errors += 1; } } static void show_lpm_repeat(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned count = ivl_lpm_size(net); ivl_nexus_t nex_q = ivl_lpm_q(net); ivl_nexus_t nex_a = ivl_lpm_data(net,0); fprintf(out, " LPM_REPEAT %s: \n", ivl_lpm_basename(net), width, count); fprintf(out, " Q: %p\n", nex_q); fprintf(out, " D: %p\n", nex_a); if (width != width_of_nexus(nex_q)) { fprintf(out, " ERROR: Width of Q is %u, expecting %u\n", width_of_nexus(nex_q), width); stub_errors += 1; } if (count == 0 || count > width || (width%count != 0)) { fprintf(out, " ERROR: Repeat count not reasonable\n"); stub_errors += 1; } else if (width/count != width_of_nexus(nex_a)) { fprintf(out, " ERROR: Width of D is %u, expecting %u\n", width_of_nexus(nex_a), width/count); stub_errors += 1; } } static void show_lpm_shift(ivl_lpm_t net, const char*shift_dir) { ivl_nexus_t nex; unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_SHIFT%s %s: \n", shift_dir, ivl_lpm_basename(net), width, ivl_lpm_signed(net)? "" : "un"); nex = ivl_lpm_q(net); fprintf(out, " Q: %p\n", nex); if (width != width_of_nexus(nex)) { fprintf(out, " ERROR: Q output nexus width=%u " "does not match part width\n", width_of_nexus(nex)); stub_errors += 1; } nex = ivl_lpm_data(net, 0); fprintf(out, " D: %p\n", nex); if (width != width_of_nexus(nex)) { fprintf(out, " ERROR: Q output nexus width=%u " "does not match part width\n", width_of_nexus(nex)); stub_errors += 1; } nex = ivl_lpm_data(net, 1); fprintf(out, " S: %p \n", nex, width_of_nexus(nex)); } static void show_lpm_sign_ext(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); ivl_nexus_t nex_q = ivl_lpm_q(net); ivl_nexus_t nex_a = ivl_lpm_data(net,0); fprintf(out, " LPM_SIGN_EXT %s: \n", ivl_lpm_basename(net), width); fprintf(out, " Q: %p\n", nex_q); fprintf(out, " D: %p \n", nex_a, width_of_nexus(nex_a)); if (width != width_of_nexus(nex_q)) { fprintf(out, " ERROR: Width of Q is %u, expecting %u\n", width_of_nexus(nex_q), width); stub_errors += 1; } } static void show_lpm_sub(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_SUB %s: \n", ivl_lpm_basename(net), width); show_lpm_arithmetic_pins(net); } static void show_lpm_substitute(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); ivl_nexus_t nex_q = ivl_lpm_q(net); ivl_nexus_t nex_a = ivl_lpm_data(net,0); ivl_nexus_t nex_s = ivl_lpm_data(net,1); unsigned sbase = ivl_lpm_base(net); unsigned swidth = width_of_nexus(nex_s); fprintf(out, " LPM_SUBSTITUTE %s: \n", ivl_lpm_basename(net), width, sbase, swidth); fprintf(out, " Q: %p\n", nex_q); if (width != width_of_nexus(nex_q)) { fprintf(out, " ERROR: Width of Q is %u, expecting %u\n", width_of_nexus(nex_q), width); stub_errors += 1; } fprintf(out, " A: %p\n", nex_a); if (width != width_of_nexus(nex_a)) { fprintf(out, " ERROR: Width of A is %u, expecting %u\n", width_of_nexus(nex_a), width); stub_errors += 1; } fprintf(out, " S: %p\n", nex_s); if (sbase + swidth > width) { fprintf(out, " ERROR: S part is out of bounds\n"); stub_errors += 1; } } static void show_lpm_sfunc(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned ports = ivl_lpm_size(net); ivl_variable_type_t data_type = type_of_nexus(ivl_lpm_q(net)); ivl_nexus_t nex; unsigned idx; fprintf(out, " LPM_SFUNC %s: \n", ivl_lpm_basename(net), ivl_lpm_string(net), width, data_type_string(data_type), ports); nex = ivl_lpm_q(net); if (width != width_of_nexus(nex)) { fprintf(out, " ERROR: Q output nexus width=%u " " does not match part width\n", width_of_nexus(nex)); stub_errors += 1; } fprintf(out, " Q: %p\n", nex); for (idx = 0 ; idx < ports ; idx += 1) { nex = ivl_lpm_data(net, idx); fprintf(out, " D%u: %p \n", idx, nex, width_of_nexus(nex), data_type_string(type_of_nexus(nex))); } } static void show_lpm_delays(ivl_lpm_t net) { ivl_expr_t rise = ivl_lpm_delay(net, 0); ivl_expr_t fall = ivl_lpm_delay(net, 1); ivl_expr_t decay= ivl_lpm_delay(net, 2); if (rise==0 && fall==0 && decay==0) return; fprintf(out, " #DELAYS\n"); if (rise) show_expression(rise, 8); else fprintf(out, " ERROR: missing rise delay\n"); if (fall) show_expression(fall, 8); else fprintf(out, " ERROR: missing fall delay\n"); if (decay) show_expression(decay, 8); else fprintf(out, " ERROR: missing decay delay\n"); fprintf(out, " #END DELAYS\n"); } static void show_lpm_ufunc(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned ports = ivl_lpm_size(net); ivl_scope_t def = ivl_lpm_define(net); ivl_nexus_t nex; unsigned idx; fprintf(out, " LPM_UFUNC %s: \n", ivl_lpm_basename(net), ivl_scope_name(def), width, ports); show_lpm_delays(net); nex = ivl_lpm_q(net); if (width != width_of_nexus(nex)) { fprintf(out, " ERROR: Q output nexus width=%u " " does not match part width\n", width_of_nexus(nex)); stub_errors += 1; } fprintf(out, " Q: %p\n", nex); for (idx = 0 ; idx < ports ; idx += 1) { nex = ivl_lpm_data(net, idx); fprintf(out, " D%u: %p \n", idx, nex, width_of_nexus(nex)); } } static void show_lpm(ivl_lpm_t net) { switch (ivl_lpm_type(net)) { case IVL_LPM_ABS: show_lpm_abs(net); break; case IVL_LPM_ADD: show_lpm_add(net); break; case IVL_LPM_ARRAY: show_lpm_array(net); break; case IVL_LPM_CAST_INT: show_lpm_cast_int(net); break; case IVL_LPM_CAST_REAL: show_lpm_cast_real(net); break; case IVL_LPM_DIVIDE: show_lpm_divide(net); break; case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_NEE: show_lpm_cmp_eeq(net); break; case IVL_LPM_FF: show_lpm_ff(net); break; case IVL_LPM_CMP_GE: show_lpm_cmp_ge(net); break; case IVL_LPM_CMP_GT: show_lpm_cmp_gt(net); break; case IVL_LPM_CMP_NE: show_lpm_cmp_ne(net); break; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: show_lpm_concat(net); break; case IVL_LPM_RE_AND: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_XNOR: show_lpm_re(net); break; case IVL_LPM_SHIFTL: show_lpm_shift(net, "L"); break; case IVL_LPM_SIGN_EXT: show_lpm_sign_ext(net); break; case IVL_LPM_SHIFTR: show_lpm_shift(net, "R"); break; case IVL_LPM_SUB: show_lpm_sub(net); break; case IVL_LPM_SUBSTITUTE: show_lpm_substitute(net); break; case IVL_LPM_MOD: show_lpm_mod(net); break; case IVL_LPM_MULT: show_lpm_mult(net); break; case IVL_LPM_MUX: show_lpm_mux(net); break; case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: show_lpm_part(net); break; case IVL_LPM_REPEAT: show_lpm_repeat(net); break; case IVL_LPM_SFUNC: show_lpm_sfunc(net); break; case IVL_LPM_UFUNC: show_lpm_ufunc(net); break; default: fprintf(out, " LPM(%d) %s: \n", ivl_lpm_type(net), ivl_lpm_basename(net), ivl_lpm_width(net), ivl_lpm_signed(net)); } } static int show_process(ivl_process_t net, void*x) { unsigned idx; (void)x; /* Parameter is not used. */ switch (ivl_process_type(net)) { case IVL_PR_INITIAL: if (ivl_process_analog(net)) fprintf(out, "analog initial\n"); else fprintf(out, "initial\n"); break; case IVL_PR_ALWAYS: if (ivl_process_analog(net)) fprintf(out, "analog\n"); else fprintf(out, "always\n"); break; case IVL_PR_FINAL: if (ivl_process_analog(net)) fprintf(out, "analog final\n"); else fprintf(out, "final\n"); break; } for (idx = 0 ; idx < ivl_process_attr_cnt(net) ; idx += 1) { ivl_attribute_t attr = ivl_process_attr_val(net, idx); switch (attr->type) { case IVL_ATT_VOID: fprintf(out, " (* %s *)\n", attr->key); break; case IVL_ATT_STR: fprintf(out, " (* %s = \"%s\" *)\n", attr->key, attr->val.str); break; case IVL_ATT_NUM: fprintf(out, " (* %s = %ld *)\n", attr->key, attr->val.num); break; } } show_statement(ivl_process_stmt(net), 4); return 0; } static void show_parameter(ivl_parameter_t net) { const char*name = ivl_parameter_basename(net); fprintf(out, " parameter %s;\n", name); show_expression(ivl_parameter_expr(net), 7); } static void show_event(ivl_event_t net) { unsigned idx; fprintf(out, " event %s (%u pos, %u neg, %u any);\n", ivl_event_basename(net), ivl_event_npos(net), ivl_event_nneg(net), ivl_event_nany(net)); for (idx = 0 ; idx < ivl_event_nany(net) ; idx += 1) { ivl_nexus_t nex = ivl_event_any(net, idx); fprintf(out, " ANYEDGE: %p\n", nex); } for (idx = 0 ; idx < ivl_event_nneg(net) ; idx += 1) { ivl_nexus_t nex = ivl_event_neg(net, idx); fprintf(out, " NEGEDGE: %p\n", nex); } for (idx = 0 ; idx < ivl_event_npos(net) ; idx += 1) { ivl_nexus_t nex = ivl_event_pos(net, idx); fprintf(out, " POSEDGE: %p\n", nex); } } static const char* str_tab[8] = { "HiZ", "small", "medium", "weak", "large", "pull", "strong", "supply"}; /* * This function is used by the show_signal to dump a constant value * that is connected to the signal. While we are here, check that the * value is consistent with the signal itself. */ static void signal_nexus_const(ivl_signal_t sig, ivl_nexus_ptr_t ptr, ivl_net_const_t con) { const char*dr0 = str_tab[ivl_nexus_ptr_drive0(ptr)]; const char*dr1 = str_tab[ivl_nexus_ptr_drive1(ptr)]; const char*bits; unsigned idx, width = ivl_const_width(con); fprintf(out, " const-"); switch (ivl_const_type(con)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: bits = ivl_const_bits(con); assert(bits); for (idx = 0 ; idx < width ; idx += 1) { fprintf(out, "%c", bits[width-idx-1]); } break; case IVL_VT_REAL: fprintf(out, "%f", ivl_const_real(con)); break; default: fprintf(out, "????"); break; } fprintf(out, " (%s0, %s1, width=%u)\n", dr0, dr1, width); if (ivl_signal_width(sig) != width) { fprintf(out, "ERROR: Width of signal does not match " "width of connected constant vector.\n"); stub_errors += 1; } int drive_type_ok = check_signal_drive_type(ivl_signal_data_type(sig), ivl_const_type(con)); if (! drive_type_ok) { fprintf(out, "ERROR: Signal data type does not match" " literal type.\n"); stub_errors += 1; } } static void show_nexus_details(ivl_signal_t net, ivl_nexus_t nex) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_net_const_t con; ivl_net_logic_t logic; ivl_lpm_t lpm; ivl_signal_t sig; ivl_switch_t swt; ivl_branch_t bra; ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); const char*dr0 = str_tab[ivl_nexus_ptr_drive0(ptr)]; const char*dr1 = str_tab[ivl_nexus_ptr_drive1(ptr)]; if ((sig = ivl_nexus_ptr_sig(ptr))) { fprintf(out, " SIG %s word=%u (%s0, %s1)", ivl_signal_name(sig), ivl_nexus_ptr_pin(ptr), dr0, dr1); if (ivl_signal_width(sig) != ivl_signal_width(net)) { fprintf(out, " (ERROR: Width=%u)", ivl_signal_width(sig)); stub_errors += 1; } if (ivl_signal_data_type(sig) != ivl_signal_data_type(net)) { fprintf(out, " (ERROR: data type mismatch : %s vs. %s)", data_type_string(ivl_signal_data_type(sig)), data_type_string(ivl_signal_data_type(net))); stub_errors += 1; } fprintf(out, "\n"); } else if ((logic = ivl_nexus_ptr_log(ptr))) { fprintf(out, " LOG %s.%s[%u] (%s0, %s1)\n", ivl_scope_name(ivl_logic_scope(logic)), ivl_logic_basename(logic), ivl_nexus_ptr_pin(ptr), dr0, dr1); } else if ((lpm = ivl_nexus_ptr_lpm(ptr))) { fprintf(out, " LPM %s.%s (%s0, %s1)\n", ivl_scope_name(ivl_lpm_scope(lpm)), ivl_lpm_basename(lpm), dr0, dr1); } else if ((swt = ivl_nexus_ptr_switch(ptr))) { fprintf(out, " SWITCH %s.%s\n", ivl_scope_name(ivl_switch_scope(swt)), ivl_switch_basename(swt)); } else if ((con = ivl_nexus_ptr_con(ptr))) { signal_nexus_const(net, ptr, con); } else if ((bra = ivl_nexus_ptr_branch(ptr))) { fprintf(out, " BRANCH %p terminal %u\n", bra, ivl_nexus_ptr_pin(ptr)); } else { fprintf(out, " ?[%u] (%s0, %s1)\n", ivl_nexus_ptr_pin(ptr), dr0, dr1); } } } static void show_signal(ivl_signal_t net) { unsigned idx; const char*type = "?"; const char*port = ""; const char*sign = ivl_signal_signed(net)? "signed" : "unsigned"; switch (ivl_signal_type(net)) { case IVL_SIT_REG: type = "reg"; if (ivl_signal_integer(net)) type = "integer"; break; case IVL_SIT_TRI: type = "tri"; break; case IVL_SIT_TRI0: type = "tri0"; break; case IVL_SIT_TRI1: type = "tri1"; break; case IVL_SIT_UWIRE: type = "uwire"; break; default: break; } switch (ivl_signal_port(net)) { case IVL_SIP_INPUT: port = "input "; break; case IVL_SIP_OUTPUT: port = "output "; break; case IVL_SIP_INOUT: port = "inout "; break; case IVL_SIP_NONE: break; } const char*discipline_txt = "NONE"; if (ivl_signal_discipline(net)) { ivl_discipline_t dis = ivl_signal_discipline(net); discipline_txt = ivl_discipline_name(dis); } for (idx = 0 ; idx < ivl_signal_array_count(net) ; idx += 1) { ivl_nexus_t nex = ivl_signal_nex(net, idx); fprintf(out, " %s %s %s", type, sign, port); show_type_of_signal(net); fprintf(out, " %s[word=%u, adr=%d] ", ivl_signal_basename(net), idx, ivl_signal_array_base(net)+idx, ivl_signal_width(net), ivl_signal_local(net)? ", local":"", discipline_txt); if (nex == NULL) { fprintf(out, "nexus=\n"); continue; } else { fprintf(out, "nexus=%p\n", nex); } show_nexus_details(net, nex); } for (idx = 0 ; idx < ivl_signal_npath(net) ; idx += 1) { ivl_delaypath_t path = ivl_signal_path(net,idx); ivl_nexus_t nex = ivl_path_source(path); ivl_nexus_t con = ivl_path_condit(path); int posedge = ivl_path_source_posedge(path); int negedge = ivl_path_source_negedge(path); fprintf(out, " path %p", nex); if (posedge) fprintf(out, " posedge"); if (negedge) fprintf(out, " negedge"); if (con) fprintf(out, " (if %p)", con); else if (ivl_path_is_condit(path)) fprintf(out, " (ifnone)"); fprintf(out, " %" PRIu64 ",%" PRIu64 ",%" PRIu64 " %" PRIu64 ",%" PRIu64 ",%" PRIu64 " %" PRIu64 ",%" PRIu64 ",%" PRIu64 " %" PRIu64 ",%" PRIu64 ",%" PRIu64, ivl_path_delay(path, IVL_PE_01), ivl_path_delay(path, IVL_PE_10), ivl_path_delay(path, IVL_PE_0z), ivl_path_delay(path, IVL_PE_z1), ivl_path_delay(path, IVL_PE_1z), ivl_path_delay(path, IVL_PE_z0), ivl_path_delay(path, IVL_PE_0x), ivl_path_delay(path, IVL_PE_x1), ivl_path_delay(path, IVL_PE_1x), ivl_path_delay(path, IVL_PE_x0), ivl_path_delay(path, IVL_PE_xz), ivl_path_delay(path, IVL_PE_zx)); fprintf(out, " scope=%s\n", ivl_scope_name(ivl_path_scope(path))); } for (idx = 0 ; idx < ivl_signal_attr_cnt(net) ; idx += 1) { ivl_attribute_t atr = ivl_signal_attr_val(net, idx); switch (atr->type) { case IVL_ATT_STR: fprintf(out, " %s = %s\n", atr->key, atr->val.str); break; case IVL_ATT_NUM: fprintf(out, " %s = %ld\n", atr->key, atr->val.num); break; case IVL_ATT_VOID: fprintf(out, " %s\n", atr->key); break; } } switch (ivl_signal_data_type(net)) { case IVL_VT_NO_TYPE: case IVL_VT_VOID: fprintf(out, " ERROR: Invalid type for signal.\n"); stub_errors += 1; break; default: break; } if (ivl_signal_integer(net) && ivl_signal_type(net)!=IVL_SIT_REG) { fprintf(out, " ERROR: integers must be IVL_SIT_REG\n"); stub_errors += 1; } } void test_expr_is_delay(ivl_expr_t expr) { switch (ivl_expr_type(expr)) { case IVL_EX_ULONG: return; case IVL_EX_NUMBER: return; case IVL_EX_SIGNAL: return; default: break; } fprintf(out, " ERROR: Expression is not a suitable delay\n"); stub_errors += 1; } /* * All logic gates have inputs and outputs that match exactly in * width. For example, and AND gate with 4 bit inputs generates a 4 * bit output, and all the inputs are 4 bits. */ static void show_logic(ivl_net_logic_t net) { unsigned npins, idx; const char*name = ivl_logic_basename(net); ivl_drive_t drive0 = ivl_logic_drive0(net); ivl_drive_t drive1 = ivl_logic_drive1(net); switch (ivl_logic_type(net)) { case IVL_LO_AND: fprintf(out, " and %s", name); break; case IVL_LO_BUF: fprintf(out, " buf %s", name); break; case IVL_LO_BUFIF0: fprintf(out, " bufif0 %s", name); break; case IVL_LO_BUFIF1: fprintf(out, " bufif1 %s", name); break; case IVL_LO_BUFT: fprintf(out, " buft %s", name); break; case IVL_LO_BUFZ: fprintf(out, " bufz %s", name); break; case IVL_LO_CMOS: fprintf(out, " cmos %s", name); break; case IVL_LO_NAND: fprintf(out, " nand %s", name); break; case IVL_LO_NMOS: fprintf(out, " nmos %s", name); break; case IVL_LO_NOR: fprintf(out, " nor %s", name); break; case IVL_LO_NOT: fprintf(out, " not %s", name); break; case IVL_LO_NOTIF0: fprintf(out, " notif0 %s", name); break; case IVL_LO_NOTIF1: fprintf(out, " notif1 %s", name); break; case IVL_LO_OR: fprintf(out, " or %s", name); break; case IVL_LO_PMOS: fprintf(out, " pmos %s", name); break; case IVL_LO_PULLDOWN: fprintf(out, " pulldown %s", name); break; case IVL_LO_PULLUP: fprintf(out, " pullup %s", name); break; case IVL_LO_RCMOS: fprintf(out, " rcmos %s", name); break; case IVL_LO_RNMOS: fprintf(out, " rnmos %s", name); break; case IVL_LO_RPMOS: fprintf(out, " rpmos %s", name); break; case IVL_LO_XNOR: fprintf(out, " xnor %s", name); break; case IVL_LO_XOR: fprintf(out, " xor %s", name); break; case IVL_LO_UDP: fprintf(out, " primitive<%s> %s", ivl_udp_name(ivl_logic_udp(net)), name); break; default: fprintf(out, " unsupported gate %s", ivl_logic_type(net), name); break; } fprintf(out, " \n", ivl_logic_width(net)); fprintf(out, " \n"); if (ivl_logic_delay(net,0)) { test_expr_is_delay(ivl_logic_delay(net,0)); show_expression(ivl_logic_delay(net,0), 6); } if (ivl_logic_delay(net,1)) { test_expr_is_delay(ivl_logic_delay(net,1)); show_expression(ivl_logic_delay(net,1), 6); } if (ivl_logic_delay(net,2)) { test_expr_is_delay(ivl_logic_delay(net,2)); show_expression(ivl_logic_delay(net,2), 6); } npins = ivl_logic_pins(net); /* Show the pins of the gate. Pin-0 is always the output, and the remaining pins are the inputs. Inputs may be unconnected, but if connected the nexus width must exactly match the gate width. */ for (idx = 0 ; idx < npins ; idx += 1) { ivl_nexus_t nex = ivl_logic_pin(net, idx); fprintf(out, " %u: %p", idx, nex); if (idx == 0) fprintf(out, " ", drive0, drive1); fprintf(out, "\n"); if (nex == 0) { if (idx == 0) { fprintf(out, " 0: ERROR: Pin 0 must not " "be unconnected\n"); stub_errors += 1; } continue; } if (ivl_logic_width(net) != width_of_nexus(nex)) { fprintf(out, " %u: ERROR: Nexus width is %u\n", idx, width_of_nexus(nex)); stub_errors += 1; } } /* If this is an instance of a UDP, then check that the instantiation is consistent with the definition. */ if (ivl_logic_type(net) == IVL_LO_UDP) { ivl_udp_t udp = ivl_logic_udp(net); if (npins != 1+ivl_udp_nin(udp)) { fprintf(out, " ERROR: UDP %s expects %u inputs\n", ivl_udp_name(udp), ivl_udp_nin(udp)); stub_errors += 1; } /* Add a reference to this udp definition. */ reference_udp_definition(udp); } npins = ivl_logic_attr_cnt(net); for (idx = 0 ; idx < npins ; idx += 1) { ivl_attribute_t cur = ivl_logic_attr_val(net,idx); switch (cur->type) { case IVL_ATT_VOID: fprintf(out, " %s\n", cur->key); break; case IVL_ATT_NUM: fprintf(out, " %s = %ld\n", cur->key, cur->val.num); break; case IVL_ATT_STR: fprintf(out, " %s = %s\n", cur->key, cur->val.str); break; } } } static int show_scope(ivl_scope_t net, void*x) { unsigned idx; const char *is_auto; (void)x; /* Parameter is not used. */ fprintf(out, "scope: %s (%u parameters, %u signals, %u logic)", ivl_scope_name(net), ivl_scope_params(net), ivl_scope_sigs(net), ivl_scope_logs(net)); is_auto = ivl_scope_is_auto(net) ? "automatic " : ""; switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: fprintf(out, " module %s%s", ivl_scope_tname(net), ivl_scope_is_cell(net) ? " (cell)" : ""); break; case IVL_SCT_FUNCTION: fprintf(out, " function %s%s", is_auto, ivl_scope_tname(net)); break; case IVL_SCT_BEGIN: fprintf(out, " begin : %s", ivl_scope_tname(net)); break; case IVL_SCT_FORK: fprintf(out, " fork : %s", ivl_scope_tname(net)); break; case IVL_SCT_TASK: fprintf(out, " task %s%s", is_auto, ivl_scope_tname(net)); break; case IVL_SCT_CLASS: fprintf(out, " class %s", ivl_scope_tname(net)); break; default: fprintf(out, " type(%d) %s", ivl_scope_type(net), ivl_scope_tname(net)); break; } fprintf(out, " time units = 1e%d\n", ivl_scope_time_units(net)); fprintf(out, " time precision = 1e%d\n", ivl_scope_time_precision(net)); for (idx = 0 ; idx < ivl_scope_attr_cnt(net) ; idx += 1) { ivl_attribute_t attr = ivl_scope_attr_val(net, idx); switch (attr->type) { case IVL_ATT_VOID: fprintf(out, " (* %s *)\n", attr->key); break; case IVL_ATT_STR: fprintf(out, " (* %s = \"%s\" *)\n", attr->key, attr->val.str); break; case IVL_ATT_NUM: fprintf(out, " (* %s = %ld *)\n", attr->key, attr->val.num); break; } } for (idx = 0 ; idx < ivl_scope_classes(net) ; idx += 1) show_class(ivl_scope_class(net, idx)); for (idx = 0 ; idx < ivl_scope_params(net) ; idx += 1) show_parameter(ivl_scope_param(net, idx)); for (idx = 0 ; idx < ivl_scope_enumerates(net) ; idx += 1) show_enumerate(ivl_scope_enumerate(net, idx)); for (idx = 0 ; idx < ivl_scope_sigs(net) ; idx += 1) show_signal(ivl_scope_sig(net, idx)); for (idx = 0 ; idx < ivl_scope_events(net) ; idx += 1) show_event(ivl_scope_event(net, idx)); for (idx = 0 ; idx < ivl_scope_logs(net) ; idx += 1) show_logic(ivl_scope_log(net, idx)); for (idx = 0 ; idx < ivl_scope_lpms(net) ; idx += 1) show_lpm(ivl_scope_lpm(net, idx)); for (idx = 0 ; idx < ivl_scope_switches(net) ; idx += 1) show_switch(ivl_scope_switch(net, idx)); switch (ivl_scope_type(net)) { case IVL_SCT_FUNCTION: case IVL_SCT_TASK: fprintf(out, " scope function/task definition\n"); if (ivl_scope_def(net) == 0) { fprintf(out, " ERROR: scope missing required task definition\n"); stub_errors += 1; } else { show_statement(ivl_scope_def(net), 6); } break; default: if (ivl_scope_def(net)) { fprintf(out, " ERROR: scope has an attached task definition:\n"); show_statement(ivl_scope_def(net), 6); stub_errors += 1; } break; } fprintf(out, "end scope %s\n", ivl_scope_name(net)); return ivl_scope_children(net, show_scope, 0); } static void show_primitive(ivl_udp_t net, unsigned ref_count) { unsigned rdx; fprintf(out, "primitive %s (referenced %u times)\n", ivl_udp_name(net), ref_count); if (ivl_udp_sequ(net)) fprintf(out, " reg out = %c;\n", ivl_udp_init(net)); else fprintf(out, " wire out;\n"); fprintf(out, " table\n"); for (rdx = 0 ; rdx < ivl_udp_rows(net) ; rdx += 1) { /* Each row has the format: Combinational: iii...io Sequential: oiii...io In the sequential case, the o value in the front is the current output value. */ unsigned idx; const char*row = ivl_udp_row(net,rdx); fprintf(out, " "); if (ivl_udp_sequ(net)) fprintf(out, " cur=%c :", *row++); for (idx = 0 ; idx < ivl_udp_nin(net) ; idx += 1) fprintf(out, " %c", *row++); fprintf(out, " : out=%c\n", *row++); } fprintf(out, " endtable\n"); fprintf(out, "endprimitive\n"); } int target_design(ivl_design_t des) { ivl_scope_t*root_scopes; unsigned nroot = 0; unsigned idx; const char*path = ivl_design_flag(des, "-o"); if (path == 0) { return -1; } out = fopen(path, "w"); if (out == 0) { perror(path); return -2; } for (idx = 0 ; idx < ivl_design_disciplines(des) ; idx += 1) { ivl_discipline_t dis = ivl_design_discipline(des,idx); fprintf(out, "discipline %s\n", ivl_discipline_name(dis)); } ivl_design_roots(des, &root_scopes, &nroot); for (idx = 0 ; idx < nroot ; idx += 1) { ivl_scope_t cur = root_scopes[idx]; switch (ivl_scope_type(cur)) { case IVL_SCT_TASK: fprintf(out, "task = %s\n", ivl_scope_name(cur)); break; case IVL_SCT_FUNCTION: fprintf(out, "function = %s\n", ivl_scope_name(cur)); break; case IVL_SCT_CLASS: fprintf(out, "class = %s\n", ivl_scope_name(cur)); break; case IVL_SCT_PACKAGE: fprintf(out, "package = %s\n", ivl_scope_name(cur)); break; case IVL_SCT_MODULE: fprintf(out, "root module = %s\n", ivl_scope_name(cur)); break; default: fprintf(out, "ERROR scope %s unknown type\n", ivl_scope_name(cur)); stub_errors += 1; break; } show_scope(root_scopes[idx], 0); } while (udp_define_list) { struct udp_define_cell*cur = udp_define_list; udp_define_list = cur->next; show_primitive(cur->udp, cur->ref); free(cur); } fprintf(out, "# There are %u constants detected\n", ivl_design_consts(des)); for (idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) { ivl_net_const_t con = ivl_design_const(des, idx); show_constant(con); } ivl_design_process(des, show_process, 0); fclose(out); return stub_errors; } const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } iverilog-10_1/tgt-stub/stub.conf000066400000000000000000000000611265551621300167340ustar00rootroot00000000000000functor:cprop functor:nodangle flag:DLL=stub.tgt iverilog-10_1/tgt-stub/switches.c000066400000000000000000000110411265551621300171050ustar00rootroot00000000000000/* * Copyright (c) 2008-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include # include # include void show_switch(ivl_switch_t net) { const char*name = ivl_switch_basename(net); int has_enable = 0; ivl_nexus_t nexa, nexb; ivl_variable_type_t nex_type_a, nex_type_b; switch (ivl_switch_type(net)) { case IVL_SW_TRAN: fprintf(out, " tran %s", name); break; case IVL_SW_RTRAN: fprintf(out, " rtran %s", name); break; case IVL_SW_TRANIF0: fprintf(out, " tranif0 %s", name); has_enable = 1; break; case IVL_SW_RTRANIF0: fprintf(out, " rtranif0 %s", name); has_enable = 1; break; case IVL_SW_TRANIF1: fprintf(out, " tranif1 %s", name); has_enable = 1; break; case IVL_SW_RTRANIF1: fprintf(out, " rtranif1 %s", name); has_enable = 1; break; case IVL_SW_TRAN_VP: fprintf(out, " tran(VP wid=%u, part=%u, off=%u) %s", ivl_switch_width(net), ivl_switch_part(net), ivl_switch_offset(net), name); break; } fprintf(out, " island=%p\n", ivl_switch_island(net)); fprintf(out, " \n"); if (ivl_switch_delay(net,0)) { test_expr_is_delay(ivl_switch_delay(net,0)); show_expression(ivl_switch_delay(net,0), 6); } if (ivl_switch_delay(net,1)) { test_expr_is_delay(ivl_switch_delay(net,1)); show_expression(ivl_switch_delay(net,1), 6); } if (ivl_switch_delay(net,2)) { test_expr_is_delay(ivl_switch_delay(net,2)); show_expression(ivl_switch_delay(net,2), 6); } nexa = ivl_switch_a(net); nex_type_a = nexa? type_of_nexus(nexa) : IVL_VT_NO_TYPE; fprintf(out, " A: %p \n", nexa, data_type_string(nex_type_a)); nexb = ivl_switch_b(net); nex_type_b = nexb? type_of_nexus(nexb) : IVL_VT_NO_TYPE; fprintf(out, " B: %p \n", nexb, data_type_string(nex_type_b)); /* The A/B pins of the switch must be present, and must match. */ if (nex_type_a == IVL_VT_NO_TYPE) { fprintf(out, " A: ERROR: Type missing for pin A\n"); stub_errors += 1; } if (nex_type_b == IVL_VT_NO_TYPE) { fprintf(out, " B: ERROR: Type missing for pin B\n"); stub_errors += 1; } if (nex_type_a != nex_type_b) { fprintf(out, " A/B: ERROR: Type mismatch between pins A and B\n"); stub_errors += 1; } if (ivl_switch_type(net) == IVL_SW_TRAN_VP) { /* The TRAN_VP nodes are special in that the specific width matters for each port and should be exactly right for both. */ if (width_of_nexus(nexa) != ivl_switch_width(net)) { fprintf(out, " A: ERROR: part vector nexus " "width=%u, expecting width=%u\n", width_of_nexus(nexa), ivl_switch_width(net)); stub_errors += 1; } if (width_of_nexus(nexb) != ivl_switch_part(net)) { fprintf(out, " B: ERROR: part select nexus " "width=%u, expecting width=%u\n", width_of_nexus(nexb), ivl_switch_part(net)); stub_errors += 1; } } else { /* All other TRAN nodes will have matching vector widths, but the actual value doesn't matter. */ if (width_of_nexus(nexa) != width_of_nexus(nexb)) { fprintf(out, " A/B: ERROR: Width of ports don't match" ": A=%u, B=%u\n", width_of_nexus(nexa), width_of_nexus(nexb)); stub_errors += 1; } } if (has_enable) { ivl_nexus_t nexe = ivl_switch_enable(net); ivl_variable_type_t nexe_type = type_of_nexus(nexe); fprintf(out, " E: %p \n", nexe, data_type_string(nexe_type)); if (width_of_nexus(nexe) != 1) { fprintf(out, " E: ERROR: Nexus width is %u\n", width_of_nexus(nexe)); } } } iverilog-10_1/tgt-stub/types.c000066400000000000000000000077531265551621300164370ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "priv.h" # include static void show_net_type_darray(ivl_type_t net_type) { /* Dynamic arrays have a single element type. */ ivl_type_t element_type = ivl_type_element(net_type); fprintf(out, "darray of "); show_net_type(element_type); } static void show_net_type_queue(ivl_type_t net_type) { /* Dynamic arrays have a single element type. */ ivl_type_t element_type = ivl_type_element(net_type); fprintf(out, "queue of "); show_net_type(element_type); } void show_net_type(ivl_type_t net_type) { ivl_variable_type_t data_type = ivl_type_base(net_type); switch (data_type) { case IVL_VT_NO_TYPE: fprintf(out, ""); stub_errors += 1; break; case IVL_VT_BOOL: fprintf(out, "bool"); break; case IVL_VT_LOGIC: fprintf(out, "logic"); break; case IVL_VT_REAL: fprintf(out, "real"); break; case IVL_VT_STRING: fprintf(out, "string"); break; case IVL_VT_DARRAY: show_net_type_darray(net_type); break; case IVL_VT_CLASS: fprintf(out, "class"); break; case IVL_VT_QUEUE: show_net_type_queue(net_type); break; case IVL_VT_VOID: fprintf(out, "void"); break; } unsigned packed_dimensions = ivl_type_packed_dimensions(net_type); unsigned idx; for (idx = 0 ; idx < packed_dimensions ; idx += 1) { fprintf(out, "[%d:%d]", ivl_type_packed_msb(net_type, idx), ivl_type_packed_lsb(net_type, idx)); } } void show_type_of_signal(ivl_signal_t net) { unsigned dim; /* The data_type is the base type of the signal. This the the starting point for the type. In the long run I think I want to remove this in favor of the ivl_signal_net_type below. */ ivl_variable_type_t data_type = ivl_signal_data_type(net); /* This gets the more general type description. This is a newer form so doesn't yet handle all the cases. Newer types, such DARRAY types, REQUIRE this method to get at the type details. */ ivl_type_t net_type = ivl_signal_net_type(net); if (net_type) { show_net_type(net_type); return; } switch (data_type) { case IVL_VT_NO_TYPE: fprintf(out, ""); break; case IVL_VT_BOOL: fprintf(out, "bool"); break; case IVL_VT_LOGIC: fprintf(out, "logic"); break; case IVL_VT_REAL: fprintf(out, "real"); break; case IVL_VT_STRING: fprintf(out, "string"); break; case IVL_VT_DARRAY: /* The DARRAY type MUST be described by an ivl_signal_net_type object. */ fprintf(out, "ERROR-DARRAY"); stub_errors += 1; break; case IVL_VT_QUEUE: /* The QUEUE type MUST be described by an ivl_signal_net_type object. */ fprintf(out, "ERROR-QUEUE"); stub_errors += 1; break; case IVL_VT_VOID: fprintf(out, "void"); break; case IVL_VT_CLASS: fprintf(out, "class"); break; } for (dim = 0 ; dim < ivl_signal_packed_dimensions(net) ; dim += 1) { fprintf(out, "[%d:%d]", ivl_signal_packed_msb(net,dim), ivl_signal_packed_lsb(net,dim)); } } iverilog-10_1/tgt-verilog/000077500000000000000000000000001265551621300156055ustar00rootroot00000000000000iverilog-10_1/tgt-verilog/Makefile.in000066400000000000000000000044621265551621300176600ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = @ident_support@ $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = verilog.o all: dep verilog.tgt check: all clean: rm -rf *.o dep verilog.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-verilog/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -MD -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS= -L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif verilog.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl/verilog.tgt \ $(includedir)/vpi_user.h $(libdir)/ivl/verilog.tgt: ./verilog.tgt $(INSTALL_PROGRAM) ./verilog.tgt "$(DESTDIR)$(libdir)/ivl/verilog.tgt" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl/verilog.tgt" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-verilog/cppcheck.sup000066400000000000000000000002211265551621300201110ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:verilog.c:408 iverilog-10_1/tgt-verilog/verilog.c000066400000000000000000000245711265551621300174310ustar00rootroot00000000000000/* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" /* * This target writes a Verilog description of the design. The output * Verilog is a single module that has the name of the root module of * the design, but is internally the complete design. */ # include "ivl_target.h" # include # include /* This is the output file where the Verilog program is sent. */ static FILE*out; /* * Scoped objects are the signals, reg and wire and what-not. What * this function does is draw the objects of the scope, along with a * fake scope context so that the hierarchical name remains * pertinent. */ static void draw_scoped_objects(ivl_design_t des) { ivl_scope_t root = ivl_design_root(des); unsigned cnt, idx; cnt = ivl_scope_sigs(root); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(root, idx); switch (ivl_signal_type(sig)) { case IVL_SIT_REG: if (ivl_signal_pins(sig) > 1) fprintf(out, " reg [%u:0] %s;\n", ivl_signal_pins(sig), ivl_signal_basename(sig)); else fprintf(out, " reg %s;\n", ivl_signal_basename(sig)); break; case IVL_SIT_TRI: fprintf(out, " wire %s;\n", ivl_signal_basename(sig)); break; default: assert(0); } } } /* * Given a nexus, this function draws a signal reference. We don't * care really whether the signal is a reg or wire, because this may * be an input or output of a gate. Just print it. And if this is a * bit of a vector, draw the bit select needed to get at the right bit. */ static void draw_nexus(ivl_nexus_t nex) { ivl_signal_t sig=NULL; ivl_nexus_ptr_t ptr=NULL; unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ptr = ivl_nexus_ptr(nex, idx); sig = ivl_nexus_ptr_sig(ptr); if (sig) break; } assert(sig); if (ivl_signal_pins(sig) == 1) { fprintf(out, "%s", ivl_signal_name(sig)); } else { fprintf(out, "%s[%u]", ivl_signal_name(sig), ivl_nexus_ptr_pin(ptr)); } } /* * Draw a single logic gate. Escape the name so that it is preserved * completely. This drawing is happening in the root scope so signal * references can remain hierarchical. */ static int draw_logic(ivl_net_logic_t net) { unsigned npins, idx; const char*name = ivl_logic_name(net); switch (ivl_logic_type(net)) { case IVL_LO_AND: fprintf(out, " and \\%s (", name); break; case IVL_LO_BUF: fprintf(out, " buf \\%s (", name); break; case IVL_LO_OR: fprintf(out, " or \\%s (", name); break; case IVL_LO_XOR: fprintf(out, " xor \\%s (", name); break; default: fprintf(out, "STUB: %s: unsupported gate\n", name); return -1; } draw_nexus(ivl_logic_pin(net, 0)); npins = ivl_logic_pins(net); for (idx = 1 ; idx < npins ; idx += 1) { fprintf(out, ", "); draw_nexus(ivl_logic_pin(net,idx)); } fprintf(out, ");\n"); return 0; } /* * Scan the scope and its children for logic gates. Use the draw_logic * function to draw the actual gate. */ static int draw_scope_logic(ivl_scope_t scope, void*x) { unsigned cnt = ivl_scope_logs(scope); unsigned idx; for (idx = 0 ; idx < cnt ; idx += 1) { draw_logic(ivl_scope_log(scope, idx)); } ivl_scope_children(scope, draw_scope_logic, 0); return 0; } static void show_expression(ivl_expr_t net) { if (net == 0) return; switch (ivl_expr_type(net)) { case IVL_EX_BINARY: { char code = ivl_expr_opcode(net); show_expression(ivl_expr_oper1(net)); switch (code) { case 'e': fprintf(out, "=="); break; case 'n': fprintf(out, "!="); break; case 'N': fprintf(out, "!=="); break; case 'r': fprintf(out, ">>"); break; default: fprintf(out, "%c", code); } show_expression(ivl_expr_oper2(net)); break; } case IVL_EX_CONCAT: { unsigned idx; fprintf(out, "{"); show_expression(ivl_expr_parm(net, 0)); for (idx = 1 ; idx < ivl_expr_parms(net) ; idx += 1) { fprintf(out, ", "); show_expression(ivl_expr_parm(net, idx)); } fprintf(out, "}"); break; } case IVL_EX_NUMBER: { int sigflag = ivl_expr_signed(net); unsigned idx, width = ivl_expr_width(net); const char*bits = ivl_expr_bits(net); fprintf(out, "%u'%sb", width, sigflag? "s" : ""); for (idx = width ; idx > 0 ; idx -= 1) fprintf(out, "%c", bits[idx-1]); break; } case IVL_EX_SFUNC: fprintf(out, "%s", ivl_expr_name(net)); break; case IVL_EX_STRING: fprintf(out, "\"%s\"", ivl_expr_string(net)); break; case IVL_EX_SIGNAL: fprintf(out, "%s", ivl_expr_name(net)); break; default: fprintf(out, "..."); } } /* * An assignment is one of a possible list of l-values to a behavioral * assignment. Each l-value is either a part select of a signal or a * non-constant bit select. */ static void show_assign_lval(ivl_lval_t lval) { ivl_nexus_t nex; ivl_signal_t sig=NULL; unsigned idx; unsigned lsb=0; assert(ivl_lval_mux(lval) == 0); assert(ivl_lval_mem(lval) == 0); nex = ivl_lval_pin(lval, 0); for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { unsigned pin; ivl_nexus_ptr_t ptr; ptr = ivl_nexus_ptr(nex, idx); sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; lsb = ivl_nexus_ptr_pin(ptr); for (pin = 1 ; pin < ivl_lval_pins(lval) ; pin += 1) { if (ivl_signal_pin(sig, lsb+pin) != ivl_lval_pin(lval,pin)) break; } if (pin < ivl_lval_pins(lval)) continue; break; } assert(sig); if ((lsb > 0) || (lsb + ivl_lval_pins(lval)) < ivl_signal_pins(sig)) { fprintf(out, "%s[%u:%u]", ivl_signal_name(sig), lsb+ivl_lval_pins(lval)-1, lsb); } else { fprintf(out, "%s", ivl_signal_name(sig)); } } static void show_assign_lvals(ivl_statement_t net) { const unsigned cnt = ivl_stmt_lvals(net); if (cnt == 1) { show_assign_lval(ivl_stmt_lval(net, 0)); } else { unsigned idx; fprintf(out, "{"); show_assign_lval(ivl_stmt_lval(net, 0)); for (idx = 1 ; idx < cnt ; idx += 1) { fprintf(out, ", "); show_assign_lval(ivl_stmt_lval(net, idx)); } fprintf(out, "}"); } } static void show_statement(ivl_statement_t net, unsigned ind) { const ivl_statement_type_t code = ivl_statement_type(net); switch (code) { case IVL_ST_ASSIGN: fprintf(out, "%*s", ind, ""); show_assign_lvals(net); fprintf(out, " = "); show_expression(ivl_stmt_rval(net)); fprintf(out, ";\n"); break; case IVL_ST_BLOCK: { unsigned cnt = ivl_stmt_block_count(net); unsigned idx; fprintf(out, "%*sbegin\n", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); show_statement(cur, ind+4); } fprintf(out, "%*send\n", ind, ""); break; } case IVL_ST_CONDIT: { ivl_statement_t t = ivl_stmt_cond_true(net); ivl_statement_t f = ivl_stmt_cond_false(net); fprintf(out, "%*sif (", ind, ""); show_expression(ivl_stmt_cond_expr(net)); fprintf(out, ")\n"); if (t) show_statement(t, ind+4); else fprintf(out, "%*s;\n", ind+4, ""); if (f) { fprintf(out, "%*selse\n", ind, ""); show_statement(f, ind+4); } break; } case IVL_ST_DELAY: fprintf(out, "%*s#%lu\n", ind, "", ivl_stmt_delay_val(net)); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; case IVL_ST_NOOP: fprintf(out, "%*s/* noop */;\n", ind, ""); break; case IVL_ST_STASK: if (ivl_stmt_parm_count(net) == 0) { fprintf(out, "%*s%s;\n", ind, "", ivl_stmt_name(net)); } else { unsigned idx; fprintf(out, "%*s%s(", ind, "", ivl_stmt_name(net)); show_expression(ivl_stmt_parm(net, 0)); for (idx = 1 ; idx < ivl_stmt_parm_count(net) ; idx += 1) { fprintf(out, ", "); show_expression(ivl_stmt_parm(net, idx)); } fprintf(out, ");\n"); } break; case IVL_ST_WAIT: fprintf(out, "%*s@(...)\n", ind, ""); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; case IVL_ST_WHILE: fprintf(out, "%*swhile ()\n", ind, ""); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; default: fprintf(out, "%*sunknown statement type (%d)\n", ind, "", code); } } /* * Processes are all collected by ivl and I draw them here in the root * scope. This way, I don't need to do anything about scope * references. */ static int show_process(ivl_process_t net, void*x) { switch (ivl_process_type(net)) { case IVL_PR_INITIAL: fprintf(out, " initial\n"); break; case IVL_PR_ALWAYS: fprintf(out, " always\n"); break; case IVL_PR_FINAL: fprintf(out, " final\n"); break; } show_statement(ivl_process_stmt(net), 8); return 0; } int target_design(ivl_design_t des) { const char*path = ivl_design_flag(des, "-o"); if (path == 0) { return -1; } out = fopen(path, "w"); if (out == 0) { perror(path); return -2; } fprintf(out, "module %s;\n", ivl_scope_name(ivl_design_root(des))); /* Declare all the signals. */ draw_scoped_objects(des); /* Declare logic gates. */ draw_scope_logic(ivl_design_root(des), 0); /* Write out processes. */ ivl_design_process(des, show_process, 0); fprintf(out, "endmodule\n"); fclose(out); return 0; } iverilog-10_1/tgt-vhdl/000077500000000000000000000000001265551621300150735ustar00rootroot00000000000000iverilog-10_1/tgt-vhdl/Makefile.in000066400000000000000000000061111265551621300171370ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ O = vhdl.o state.o vhdl_element.o vhdl_type.o vhdl_syntax.o scope.o process.o \ stmt.o expr.o lpm.o support.o cast.o logic.o all: dep vhdl.tgt vhdl.conf vhdl-s.conf check: all clean: rm -rf $(O) dep vhdl.tgt distclean: clean rm -f Makefile config.log rm -f stamp-vhdl_config-h vhdl_config.h cppcheck: $(O:.o=.cc) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-vhdl/$@ dep: mkdir dep %.o: %.cc vhdl_config.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif vhdl.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) stamp-vhdl_config-h: $(srcdir)/vhdl_config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=tgt-vhdl/vhdl_config.h vhdl_config.h: stamp-vhdl_config-h install: all installdirs $(libdir)/ivl$(suffix)/vhdl.tgt $(libdir)/ivl$(suffix)/vhdl.conf \ $(libdir)/ivl$(suffix)/vhdl-s.conf $(libdir)/ivl$(suffix)/vhdl.tgt: ./vhdl.tgt $(INSTALL_PROGRAM) ./vhdl.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.tgt" $(libdir)/ivl$(suffix)/vhdl.conf: vhdl.conf $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.conf" $(libdir)/ivl$(suffix)/vhdl-s.conf: vhdl-s.conf $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.tgt" "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.conf" "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-vhdl/cast.cc000066400000000000000000000232251265551621300163400ustar00rootroot00000000000000/* * Generate code to convert between VHDL types. * * Copyright (C) 2008-2012 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_syntax.hh" #include "vhdl_target.h" #include "support.hh" #include #include vhdl_expr *vhdl_expr::cast(const vhdl_type *to) { #if 0 std::cout << "Cast: from=" << type_->get_string() << " (" << type_->get_width() << ") " << " to=" << to->get_string() << " (" << to->get_width() << ")" << std::endl; #endif // If this expression hasn't been given a type then // we can't generate any type conversion code if (NULL == type_) return this; if (to->get_name() == type_->get_name()) { if (to->get_width() == type_->get_width()) return this; // Identical else return resize(to->get_width()); } else { switch (to->get_name()) { case VHDL_TYPE_BOOLEAN: return to_boolean(); case VHDL_TYPE_INTEGER: return to_integer(); case VHDL_TYPE_UNSIGNED: case VHDL_TYPE_SIGNED: case VHDL_TYPE_STD_LOGIC_VECTOR: return to_vector(to->get_name(), to->get_width()); case VHDL_TYPE_STD_LOGIC: return to_std_logic(); case VHDL_TYPE_STD_ULOGIC: return to_std_ulogic(); case VHDL_TYPE_STRING: return to_string(); default: assert(false); } } assert(false); return NULL; } /* * Generate code to cast an expression to a vector type (std_logic_vector, * signed, unsigned). */ vhdl_expr *vhdl_expr::to_vector(vhdl_type_name_t name, int w) { if (type_->get_name() == VHDL_TYPE_STD_LOGIC) { vhdl_expr *others = w == 1 ? NULL : new vhdl_const_bit('0'); vhdl_bit_spec_expr *bs = new vhdl_bit_spec_expr(new vhdl_type(name, w - 1, 0), others); bs->add_bit(0, this); return bs; } else { // We have to cast the expression before resizing or the // wrong sign bit may be extended (i.e. when casting between // signed/unsigned *and* resizing) vhdl_type *t = new vhdl_type(name, w - 1, 0); vhdl_fcall *conv = new vhdl_fcall(t->get_string().c_str(), t); conv->add_expr(this); if (w != type_->get_width()) return conv->resize(w); else return conv; } } /* * Convert a generic expression to an Integer. */ vhdl_expr *vhdl_expr::to_integer() { vhdl_fcall *conv; if (type_->get_name() == VHDL_TYPE_STD_LOGIC) { require_support_function(SF_LOGIC_TO_INTEGER); conv = new vhdl_fcall(support_function::function_name(SF_LOGIC_TO_INTEGER), vhdl_type::integer()); } else conv = new vhdl_fcall("To_Integer", vhdl_type::integer()); conv->add_expr(this); return conv; } vhdl_expr *vhdl_expr::to_string() { bool numeric = type_->get_name() == VHDL_TYPE_UNSIGNED || type_->get_name() == VHDL_TYPE_SIGNED; if (numeric) { vhdl_fcall *image = new vhdl_fcall("integer'image", vhdl_type::string()); image->add_expr(this->cast(vhdl_type::integer())); return image; } else { // Assume type'image exists vhdl_fcall *image = new vhdl_fcall(type_->get_string() + "'image", vhdl_type::string()); image->add_expr(this); return image; } } /* * Convert a generic expression to a Boolean. */ vhdl_expr *vhdl_expr::to_boolean() { if (type_->get_name() == VHDL_TYPE_STD_LOGIC) { // '1' is true all else are false vhdl_const_bit *one = new vhdl_const_bit('1'); return new vhdl_binop_expr (this, VHDL_BINOP_EQ, one, vhdl_type::boolean()); } else if (type_->get_name() == VHDL_TYPE_UNSIGNED) { // Need to use a support function for this conversion require_support_function(SF_UNSIGNED_TO_BOOLEAN); vhdl_fcall *conv = new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_BOOLEAN), vhdl_type::boolean()); conv->add_expr(this); return conv; } else if (type_->get_name() == VHDL_TYPE_SIGNED) { require_support_function(SF_SIGNED_TO_BOOLEAN); vhdl_fcall *conv = new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_BOOLEAN), vhdl_type::boolean()); conv->add_expr(this); return conv; } assert(false); return NULL; } /* * Generate code to convert and expression to std_logic. */ vhdl_expr *vhdl_expr::to_std_logic() { if (type_->get_name() == VHDL_TYPE_BOOLEAN) { require_support_function(SF_BOOLEAN_TO_LOGIC); vhdl_fcall *ah = new vhdl_fcall(support_function::function_name(SF_BOOLEAN_TO_LOGIC), vhdl_type::std_logic()); ah->add_expr(this); return ah; } else if (type_->get_name() == VHDL_TYPE_SIGNED) { require_support_function(SF_SIGNED_TO_LOGIC); vhdl_fcall *ah = new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_LOGIC), vhdl_type::std_logic()); ah->add_expr(this); return ah; } else if (type_->get_name() == VHDL_TYPE_UNSIGNED) { require_support_function(SF_UNSIGNED_TO_LOGIC); vhdl_fcall *ah = new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_LOGIC), vhdl_type::std_logic()); ah->add_expr(this); return ah; } assert(false); return NULL; } vhdl_expr *vhdl_expr::to_std_ulogic() { if (type_->get_name() == VHDL_TYPE_STD_LOGIC) { vhdl_fcall *f = new vhdl_fcall("std_logic", vhdl_type::std_logic()); f->add_expr(this); return f; } else assert(false); return NULL; } /* * Change the width of a signed/unsigned type. */ vhdl_expr *vhdl_expr::resize(int newwidth) { vhdl_type *rtype; assert(type_); if (type_->get_name() == VHDL_TYPE_SIGNED) rtype = vhdl_type::nsigned(newwidth); else if (type_->get_name() == VHDL_TYPE_UNSIGNED) rtype = vhdl_type::nunsigned(newwidth); else if (type_->get_name() == VHDL_TYPE_STD_LOGIC) { // Pad it with zeros vhdl_expr* zeros = new vhdl_const_bits(string(newwidth - 1, '0').c_str(), newwidth - 1, false, true); vhdl_binop_expr* concat = new vhdl_binop_expr(zeros, VHDL_BINOP_CONCAT, this, vhdl_type::nunsigned(newwidth)); return concat; } else return this; // Doesn't make sense to resize non-vector type vhdl_fcall *resizef = new vhdl_fcall("Resize", rtype); resizef->add_expr(this); resizef->add_expr(new vhdl_const_int(newwidth)); return resizef; } vhdl_expr *vhdl_const_int::to_vector(vhdl_type_name_t name, int w) { if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) { const char *fname = name == VHDL_TYPE_SIGNED ? "To_Signed" : "To_Unsigned"; vhdl_fcall *conv = new vhdl_fcall(fname, new vhdl_type(name, w - 1, 0)); conv->add_expr(this); conv->add_expr(new vhdl_const_int(w)); return conv; } else return vhdl_expr::to_vector(name, w); } int64_t vhdl_const_bits::bits_to_int() const { char msb = value_[value_.size() - 1]; int64_t result = 0, bit; for (int i = sizeof(int64_t)*8 - 1; i >= 0; i--) { if (i > (int)value_.size() - 1) bit = (msb == '1' && signed_) ? 1 : 0; else bit = value_[i] == '1' ? 1 : 0; result = (result << 1) | bit; } return result; } vhdl_expr *vhdl_const_bits::to_std_logic() { // VHDL won't let us cast directly between a vector and // a scalar type // But we don't need to here as we have the bits available // Take the least significant bit char lsb = value_[0]; return new vhdl_const_bit(lsb); } char vhdl_const_bits::sign_bit() const { return signed_ ? value_[value_.length()-1] : '0'; } vhdl_expr *vhdl_const_bits::to_vector(vhdl_type_name_t name, int w) { if (name == VHDL_TYPE_STD_LOGIC_VECTOR) { // Don't need to do anything return this; } else if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) { // Extend with sign bit value_.resize(w, sign_bit()); return this; } assert(false); return NULL; } vhdl_expr *vhdl_const_bits::to_integer() { return new vhdl_const_int(bits_to_int()); } vhdl_expr *vhdl_const_bits::resize(int w) { // Rather than generating a call to Resize, when can just sign-extend // the bits here. As well as looking better, this avoids any ambiguity // between which of the signed/unsigned versions of Resize to use. value_.resize(w, sign_bit()); return this; } vhdl_expr *vhdl_const_bit::to_integer() { return new vhdl_const_int(bit_ == '1' ? 1 : 0); } vhdl_expr *vhdl_const_bit::to_boolean() { return new vhdl_const_bool(bit_ == '1'); } vhdl_expr *vhdl_const_bit::to_std_ulogic() { return this; } vhdl_expr *vhdl_const_bit::to_vector(vhdl_type_name_t name, int w) { // Zero-extend this bit to the correct width return (new vhdl_const_bits(&bit_, 1, name == VHDL_TYPE_SIGNED))->resize(w); } iverilog-10_1/tgt-vhdl/cppcheck.sup000066400000000000000000000002741265551621300174070ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:vhdl.cc:96 // target_query() unusedFunction:vhdl.cc:137 iverilog-10_1/tgt-vhdl/expr.cc000066400000000000000000000553061265551621300163710ustar00rootroot00000000000000/* * VHDL code generation for expressions. * * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "support.hh" #include "state.hh" #include #include #include /* * Change the signedness of a vector. */ static vhdl_expr *change_signedness(vhdl_expr *e, bool issigned) { int msb = e->get_type()->get_msb(); int lsb = e->get_type()->get_lsb(); vhdl_type u(issigned ? VHDL_TYPE_SIGNED : VHDL_TYPE_UNSIGNED, msb, lsb); return e->cast(&u); } /* * Generate code to ensure that the VHDL expression vhd_e has the * same signedness as the Verilog expression vl_e. */ static vhdl_expr *correct_signedness(vhdl_expr *vhd_e, ivl_expr_t vl_e) { bool should_be_signed = ivl_expr_signed(vl_e) != 0; if (vhd_e->get_type()->get_name() == VHDL_TYPE_UNSIGNED && should_be_signed) { //operand->print(); //std::cout << "^ should be signed but is not" << std::endl; return change_signedness(vhd_e, true); } else if (vhd_e->get_type()->get_name() == VHDL_TYPE_SIGNED && !should_be_signed) { //operand->print(); //std::cout << "^ should be unsigned but is not" << std::endl; return change_signedness(vhd_e, false); } else return vhd_e; } /* * Convert a constant Verilog string to a constant VHDL string. */ static vhdl_expr *translate_string(ivl_expr_t e) { // TODO: May need to inspect or escape parts of this const char *str = ivl_expr_string(e); return new vhdl_const_string(str); } /* * A reference to a signal in an expression. It's assumed that the * signal has already been defined elsewhere. */ static vhdl_var_ref *translate_signal(ivl_expr_t e) { ivl_signal_t sig = ivl_expr_signal(e); const vhdl_scope *scope = find_scope_for_signal(sig); assert(scope); const char *renamed = get_renamed_signal(sig).c_str(); vhdl_decl *decl = scope->get_decl(renamed); assert(decl); // Make sure we can read from this declaration // E.g. if this is an `out' port then we need to make it a buffer decl->ensure_readable(); // Can't generate a constant initialiser for this signal // later as it has already been read if (scope->initializing()) decl->set_initial(NULL); vhdl_var_ref *ref = new vhdl_var_ref(renamed, new vhdl_type(*decl->get_type())); ivl_expr_t off; if (ivl_signal_array_count(sig) > 0 && (off = ivl_expr_oper1(e))) { // Select from an array vhdl_expr *vhd_off = translate_expr(off); if (NULL == vhd_off) { delete ref; return NULL; } vhdl_type integer(VHDL_TYPE_INTEGER); ref->set_slice(vhd_off->cast(&integer)); } return ref; } /* * A numeric literal ends up as std_logic bit string. */ static vhdl_expr *translate_number(ivl_expr_t e) { if (ivl_expr_width(e) == 1) return new vhdl_const_bit(ivl_expr_bits(e)[0]); else return new vhdl_const_bits(ivl_expr_bits(e), ivl_expr_width(e), ivl_expr_signed(e) != 0); } static vhdl_expr *translate_ulong(ivl_expr_t e) { return new vhdl_const_int(ivl_expr_uvalue(e)); } static vhdl_expr *translate_delay(ivl_expr_t e) { return scale_time(get_active_entity(), ivl_expr_delay_val(e)); } static vhdl_expr *translate_reduction(support_function_t f, bool neg, vhdl_expr *operand) { vhdl_expr *result; if (operand->get_type()->get_name() == VHDL_TYPE_STD_LOGIC) result = operand; else { require_support_function(f); vhdl_fcall *fcall = new vhdl_fcall(support_function::function_name(f), vhdl_type::std_logic()); vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR); fcall->add_expr(operand->cast(&std_logic_vector)); result = fcall; } if (neg) return new vhdl_unaryop_expr(VHDL_UNARYOP_NOT, result, vhdl_type::std_logic()); else return result; } static vhdl_expr *translate_unary(ivl_expr_t e) { vhdl_expr *operand = translate_expr(ivl_expr_oper1(e)); if (NULL == operand) return NULL; operand = correct_signedness(operand, e); char opcode = ivl_expr_opcode(e); switch (opcode) { case '!': case '~': return new vhdl_unaryop_expr (VHDL_UNARYOP_NOT, operand, new vhdl_type(*operand->get_type())); case '-': operand = change_signedness(operand, true); return new vhdl_unaryop_expr (VHDL_UNARYOP_NEG, operand, new vhdl_type(*operand->get_type())); case 'N': // NOR return translate_reduction(SF_REDUCE_OR, true, operand); case '|': return translate_reduction(SF_REDUCE_OR, false, operand); case 'A': // NAND return translate_reduction(SF_REDUCE_AND, true, operand); case '&': return translate_reduction(SF_REDUCE_AND, false, operand); case '^': // XOR return translate_reduction(SF_REDUCE_XOR, false, operand); case 'X': // XNOR return translate_reduction(SF_REDUCE_XNOR, false, operand); default: error("No translation for unary opcode '%c'\n", ivl_expr_opcode(e)); delete operand; return NULL; } } /* * Translate a numeric binary operator (+, -, etc.) to * a VHDL equivalent using the numeric_std package. */ static vhdl_expr *translate_numeric(vhdl_expr *lhs, vhdl_expr *rhs, vhdl_binop_t op) { // May need to make either side Boolean for operators // to work vhdl_type boolean(VHDL_TYPE_BOOLEAN); if (lhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN) rhs = rhs->cast(&boolean); else if (rhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN) lhs = lhs->cast(&boolean); vhdl_type *rtype; if (op == VHDL_BINOP_MULT) rtype = new vhdl_type(lhs->get_type()->get_name(), (lhs->get_type()->get_width()*2) - 1, 0); else rtype = new vhdl_type(*lhs->get_type()); return new vhdl_binop_expr(lhs, op, rhs, rtype); } static vhdl_expr *translate_relation(vhdl_expr *lhs, vhdl_expr *rhs, vhdl_binop_t op) { // Generate any necessary casts // Arbitrarily, the RHS is casted to the type of the LHS vhdl_expr *r_cast = rhs->cast(lhs->get_type()); return new vhdl_binop_expr(lhs, op, r_cast, vhdl_type::boolean()); } /* * Like translate_relation but both operands must be Boolean. */ static vhdl_expr *translate_logical(vhdl_expr *lhs, vhdl_expr *rhs, vhdl_binop_t op) { vhdl_type boolean(VHDL_TYPE_BOOLEAN); return translate_relation(lhs->cast(&boolean), rhs->cast(&boolean), op); } static vhdl_expr *translate_shift(vhdl_expr *lhs, vhdl_expr *rhs, vhdl_binop_t op) { // The RHS must be an integer vhdl_type integer(VHDL_TYPE_INTEGER); vhdl_expr *r_cast = rhs->cast(&integer); vhdl_type *rtype = new vhdl_type(*lhs->get_type()); // The sra operator is not defined on numeric_std types until // VHDL-2006 which is not well supported. Instead we can use // the shift_right function which does the same thing and // exists in earlier versions of numeric_std. if (op == VHDL_BINOP_SRA) { vhdl_fcall *sra = new vhdl_fcall("shift_right", rtype); sra->add_expr(lhs); sra->add_expr(r_cast); return sra; } else return new vhdl_binop_expr(lhs, op, r_cast, rtype); } /* * The exponentiation operator in VHDL is not defined for numeric_std * types. We can get around this by converting the operands to integers, * performing the operation, then converting the result back to the * original type. This will work OK in simulation but certainly will not * synthesise unless the operands are constant. * * However, even this does not work quite correctly. The Integer type in * VHDL is signed and usually only 32 bits, therefore any result larger * than this will overflow and raise an exception. I can't see a way * around this at the moment. */ static vhdl_expr *translate_power(ivl_expr_t e, vhdl_expr *lhs, vhdl_expr *rhs) { vhdl_type integer(VHDL_TYPE_INTEGER); vhdl_expr *lhs_int = lhs->cast(&integer); vhdl_expr *rhs_int = rhs->cast(&integer); vhdl_expr *result = new vhdl_binop_expr(lhs_int, VHDL_BINOP_POWER, rhs_int, vhdl_type::integer()); int width = ivl_expr_width(e); const char *func = ivl_expr_signed(e) ? "To_Signed" : "To_Unsigned"; vhdl_type *type = ivl_expr_signed(e) ? vhdl_type::nsigned(width) : vhdl_type::nunsigned(width); vhdl_fcall *conv = new vhdl_fcall(func, type); conv->add_expr(result); conv->add_expr(new vhdl_const_int(width)); return conv; } static vhdl_expr *translate_binary(ivl_expr_t e) { vhdl_expr *lhs = translate_expr(ivl_expr_oper1(e)); if (NULL == lhs) return NULL; vhdl_expr *rhs = translate_expr(ivl_expr_oper2(e)); if (NULL == rhs) { delete lhs; return NULL; } int lwidth = lhs->get_type()->get_width(); int rwidth = rhs->get_type()->get_width(); int result_width = ivl_expr_width(e); // There's a funny corner-case where both the LHS and RHS are constant // single bit numbers and the VHDL compiler can't decide between the // std_ulogic and bit overloads of various operators const bool lnumber = ivl_expr_type(ivl_expr_oper1(e)) == IVL_EX_NUMBER; const bool rnumber = ivl_expr_type(ivl_expr_oper2(e)) == IVL_EX_NUMBER; if (lwidth == 1 && rwidth == 1 && lnumber && rnumber) { // It's sufficient to qualify only one side vhdl_fcall *lqual = new vhdl_fcall("std_logic'", lhs->get_type()); lqual->add_expr(lhs); lhs = lqual; } // For === and !== we need to compare std_logic_vectors // rather than signeds vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR, result_width-1, 0); vhdl_type_name_t ltype = lhs->get_type()->get_name(); vhdl_type_name_t rtype = rhs->get_type()->get_name(); bool vectorop = (ltype == VHDL_TYPE_SIGNED || ltype == VHDL_TYPE_UNSIGNED) && (rtype == VHDL_TYPE_SIGNED || rtype == VHDL_TYPE_UNSIGNED); // May need to resize the left or right hand side or change the // signedness if (vectorop) { if (lwidth < rwidth) lhs = lhs->resize(rwidth); else if (rwidth < lwidth) rhs = rhs->resize(lwidth); lhs = correct_signedness(lhs, ivl_expr_oper1(e)); rhs = correct_signedness(rhs, ivl_expr_oper2(e)); } vhdl_expr *result; switch (ivl_expr_opcode(e)) { case '+': result = translate_numeric(lhs, rhs, VHDL_BINOP_ADD); break; case '-': result = translate_numeric(lhs, rhs, VHDL_BINOP_SUB); break; case '*': result = translate_numeric(lhs, rhs, VHDL_BINOP_MULT); break; case '/': result = translate_numeric(lhs, rhs, VHDL_BINOP_DIV); break; case '%': result = translate_numeric(lhs, rhs, VHDL_BINOP_MOD); break; case 'e': result = translate_relation(lhs, rhs, VHDL_BINOP_EQ); break; case 'E': if (vectorop) result = translate_relation(lhs->cast(&std_logic_vector), rhs->cast(&std_logic_vector), VHDL_BINOP_EQ); else result = translate_relation(lhs, rhs, VHDL_BINOP_EQ); break; case 'n': result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ); break; case 'N': if (vectorop) result = translate_relation(lhs->cast(&std_logic_vector), rhs->cast(&std_logic_vector), VHDL_BINOP_NEQ); else result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ); break; case '&': // Bitwise AND result = translate_numeric(lhs, rhs, VHDL_BINOP_AND); break; case 'a': // Logical AND result = translate_logical(lhs, rhs, VHDL_BINOP_AND); break; case 'A': // Bitwise NAND result = translate_numeric(lhs, rhs, VHDL_BINOP_NAND); break; case 'O': // Bitwise NOR result = translate_numeric(lhs, rhs, VHDL_BINOP_NOR); break; case 'X': // Bitwise XNOR result = translate_numeric(lhs, rhs, VHDL_BINOP_XNOR); break; case '|': // Bitwise OR result = translate_numeric(lhs, rhs, VHDL_BINOP_OR); break; case 'o': // Logical OR result = translate_logical(lhs, rhs, VHDL_BINOP_OR); break; case '<': result = translate_relation(lhs, rhs, VHDL_BINOP_LT); break; case 'L': result = translate_relation(lhs, rhs, VHDL_BINOP_LEQ); break; case '>': result = translate_relation(lhs, rhs, VHDL_BINOP_GT); break; case 'G': result = translate_relation(lhs, rhs, VHDL_BINOP_GEQ); break; case 'l': result = translate_shift(lhs, rhs, VHDL_BINOP_SL); break; case 'r': result = translate_shift(lhs, rhs, VHDL_BINOP_SR); break; case 'R': // Arithmetic right shift // Verilog only actually performs a signed shift if the // argument being shifted is signed, otherwise it defaults // to a normal shift if (ivl_expr_signed(ivl_expr_oper1(e))) result = translate_shift(lhs, rhs, VHDL_BINOP_SRA); else result = translate_shift(lhs, rhs, VHDL_BINOP_SR); break; case '^': result = translate_numeric(lhs, rhs, VHDL_BINOP_XOR); break; case 'p': // Power result = translate_power(e, lhs, rhs); break; default: error("No translation for binary opcode '%c'\n", ivl_expr_opcode(e)); delete lhs; delete rhs; return NULL; } if (NULL == result) return NULL; if (vectorop) { result = correct_signedness(result, e); int actual_width = result->get_type()->get_width(); if (actual_width != result_width) { //result->print(); //std::cout << "^ should be " << result_width << " but is " << actual_width << std::endl; } } return result; } static vhdl_expr *translate_select(ivl_expr_t e) { vhdl_expr *from = translate_expr(ivl_expr_oper1(e)); if (NULL == from) return NULL; ivl_expr_t o2 = ivl_expr_oper2(e); if (o2) { vhdl_expr *base = translate_expr(ivl_expr_oper2(e)); if (NULL == base) return NULL; vhdl_var_ref *from_var_ref = dynamic_cast(from); if (NULL == from_var_ref) { // We can't directly select bits from something that's not // a variable reference in VHDL, but we can emulate the // effect with a shift and a resize if (ivl_expr_signed(ivl_expr_oper1(e))) { vhdl_fcall *sra = new vhdl_fcall("shift_right", from->get_type()); sra->add_expr(from); sra->add_expr(base->to_integer()); return sra; } else return new vhdl_binop_expr(from, VHDL_BINOP_SR, base->to_integer(), from->get_type()); } else if (from_var_ref->get_type()->get_name() != VHDL_TYPE_STD_LOGIC) { // We can use the more idiomatic VHDL slice notation on a // single variable reference vhdl_type integer(VHDL_TYPE_INTEGER); from_var_ref->set_slice(base->cast(&integer), ivl_expr_width(e) - 1); return from_var_ref; } else { // Make sure we're not trying to select more than one bit // from a std_logic (this shouldn't actually happen) if (ivl_expr_width(e) > 1) { error("%s:%d: trying to select more than one bit from a std_logic", ivl_expr_file(e), ivl_expr_lineno(e)); return NULL; } else return from_var_ref; } } else return correct_signedness(from, e)->resize(ivl_expr_width(e)); } template static T *translate_parms(T *t, ivl_expr_t e) { int nparams = ivl_expr_parms(e); for (int i = 0; i < nparams; i++) { vhdl_expr *param = translate_expr(ivl_expr_parm(e, i)); if (NULL == param) return NULL; t->add_expr(param); } return t; } static vhdl_expr *translate_ufunc(ivl_expr_t e) { ivl_scope_t defscope = ivl_expr_def(e); ivl_scope_t parentscope = ivl_scope_parent(defscope); assert(ivl_scope_type(parentscope) == IVL_SCT_MODULE); // A function is always declared in a module, which should have // a corresponding entity by this point: so we can get type // information, etc. from the declaration vhdl_entity *parent_ent = find_entity(parentscope); assert(parent_ent); const char *funcname = ivl_scope_tname(defscope); vhdl_type *rettype = vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0); vhdl_fcall *fcall = new vhdl_fcall(funcname, rettype); int nparams = ivl_expr_parms(e); for (int i = 0; i < nparams; i++) { vhdl_expr *param = translate_expr(ivl_expr_parm(e, i)); if (NULL == param) { delete fcall; return NULL; } // Ensure the parameter has the correct VHDL type // Parameter number is i + 1 since 0th parameter is return value ivl_signal_t param_sig = ivl_scope_port(defscope, i + 1); vhdl_type *param_type = vhdl_type::type_for(ivl_signal_width(param_sig), ivl_signal_signed(param_sig) != 0); fcall->add_expr(param->cast(param_type)); delete param_type; } return fcall; } static vhdl_expr *translate_ternary(ivl_expr_t e) { support_function_t sf; int width = ivl_expr_width(e); bool issigned = ivl_expr_signed(e) != 0; if (width == 1) sf = SF_TERNARY_LOGIC; else if (issigned) sf = SF_TERNARY_SIGNED; else sf = SF_TERNARY_UNSIGNED; require_support_function(sf); vhdl_expr *test = translate_expr(ivl_expr_oper1(e)); vhdl_expr *true_part = translate_expr(ivl_expr_oper2(e)); vhdl_expr *false_part = translate_expr(ivl_expr_oper3(e)); if (!test || !true_part || !false_part) return NULL; vhdl_type boolean(VHDL_TYPE_BOOLEAN); test = test->cast(&boolean); vhdl_fcall *fcall = new vhdl_fcall(support_function::function_name(sf), vhdl_type::type_for(width, issigned)); fcall->add_expr(test); fcall->add_expr(true_part); fcall->add_expr(false_part); return fcall; } static vhdl_expr *translate_concat(ivl_expr_t e) { vhdl_type *rtype = vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0); vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, rtype); int nrepeat = ivl_expr_repeat(e); while (nrepeat--) translate_parms(concat, e); return concat; } vhdl_expr *translate_sfunc_time(ivl_expr_t) { cerr << "warning: no translation for $time (returning 0)" << endl; vhdl_expr *result = new vhdl_const_int(0); result->set_comment("$time not supported, returned 0 instead!"); return result; } vhdl_expr *translate_sfunc_stime(ivl_expr_t) { cerr << "warning: no translation for $stime (returning 0)" << endl; vhdl_expr *result = new vhdl_const_int(0); result->set_comment("$stime not supported, returned 0 instead!"); return result; } vhdl_expr *translate_sfunc_simtime(ivl_expr_t) { cerr << "warning: no translation for $simtime (returning 0)" << endl; vhdl_expr *result = new vhdl_const_int(0); result->set_comment("$simtime not supported, returned 0 instead!"); return result; } vhdl_expr *translate_sfunc_random(ivl_expr_t) { cerr << "warning: no translation for $random (returning 0)" << endl; vhdl_expr *result = new vhdl_const_int(0); result->set_comment("$random not supported, returned 0 instead!"); return result; } vhdl_expr *translate_sfunc_fopen(ivl_expr_t) { cerr << "warning: no translation for $fopen (returning 0)" << endl; vhdl_expr *result = new vhdl_const_int(0); result->set_comment("$fopen not supported, returned 0 instead!"); return result; } vhdl_expr *translate_sfunc(ivl_expr_t e) { const char *name = ivl_expr_name(e); if (strcmp(name, "$time") == 0) return translate_sfunc_time(e); else if (strcmp(name, "$stime") == 0) return translate_sfunc_stime(e); else if (strcmp(name, "$simtime") == 0) return translate_sfunc_simtime(e); else if (strcmp(name, "$random") == 0) return translate_sfunc_random(e); else if (strcmp(name, "$fopen") == 0) return translate_sfunc_random(e); else { error("No translation for system function %s", name); return NULL; } } /* * Generate a VHDL expression from a Verilog expression. */ vhdl_expr *translate_expr(ivl_expr_t e) { assert(e); ivl_expr_type_t type = ivl_expr_type(e); switch (type) { case IVL_EX_STRING: return translate_string(e); case IVL_EX_SIGNAL: return translate_signal(e); case IVL_EX_NUMBER: return translate_number(e); case IVL_EX_ULONG: return translate_ulong(e); case IVL_EX_UNARY: return translate_unary(e); case IVL_EX_BINARY: return translate_binary(e); case IVL_EX_SELECT: return translate_select(e); case IVL_EX_UFUNC: return translate_ufunc(e); case IVL_EX_TERNARY: return translate_ternary(e); case IVL_EX_CONCAT: return translate_concat(e); case IVL_EX_SFUNC: return translate_sfunc(e); case IVL_EX_DELAY: return translate_delay(e); case IVL_EX_REALNUM: error("No VHDL translation for real expression at %s:%d", ivl_expr_file(e), ivl_expr_lineno(e)); return NULL; default: error("No VHDL translation for expression at %s:%d (type = %d)", ivl_expr_file(e), ivl_expr_lineno(e), type); return NULL; } } /* * Translate an expression into a time. This is achieved simply * by multiplying the expression by 1ns. */ vhdl_expr *translate_time_expr(ivl_expr_t e) { vhdl_expr *time = translate_expr(e); if (NULL == time) return NULL; if (time->get_type()->get_name() != VHDL_TYPE_TIME) { vhdl_type integer(VHDL_TYPE_INTEGER); time = time->cast(&integer); vhdl_expr *ns1 = scale_time(get_active_entity(), 1); return new vhdl_binop_expr(time, VHDL_BINOP_MULT, ns1, vhdl_type::time()); } else // Translating IVL_EX_DELAY will always return a time type return time; } iverilog-10_1/tgt-vhdl/logic.cc000066400000000000000000000215201265551621300164770ustar00rootroot00000000000000/* * VHDL code generation for logic devices. * * Copyright (C) 2008-2014 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "vhdl_element.hh" #include "state.hh" #include #include #include /* * Convert the inputs of a logic gate to a binary expression. */ static vhdl_expr *inputs_to_expr(vhdl_scope *scope, vhdl_binop_t op, ivl_net_logic_t log) { // Not always std_logic but this is probably OK since // the program has already been type checked vhdl_binop_expr *gate = new vhdl_binop_expr(op, vhdl_type::std_logic()); int npins = ivl_logic_pins(log); for (int i = 1; i < npins; i++) { ivl_nexus_t input = ivl_logic_pin(log, i); gate->add_expr(readable_ref(scope, input)); } return gate; } /* * Convert a gate input to an unary expression. */ static vhdl_expr *input_to_expr(vhdl_scope *scope, vhdl_unaryop_t op, ivl_net_logic_t log) { ivl_nexus_t input = ivl_logic_pin(log, 1); assert(input); vhdl_expr *operand = readable_ref(scope, input); return new vhdl_unaryop_expr(op, operand, vhdl_type::std_logic()); } static void bufif_logic(vhdl_arch *arch, ivl_net_logic_t log, bool if0) { ivl_nexus_t output = ivl_logic_pin(log, 0); vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output); assert(lhs); vhdl_expr *val = readable_ref(arch->get_scope(), ivl_logic_pin(log, 1)); vhdl_expr *sel = readable_ref(arch->get_scope(), ivl_logic_pin(log, 2)); vhdl_expr *cmp; if (ivl_logic_width(log) == 1) { vhdl_expr *on = new vhdl_const_bit(if0 ? '0' : '1'); cmp = new vhdl_binop_expr(sel, VHDL_BINOP_EQ, on, NULL); } else { vhdl_expr *zero = (new vhdl_const_int(0))->cast(sel->get_type()); vhdl_binop_t op = if0 ? VHDL_BINOP_EQ : VHDL_BINOP_NEQ; cmp = new vhdl_binop_expr(sel, op, zero, NULL); } ivl_signal_t sig = find_signal_named(lhs->get_name(), arch->get_scope()); char zbit; switch (ivl_signal_type(sig)) { case IVL_SIT_TRI0: zbit = '0'; break; case IVL_SIT_TRI1: zbit = '1'; break; case IVL_SIT_TRI: default: zbit = 'Z'; } vhdl_expr *z = new vhdl_const_bit(zbit); if (ivl_logic_width(log) > 1) z = new vhdl_bit_spec_expr(NULL, z); vhdl_cassign_stmt *cass = new vhdl_cassign_stmt(lhs, z); cass->add_condition(val, cmp); arch->add_stmt(cass); } static void comb_udp_logic(vhdl_arch *arch, ivl_net_logic_t log) { ivl_udp_t udp = ivl_logic_udp(log); // As with regular case statements, the expression in a // `with .. select' statement must be "locally static". // This is achieved by first combining the inputs into // a temporary ostringstream ss; ss << ivl_logic_basename(log) << "_Tmp"; int msb = ivl_udp_nin(udp) - 1; vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0); vhdl_signal_decl *tmp_decl = new vhdl_signal_decl(ss.str(), tmp_type); arch->get_scope()->add_decl(tmp_decl); int nin = ivl_udp_nin(udp); vhdl_expr *tmp_rhs; if (nin == 1) { tmp_rhs = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1)); tmp_rhs = tmp_rhs->cast(tmp_type); } else tmp_rhs = inputs_to_expr(arch->get_scope(), VHDL_BINOP_CONCAT, log); ss.str(""); ss << "Input to " << ivl_logic_basename(log) << " " << ivl_udp_name(udp) << " UDP"; tmp_decl->set_comment(ss.str()); vhdl_var_ref *tmp_ref = new vhdl_var_ref(tmp_decl->get_name().c_str(), NULL); arch->add_stmt(new vhdl_cassign_stmt(tmp_ref, tmp_rhs)); // Now we can implement the UDP as a `with .. select' statement // by reading values out of the table ivl_nexus_t output_nex = ivl_logic_pin(log, 0); vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), output_nex); vhdl_with_select_stmt *ws = new vhdl_with_select_stmt(new vhdl_var_ref(*tmp_ref), out); // Ensure the select statement completely covers the input space // or some strict VHDL compilers will complain ws->add_default(new vhdl_const_bit('X')); int nrows = ivl_udp_rows(udp); for (int i = 0; i < nrows; i++) { const char *row = ivl_udp_row(udp, i); vhdl_expr *value = new vhdl_const_bit(row[nin]); vhdl_expr *cond = new vhdl_const_bits(row, nin, false); ivl_expr_t delay_ex = ivl_logic_delay(log, 1); vhdl_expr *delay = NULL; if (delay_ex) delay = translate_time_expr(delay_ex); ws->add_condition(value, cond, delay); } ss.str(""); ss << "UDP " << ivl_udp_name(udp); ws->set_comment(ss.str()); arch->add_stmt(ws); } static void seq_udp_logic(vhdl_arch *arch, ivl_net_logic_t log) { ivl_udp_t udp = ivl_logic_udp(log); // These will be translated to a process with a single // case statement vhdl_process *proc = new vhdl_process(ivl_logic_basename(log)); ostringstream ss; ss << "Generated from UDP " << ivl_udp_name(udp); proc->set_comment(ss.str().c_str()); // Create a variable to hold the concatenation of the inputs int msb = ivl_udp_nin(udp) - 1; vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0); proc->get_scope()->add_decl(new vhdl_var_decl("UDP_Inputs", tmp_type)); // Concatenate the inputs into a single expression that can be // used as the test in a case statement (this can't be inserted // directly into the case statement due to the requirement that // the test expression be "locally static") int nin = ivl_udp_nin(udp); vhdl_expr *tmp_rhs = NULL; if (nin == 1) { vhdl_var_ref *ref = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1)); tmp_rhs = ref->cast(tmp_type); proc->add_sensitivity(ref->get_name()); } else { vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, NULL); for (int i = 1; i < nin; i++) { vhdl_var_ref *ref = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, i)); concat->add_expr(ref); proc->add_sensitivity(ref->get_name()); } tmp_rhs = concat; } proc->get_container()->add_stmt (new vhdl_assign_stmt(new vhdl_var_ref("UDP_Inputs", NULL), tmp_rhs)); arch->add_stmt(proc); } static void udp_logic(vhdl_arch *arch, ivl_net_logic_t log) { if (ivl_udp_sequ(ivl_logic_udp(log))) seq_udp_logic(arch, log); else comb_udp_logic(arch, log); } static vhdl_expr *translate_logic_inputs(vhdl_scope *scope, ivl_net_logic_t log) { switch (ivl_logic_type(log)) { case IVL_LO_NOT: return input_to_expr(scope, VHDL_UNARYOP_NOT, log); case IVL_LO_AND: return inputs_to_expr(scope, VHDL_BINOP_AND, log); case IVL_LO_OR: return inputs_to_expr(scope, VHDL_BINOP_OR, log); case IVL_LO_NAND: return inputs_to_expr(scope, VHDL_BINOP_NAND, log); case IVL_LO_NOR: return inputs_to_expr(scope, VHDL_BINOP_NOR, log); case IVL_LO_XOR: return inputs_to_expr(scope, VHDL_BINOP_XOR, log); case IVL_LO_XNOR: return inputs_to_expr(scope, VHDL_BINOP_XNOR, log); case IVL_LO_BUF: case IVL_LO_BUFT: case IVL_LO_BUFZ: return nexus_to_var_ref(scope, ivl_logic_pin(log, 1)); case IVL_LO_PULLUP: return new vhdl_const_bit('1'); case IVL_LO_PULLDOWN: return new vhdl_const_bit('0'); default: error("Don't know how to translate logic type = %d to expression", ivl_logic_type(log)); return NULL; } } void draw_logic(vhdl_arch *arch, ivl_net_logic_t log) { switch (ivl_logic_type(log)) { case IVL_LO_BUFIF0: bufif_logic(arch, log, true); break; case IVL_LO_BUFIF1: bufif_logic(arch, log, false); break; case IVL_LO_UDP: udp_logic(arch, log); break; default: { // The output is always pin zero ivl_nexus_t output = ivl_logic_pin(log, 0); vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output); vhdl_expr *rhs = translate_logic_inputs(arch->get_scope(), log); vhdl_cassign_stmt *ass = new vhdl_cassign_stmt(lhs, rhs); ivl_expr_t delay = ivl_logic_delay(log, 1); if (delay) ass->set_after(translate_time_expr(delay)); arch->add_stmt(ass); } } } iverilog-10_1/tgt-vhdl/lpm.cc000066400000000000000000000251631265551621300162010ustar00rootroot00000000000000/* * VHDL code generation for LPM devices. * * Copyright (C) 2008-2009 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "state.hh" #include #include /* * Return the base of a part select. */ static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm) { vhdl_expr *off; ivl_nexus_t base = ivl_lpm_data(lpm, 1); if (base != NULL) off = readable_ref(scope, base); else off = new vhdl_const_int(ivl_lpm_base(lpm)); // Array indexes must be integers vhdl_type integer(VHDL_TYPE_INTEGER); return off->cast(&integer); } static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op) { unsigned out_width = ivl_lpm_width(lpm); vhdl_type *result_type = vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0); vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type); for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) { vhdl_expr *e = readable_ref(scope, ivl_lpm_data(lpm, i)); if (NULL == e) return NULL; // It's possible that the inputs are a mixture of signed and unsigned // in which case we must cast them to the output type e = e->cast(vhdl_type::type_for(e->get_type()->get_width(), ivl_lpm_signed(lpm) != 0)); // Bit of a hack: the LPM inputs are in the wrong order for concatenation if (op == VHDL_BINOP_CONCAT) expr->add_expr_front(e); else expr->add_expr(e); } if (op == VHDL_BINOP_MULT) { // Need to resize the output to the desired size, // as this does not happen automatically in VHDL vhdl_fcall *resize = new vhdl_fcall("Resize", vhdl_type::nsigned(out_width)); resize->add_expr(expr); resize->add_expr(new vhdl_const_int(out_width)); return resize; } else return expr; } static vhdl_expr *rel_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op) { vhdl_binop_expr *expr = new vhdl_binop_expr(op, vhdl_type::boolean()); vhdl_expr *lhs = readable_ref(scope, ivl_lpm_data(lpm, 0)); if (NULL == lhs) return NULL; vhdl_expr *rhs = readable_ref(scope, ivl_lpm_data(lpm, 1)); if (NULL == rhs) { delete lhs; return NULL; } // Ensure LHS and RHS are the same type if (lhs->get_type() != rhs->get_type()) rhs = rhs->cast(lhs->get_type()); expr->add_expr(lhs); expr->add_expr(rhs); return expr; } static vhdl_expr *part_select_vp_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { vhdl_var_ref *selfrom = readable_ref(scope, ivl_lpm_data(lpm, 0)); if (NULL == selfrom) return NULL; vhdl_expr *off = part_select_base(scope, lpm);; if (NULL == off) return NULL; if (selfrom->get_type()->get_width() > 1) selfrom->set_slice(off, ivl_lpm_width(lpm) - 1); return selfrom; } static vhdl_expr *part_select_pv_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { return readable_ref(scope, ivl_lpm_data(lpm, 0)); } static vhdl_expr *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { ivl_scope_t f_scope = ivl_lpm_define(lpm); vhdl_fcall *fcall = new vhdl_fcall(ivl_scope_basename(f_scope), NULL); for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) { vhdl_var_ref *ref = readable_ref(scope, ivl_lpm_data(lpm, i)); if (NULL == ref) { delete fcall; return NULL; } fcall->add_expr(ref); } return fcall; } static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, support_function_t f, bool invert) { vhdl_var_ref *ref = readable_ref(scope, ivl_lpm_data(lpm, 0)); if (NULL == ref) return NULL; vhdl_expr *result; if (ref->get_type()->get_name() == VHDL_TYPE_STD_LOGIC) result = ref; else { require_support_function(f); vhdl_fcall *fcall = new vhdl_fcall(support_function::function_name(f), vhdl_type::std_logic()); vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR); fcall->add_expr(ref->cast(&std_logic_vector)); result = fcall; } if (invert) return new vhdl_unaryop_expr (VHDL_UNARYOP_NOT, result, vhdl_type::std_logic()); else return result; } static vhdl_expr *sign_extend_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { vhdl_expr *ref = readable_ref(scope, ivl_lpm_data(lpm, 0)); if (ref) return ref->resize(ivl_lpm_width(lpm)); else return NULL; } static vhdl_expr *array_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { ivl_signal_t array = ivl_lpm_array(lpm); if (!seen_signal_before(array)) return NULL; const char *renamed = get_renamed_signal(array).c_str(); vhdl_decl *adecl = scope->get_decl(renamed); assert(adecl); vhdl_type *atype = new vhdl_type(*adecl->get_type()); vhdl_expr *select = readable_ref(scope, ivl_lpm_select(lpm)); if (NULL == select) { delete atype; return NULL; } vhdl_var_ref *ref = new vhdl_var_ref(renamed, atype); vhdl_type integer(VHDL_TYPE_INTEGER); ref->set_slice(select->cast(&integer)); return ref; } static vhdl_expr *shift_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t shift_op) { vhdl_expr *lhs = readable_ref(scope, ivl_lpm_data(lpm, 0)); vhdl_expr *rhs = readable_ref(scope, ivl_lpm_data(lpm, 1)); if (!lhs || !rhs) return NULL; // The RHS must be an integer vhdl_type integer(VHDL_TYPE_INTEGER); vhdl_expr *r_cast = rhs->cast(&integer); vhdl_type *rtype = new vhdl_type(*lhs->get_type()); return new vhdl_binop_expr(lhs, shift_op, r_cast, rtype); } static vhdl_expr *repeat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { vhdl_expr *in = readable_ref(scope, ivl_lpm_data(lpm, 0)); return new vhdl_bit_spec_expr(NULL, in); } static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) { switch (ivl_lpm_type(lpm)) { case IVL_LPM_ADD: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD); case IVL_LPM_SUB: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB); case IVL_LPM_MULT: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT); case IVL_LPM_DIVIDE: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_DIV); case IVL_LPM_MOD: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MOD); case IVL_LPM_CONCAT: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_CONCAT); case IVL_LPM_CMP_GE: return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ); case IVL_LPM_CMP_GT: return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GT); case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_NEQ); case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EEQ: return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_EQ); case IVL_LPM_PART_VP: return part_select_vp_lpm_to_expr(scope, lpm); case IVL_LPM_PART_PV: return part_select_pv_lpm_to_expr(scope, lpm); case IVL_LPM_UFUNC: return ufunc_lpm_to_expr(scope, lpm); case IVL_LPM_RE_AND: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, false); case IVL_LPM_RE_NAND: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, true); case IVL_LPM_RE_NOR: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, true); case IVL_LPM_RE_OR: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, false); case IVL_LPM_RE_XOR: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false); case IVL_LPM_RE_XNOR: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XNOR, true); case IVL_LPM_SIGN_EXT: return sign_extend_lpm_to_expr(scope, lpm); case IVL_LPM_ARRAY: return array_lpm_to_expr(scope, lpm); case IVL_LPM_SHIFTL: return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SL); case IVL_LPM_SHIFTR: return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SR); case IVL_LPM_REPEAT: return repeat_lpm_to_expr(scope, lpm); default: error("Unsupported LPM type: %d", ivl_lpm_type(lpm)); return NULL; } } static int draw_mux_lpm(vhdl_arch *arch, ivl_lpm_t lpm) { int nselects = ivl_lpm_selects(lpm); if (nselects > 1) { error("Only 1 LPM select bit supported at the moment"); return 1; } vhdl_scope *scope = arch->get_scope(); vhdl_expr *s0 = readable_ref(scope, ivl_lpm_data(lpm, 0)); vhdl_expr *s1 = readable_ref(scope, ivl_lpm_data(lpm, 1)); vhdl_expr *sel = readable_ref(scope, ivl_lpm_select(lpm)); vhdl_expr *b1 = new vhdl_const_bit('1'); vhdl_expr *t1 = new vhdl_binop_expr(sel, VHDL_BINOP_EQ, b1, vhdl_type::boolean()); vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm)); // Make sure s0 and s1 have the same type as the output s0 = s0->cast(out->get_type()); s1 = s1->cast(out->get_type()); vhdl_cassign_stmt *s = new vhdl_cassign_stmt(out, s0); s->add_condition(s1, t1); arch->add_stmt(s); return 0; } int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm) { if (ivl_lpm_type(lpm) == IVL_LPM_MUX) return draw_mux_lpm(arch, lpm); vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm); if (NULL == f) return 1; vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), ivl_lpm_q(lpm)); if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) { vhdl_expr *off = part_select_base(arch->get_scope(), lpm); assert(off); out->set_slice(off, ivl_lpm_width(lpm) - 1); } // Converting from Boolean to std_logic is a common case so should be // replaced by an idiomatic VHDL construct rather than a call to a // conversion function bool bool_to_logic = out->get_type()->get_name() == VHDL_TYPE_STD_LOGIC && f->get_type()->get_name() == VHDL_TYPE_BOOLEAN; if (bool_to_logic) { vhdl_cassign_stmt* s = new vhdl_cassign_stmt(out, new vhdl_const_bit('0')); s->add_condition(new vhdl_const_bit('1'), f); arch->add_stmt(s); } else arch->add_stmt(new vhdl_cassign_stmt(out, f->cast(out->get_type()))); return 0; } iverilog-10_1/tgt-vhdl/process.cc000066400000000000000000000072371265551621300170710ustar00rootroot00000000000000/* * VHDL code generation for processes. * * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "vhdl_element.hh" #include "state.hh" #include #include #include /* * Convert a Verilog process to VHDL and add it to the architecture * of the given entity. */ static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc) { set_active_entity(ent); // Create a new process and store it in the entity's // architecture. This needs to be done first or the // parent link won't be valid (and draw_stmt needs this // to add information to the architecture) vhdl_process *vhdl_proc = new vhdl_process(); ent->get_arch()->add_stmt(vhdl_proc); // If this is an initial process, push signal initialisation // into the declarations vhdl_proc->get_scope()->set_initializing (ivl_process_type(proc) == IVL_PR_INITIAL); ivl_statement_t stmt = ivl_process_stmt(proc); int rc = draw_stmt(vhdl_proc, vhdl_proc->get_container(), stmt); if (rc != 0) return rc; // Initial processes are translated to VHDL processes with // no sensitivity list and and indefinite wait statement at // the end // However, if no statements were added to the container // by draw_stmt, don't bother adding a wait as `emit' // will optimise the process out of the output bool is_initial = ivl_process_type(proc) == IVL_PR_INITIAL; bool is_empty = vhdl_proc->get_container()->empty(); if (is_initial && !is_empty) { vhdl_wait_stmt *wait = new vhdl_wait_stmt(); vhdl_proc->get_container()->add_stmt(wait); } // Add a comment indicating where it came from ivl_scope_t scope = ivl_process_scope(proc); const char *type = ivl_process_type(proc) == IVL_PR_INITIAL ? "initial" : "always"; std::ostringstream ss; ss << "Generated from " << type << " process in " << ivl_scope_tname(scope) << " (" << ivl_process_file(proc) << ":" << ivl_process_lineno(proc) << ")"; vhdl_proc->set_comment(ss.str()); set_active_entity(NULL); return 0; } extern "C" int draw_process(ivl_process_t proc, void *) { ivl_scope_t scope = ivl_process_scope(proc); if (!is_default_scope_instance(scope)) return 0; // Ignore this process at it's not in a scope that // we're using to generate code debug_msg("Translating process in scope type %s (%s:%d)", ivl_scope_tname(scope), ivl_process_file(proc), ivl_process_lineno(proc)); // Skip over any generate and begin scopes until we find // the module that contains them - this is where we will // generate the process while (ivl_scope_type(scope) == IVL_SCT_GENERATE || ivl_scope_type(scope) == IVL_SCT_BEGIN) scope = ivl_scope_parent(scope); assert(ivl_scope_type(scope) == IVL_SCT_MODULE); vhdl_entity *ent = find_entity(scope); assert(ent != NULL); return generate_vhdl_process(ent, proc); } iverilog-10_1/tgt-vhdl/scope.cc000066400000000000000000001141671265551621300165250ustar00rootroot00000000000000/* * VHDL code generation for scopes. * * Copyright (C) 2008-2014 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "vhdl_element.hh" #include "state.hh" #include #include #include #include /* * This represents the portion of a nexus that is visible within * a VHDL scope. If that nexus portion does not contain a signal, * then `tmpname' gives the name of the temporary that will be * used when this nexus is used in `scope' (e.g. for LPMs that * appear in instantiations). The list `connect' lists all the * signals that should be joined together to re-create the net. */ struct scope_nexus_t { vhdl_scope *scope; ivl_signal_t sig; // A real signal unsigned pin; // The pin this signal is connected to string tmpname; // A new temporary signal list connect; // Other signals to wire together }; /* * This structure is stored in the private part of each nexus. * It stores a scope_nexus_t for each VHDL scope which is * connected to that nexus. It's stored as a list so we can use * contained_within to allow several nested scopes to reference * the same signal. */ struct nexus_private_t { list signals; vhdl_expr *const_driver; }; /* * Returns the scope_nexus_t of this nexus visible within scope. */ static scope_nexus_t *visible_nexus(nexus_private_t *priv, vhdl_scope *scope) { list::iterator it; for (it = priv->signals.begin(); it != priv->signals.end(); ++it) { if (scope->contained_within((*it).scope)) return &*it; } return NULL; } /* * Remember that a signal in `scope' is part of this nexus. The * first signal passed to this function for a scope will be used * as the canonical representation of this nexus when we need to * convert it to a variable reference (e.g. in a LPM input/output). */ static void link_scope_to_nexus_signal(nexus_private_t *priv, vhdl_scope *scope, ivl_signal_t sig, unsigned pin) { scope_nexus_t *sn; if ((sn = visible_nexus(priv, scope))) { assert(sn->tmpname == ""); // Remember to connect this signal up later // If one of the signals is a input, make sure the input is not being driven if (ivl_signal_port(sn->sig) == IVL_SIP_INPUT) sn->sig = sig; else sn->connect.push_back(sig); } else { scope_nexus_t new_sn = { scope, sig, pin, "", list() }; priv->signals.push_back(new_sn); } } /* * Make a temporary the representative of this nexus in scope. */ static void link_scope_to_nexus_tmp(nexus_private_t *priv, vhdl_scope *scope, const string &name) { scope_nexus_t new_sn = { scope, NULL, 0, name, list() }; priv->signals.push_back(new_sn); } /* * Finds the name of the nexus signal within this scope. */ static string visible_nexus_signal_name(nexus_private_t *priv, vhdl_scope *scope, unsigned *pin) { scope_nexus_t *sn = visible_nexus(priv, scope); assert(sn); *pin = sn->pin; return sn->sig ? get_renamed_signal(sn->sig) : sn->tmpname; } /* * Calculate the signal type of a nexus. This is modified from * draw_net_input in tgt-vvp. This also returns the width of * the signal(s) connected to the nexus. */ static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex, int &width) { ivl_signal_type_t out = IVL_SIT_TRI; width = 0; for (unsigned idx = 0; idx < ivl_nexus_ptrs(nex); idx += 1) { ivl_signal_type_t stype; ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; width = ivl_signal_width(sig); stype = ivl_signal_type(sig); if (stype == IVL_SIT_TRI) continue; if (stype == IVL_SIT_NONE) continue; out = stype; } return out; } /* * Generates VHDL code to fully represent a nexus. */ void draw_nexus(ivl_nexus_t nexus) { nexus_private_t *priv = new nexus_private_t; int nexus_signal_width = -1; priv->const_driver = NULL; int nptrs = ivl_nexus_ptrs(nexus); // Number of drivers for this nexus int ndrivers = 0; // First pass through connect all the signals up for (int i = 0; i < nptrs; i++) { ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i); ivl_signal_t sig; if ((sig = ivl_nexus_ptr_sig(nexus_ptr))) { vhdl_scope *scope = find_scope_for_signal(sig); if (scope) { unsigned pin = ivl_nexus_ptr_pin(nexus_ptr); link_scope_to_nexus_signal(priv, scope, sig, pin); } nexus_signal_width = ivl_signal_width(sig); } } // Second pass through make sure logic/LPMs have signal // inputs and outputs for (int i = 0; i < nptrs; i++) { ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i); ivl_net_logic_t log; ivl_lpm_t lpm; ivl_net_const_t con; if ((log = ivl_nexus_ptr_log(nexus_ptr))) { ivl_scope_t log_scope = ivl_logic_scope(log); if (!is_default_scope_instance(log_scope)) continue; vhdl_entity *ent = find_entity(log_scope); assert(ent); vhdl_scope *vhdl_scope = ent->get_arch()->get_scope(); if (visible_nexus(priv, vhdl_scope)) { // Already seen this signal in vhdl_scope } else { // Create a temporary signal to connect it to the nexus vhdl_type *type = vhdl_type::type_for(ivl_logic_width(log), false); ostringstream ss; ss << "LO" << ivl_logic_basename(log); vhdl_scope->add_decl(new vhdl_signal_decl(ss.str(), type)); link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str()); } // If this is connected to pin-0 then this nexus is driven // by this logic gate if (ivl_logic_pin(log, 0) == nexus) ndrivers++; } else if ((lpm = ivl_nexus_ptr_lpm(nexus_ptr))) { ivl_scope_t lpm_scope = ivl_lpm_scope(lpm); vhdl_entity *ent = find_entity(lpm_scope); assert(ent); vhdl_scope *vhdl_scope = ent->get_arch()->get_scope(); if (visible_nexus(priv, vhdl_scope)) { // Already seen this signal in vhdl_scope } else { // Create a temporary signal to connect the nexus // TODO: we could avoid this for IVL_LPM_PART_PV // If we already know how wide the temporary should be // (i.e. because we've seen a signal it's connected to) // then use that, otherwise use the width of the LPM int lpm_temp_width; if (nexus_signal_width != -1) lpm_temp_width = nexus_signal_width; else lpm_temp_width = ivl_lpm_width(lpm); vhdl_type *type = vhdl_type::type_for(lpm_temp_width, ivl_lpm_signed(lpm) != 0); ostringstream ss; ss << "LPM"; if (nexus == ivl_lpm_q(lpm)) ss << "_q"; else { for (unsigned d = 0; d < ivl_lpm_size(lpm); d++) { if (nexus == ivl_lpm_data(lpm, d)) ss << "_d" << d; } } ss << ivl_lpm_basename(lpm); if (!vhdl_scope->have_declared(ss.str())) vhdl_scope->add_decl(new vhdl_signal_decl(ss.str(), type)); link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str()); } // If this is connected to the LPM output then this nexus // is driven by the LPM if (ivl_lpm_q(lpm) == nexus) ndrivers++; } else if ((con = ivl_nexus_ptr_con(nexus_ptr))) { if (ivl_const_type(con) == IVL_VT_REAL) { error("No VHDL translation for real constant (%g)", ivl_const_real(con)); continue; } if (ivl_const_width(con) == 1) priv->const_driver = new vhdl_const_bit(ivl_const_bits(con)[0]); else priv->const_driver = new vhdl_const_bits(ivl_const_bits(con), ivl_const_width(con), ivl_const_signed(con) != 0); // A constant is a sort of driver ndrivers++; } } // Drive undriven nets with a constant if (ndrivers == 0) { char def = 0; int width; switch (signal_type_of_nexus(nexus, width)) { case IVL_SIT_TRI: case IVL_SIT_UWIRE: def = 'Z'; break; case IVL_SIT_TRI0: def = '0'; break; case IVL_SIT_TRI1: def = '1'; break; case IVL_SIT_TRIAND: error("No VHDL translation for triand nets"); break; case IVL_SIT_TRIOR: error("No VHDL translation for trior nets"); break; default: ; } if (def) { if (width > 1) priv->const_driver = new vhdl_bit_spec_expr(vhdl_type::std_logic(), new vhdl_const_bit(def)); else priv->const_driver = new vhdl_const_bit(def); } } // Save the private data in the nexus ivl_nexus_set_private(nexus, priv); } /* * Ensure that a nexus has been initialised. I.e. all the necessary * statements, declarations, etc. have been generated. */ static void seen_nexus(ivl_nexus_t nexus) { if (ivl_nexus_get_private(nexus) == NULL) draw_nexus(nexus); } /* * Translate a nexus to a variable reference. Given a nexus and a * scope, this function returns a reference to a signal that is * connected to the nexus and within the given scope. This signal * might not exist in the original Verilog source (even as a * compiler-generated temporary). If this nexus hasn't been * encountered before, the necessary code to connect up the nexus * will be generated. */ vhdl_var_ref *nexus_to_var_ref(vhdl_scope *scope, ivl_nexus_t nexus) { seen_nexus(nexus); nexus_private_t *priv = static_cast(ivl_nexus_get_private(nexus)); unsigned pin; string renamed(visible_nexus_signal_name(priv, scope, &pin)); vhdl_decl *decl = scope->get_decl(renamed); assert(decl); vhdl_type *type = new vhdl_type(*(decl->get_type())); vhdl_var_ref *ref = new vhdl_var_ref(renamed.c_str(), type); if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY) ref->set_slice(new vhdl_const_int(pin), 0); return ref; } // Return a variable reference for a nexus that is guaranteed to // be readable. vhdl_var_ref* readable_ref(vhdl_scope* scope, ivl_nexus_t nex) { vhdl_var_ref* ref = nexus_to_var_ref(scope, nex); vhdl_decl* decl = scope->get_decl(ref->get_name()); decl->ensure_readable(); return ref; } /* * Translate all the primitive logic gates into concurrent * signal assignments. */ static void declare_logic(vhdl_arch *arch, ivl_scope_t scope) { debug_msg("Declaring logic in scope type %s", ivl_scope_tname(scope)); int nlogs = ivl_scope_logs(scope); for (int i = 0; i < nlogs; i++) draw_logic(arch, ivl_scope_log(scope, i)); } // Replace consecutive underscores with a single underscore static void replace_consecutive_underscores(string& str) { size_t pos = str.find("__"); while (pos != string::npos) { str.replace(pos, 2, "_"); pos = str.find("__"); } } static bool is_vhdl_reserved_word(const string& word) { // This is the complete list of VHDL reserved words const char *vhdl_reserved[] = { "abs", "access", "after", "alias", "all", "and", "architecture", "array", "assert", "attribute", "begin", "block", "body", "buffer", "bus", "case", "component", "configuration", "constant", "disconnect", "downto", "else", "elsif", "end", "entity", "exit", "file", "for", "function", "generate", "generic", "group", "guarded", "if", "impure", "in", "inertial", "inout", "is", "label", "library", "linkage", "literal", "loop", "map", "mod", "nand", "new", "next", "nor", "not", "null", "of", "on", "open", "or", "others", "out", "package", "port", "postponed", "procedure", "process", "pure", "range", "record", "register", "reject", "rem", "report", "return", "rol", "ror", "select", "severity", "signal", "shared", "sla", "sll", "sra", "srl", "subtype", "then", "to", "transport", "type", "unaffected", "units", "until", "use", "variable", "wait", "when", "while", "with", "xnor", "xor", NULL }; for (const char **p = vhdl_reserved; *p != NULL; p++) { if (strcasecmp(*p, word.c_str()) == 0) return true; } return false; } // Return a valid VHDL name for a Verilog module static string valid_entity_name(const string& module_name) { string name(module_name); replace_consecutive_underscores(name); if (name[0] == '_') name = "module" + name; if (*name.rbegin() == '_') name += "module"; if (is_vhdl_reserved_word(name)) name += "_module"; ostringstream ss; int i = 1; ss << name; while (find_entity(ss.str())) { // Keep adding an extra number until we get a unique name ss.str(""); ss << name << i++; } return ss.str(); } // Make sure a signal name conforms to VHDL naming rules. string make_safe_name(ivl_signal_t sig) { string base(ivl_signal_basename(sig)); if (ivl_signal_local(sig)) base = "tmp" + base; if (base[0] == '_') base = "sig" + base; if (*base.rbegin() == '_') base += "sig"; // Can't have two consecutive underscores replace_consecutive_underscores(base); // A signal name may not be the same as a component name if (find_entity(base) != NULL) base += "_sig"; if (is_vhdl_reserved_word(base)) base += "_sig"; return base; } // Check if `name' differs from an existing name only in case and // make it unique if it does. static void avoid_name_collision(string& name, vhdl_scope* scope) { if (scope->name_collides(name)) { name += "_"; ostringstream ss; int i = 1; do { // Keep adding an extra number until we get a unique name ss.str(""); ss << name << i++; } while (scope->name_collides(ss.str())); name = ss.str(); } } // Concatenate the expanded genvar values together to make a unique // instance name // This isn't ideal: it would be better to replace the Verilog // generate with an equivalent VHDL generate, but this isn't possible // with the current API static string genvar_unique_suffix(ivl_scope_t scope) { ostringstream suffix; while (scope && ivl_scope_type(scope) == IVL_SCT_GENERATE) { for (unsigned i = 0; i < ivl_scope_params(scope); i++) { ivl_parameter_t param = ivl_scope_param(scope, i); ivl_expr_t e = ivl_parameter_expr(param); if (ivl_expr_type(e) == IVL_EX_NUMBER) { vhdl_expr* value = translate_expr(e); assert(value); value = value->cast(vhdl_type::integer()); suffix << "_" << ivl_parameter_basename(param); value->emit(suffix, 0); delete value; } else { error("Only numeric genvars supported at the moment"); return "_ERROR"; // Never used } } scope = ivl_scope_parent(scope); } return suffix.str(); } // Declare a single signal in a scope static void declare_one_signal(vhdl_entity *ent, ivl_signal_t sig, ivl_scope_t scope) { remember_signal(sig, ent->get_arch()->get_scope()); string name(make_safe_name(sig)); name += genvar_unique_suffix(scope); avoid_name_collision(name, ent->get_arch()->get_scope()); rename_signal(sig, name); vhdl_type *sig_type; unsigned dimensions = ivl_signal_dimensions(sig); if (dimensions > 0) { // Arrays are implemented by generating a separate type // declaration for each array, and then declaring a // signal of that type if (dimensions > 1) { error("> 1 dimension arrays not implemented yet"); return; } string type_name = name + "_Type"; vhdl_type *base_type = vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0); int lsb = ivl_signal_array_base(sig); int msb = lsb + ivl_signal_array_count(sig) - 1; vhdl_type *array_type = vhdl_type::array_of(base_type, type_name, msb, lsb); vhdl_decl *array_decl = new vhdl_type_decl(type_name, array_type); ent->get_arch()->get_scope()->add_decl(array_decl); sig_type = new vhdl_type(*array_type); } else { sig_type = vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0, 0, ivl_signal_type(sig) == IVL_SIT_UWIRE); } ivl_signal_port_t mode = ivl_signal_port(sig); switch (mode) { case IVL_SIP_NONE: { vhdl_decl *decl = new vhdl_signal_decl(name, sig_type); ostringstream ss; if (ivl_signal_local(sig)) { ss << "Temporary created at " << ivl_signal_file(sig) << ":" << ivl_signal_lineno(sig); } else { ss << "Declared at " << ivl_signal_file(sig) << ":" << ivl_signal_lineno(sig); } decl->set_comment(ss.str().c_str()); ent->get_arch()->get_scope()->add_decl(decl); } break; case IVL_SIP_INPUT: ent->get_scope()->add_decl (new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_IN)); break; case IVL_SIP_OUTPUT: { vhdl_port_decl *decl = new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_OUT); ent->get_scope()->add_decl(decl); } if (ivl_signal_type(sig) == IVL_SIT_REG) { // A registered output // In Verilog the output and reg can have the // same name: this is not valid in VHDL // Instead a new signal foo_Reg is created // which represents the register std::string newname(name); newname += "_Reg"; rename_signal(sig, newname); vhdl_type *reg_type = new vhdl_type(*sig_type); ent->get_arch()->get_scope()->add_decl (new vhdl_signal_decl(newname, reg_type)); // Create a concurrent assignment statement to // connect the register to the output ent->get_arch()->add_stmt (new vhdl_cassign_stmt (new vhdl_var_ref(name.c_str(), NULL), new vhdl_var_ref(newname.c_str(), NULL))); } break; case IVL_SIP_INOUT: ent->get_scope()->add_decl (new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_INOUT)); break; default: assert(false); } } // Declare all signals and ports for a scope. // This is done in two phases: first the ports are added, then // internal signals. Making two passes like this ensures ports get // first pick of names when there is a collision. static void declare_signals(vhdl_entity *ent, ivl_scope_t scope) { debug_msg("Declaring signals in scope type %s", ivl_scope_tname(scope)); int nsigs = ivl_scope_sigs(scope); for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); if (ivl_signal_port(sig) != IVL_SIP_NONE) declare_one_signal(ent, sig, scope); } for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); if (ivl_signal_port(sig) == IVL_SIP_NONE) declare_one_signal(ent, sig, scope); } } /* * Generate VHDL for LPM instances in a module. */ static void declare_lpm(vhdl_arch *arch, ivl_scope_t scope) { int nlpms = ivl_scope_lpms(scope); for (int i = 0; i < nlpms; i++) { ivl_lpm_t lpm = ivl_scope_lpm(scope, i); if (draw_lpm(arch, lpm) != 0) error("Failed to translate LPM %s", ivl_lpm_name(lpm)); } } /* * Map two signals together in an instantiation. * The signals are joined by a nexus. */ static void map_signal(ivl_signal_t to, vhdl_entity *parent, vhdl_comp_inst *inst) { // TODO: Work for multiple words ivl_nexus_t nexus = ivl_signal_nex(to, 0); seen_nexus(nexus); vhdl_scope *arch_scope = parent->get_arch()->get_scope(); nexus_private_t *priv = static_cast(ivl_nexus_get_private(nexus)); assert(priv); vhdl_expr *map_to = NULL; const string name(make_safe_name(to)); // We can only map ports to signals or constants if (visible_nexus(priv, arch_scope)) { vhdl_var_ref *ref = nexus_to_var_ref(parent->get_arch()->get_scope(), nexus); // If we're mapping an output of this entity to an output of // the child entity, then VHDL will not let us read the value // of the signal (i.e. it must pass straight through). // However, Verilog allows the signal to be read in the parent. // The solution used here is to create an intermediate signal // and connect it to both ports. vhdl_decl* from_decl = parent->get_arch()->get_scope()->get_decl(ref->get_name()); if (!from_decl->is_readable() && !arch_scope->have_declared(name + "_Readable")) { vhdl_decl* tmp_decl = new vhdl_signal_decl(name + "_Readable", ref->get_type()); // Add a comment to explain what this is for tmp_decl->set_comment("Needed to connect outputs"); arch_scope->add_decl(tmp_decl); parent->get_arch()->add_stmt (new vhdl_cassign_stmt(from_decl->make_ref(), tmp_decl->make_ref())); map_to = tmp_decl->make_ref(); } else map_to = ref; } else if (priv->const_driver && ivl_signal_port(to) == IVL_SIP_INPUT) { map_to = priv->const_driver; priv->const_driver = NULL; } else { // This nexus isn't attached to anything in the parent return; } inst->map_port(name, map_to); } /* * Find all the port mappings of a module instantiation. */ static void port_map(ivl_scope_t scope, vhdl_entity *parent, vhdl_comp_inst *inst) { // Find all the port mappings int nsigs = ivl_scope_sigs(scope); for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); ivl_signal_port_t mode = ivl_signal_port(sig); switch (mode) { case IVL_SIP_NONE: // Internal signals don't appear in the port map break; case IVL_SIP_INPUT: case IVL_SIP_OUTPUT: case IVL_SIP_INOUT: map_signal(sig, parent, inst); break; default: assert(false); } } } /* * Create a VHDL function from a Verilog function definition. */ static int draw_function(ivl_scope_t scope, ivl_scope_t parent) { assert(ivl_scope_type(scope) == IVL_SCT_FUNCTION); debug_msg("Generating function %s (%s)", ivl_scope_tname(scope), ivl_scope_name(scope)); // Find the containing entity vhdl_entity *ent = find_entity(parent); assert(ent); const char *funcname = ivl_scope_tname(scope); assert(!ent->get_arch()->get_scope()->have_declared(funcname)); // The return type is worked out from the output port vhdl_function *func = new vhdl_function(funcname, NULL); // Set the parent scope of this function to be the containing // architecture. This allows us to look up non-local variables // referenced in the body, but if we do the `impure' flag must // be set on the function // (There are actually two VHDL scopes in a function: the local // variables and the formal parameters hence the call to get_parent) func->get_scope()->get_parent()->set_parent(ent->get_arch()->get_scope()); // First we add the input/output parameters in order int nports = ivl_scope_ports(scope); for (int i = 0; i < nports; i++) { ivl_signal_t sig = ivl_scope_port(scope, i); vhdl_type *sigtype = vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0); string signame(make_safe_name(sig)); switch (ivl_signal_port(sig)) { case IVL_SIP_INPUT: func->add_param(new vhdl_param_decl(signame.c_str(), sigtype)); break; case IVL_SIP_OUTPUT: // The magic variable _Result holds the return value signame = funcname; signame += "_Result"; func->set_type(new vhdl_type(*sigtype)); func->get_scope()->add_decl (new vhdl_var_decl(signame, sigtype)); break; default: // Only expecting inputs and outputs assert(false); } remember_signal(sig, func->get_scope()); rename_signal(sig, signame); } int nsigs = ivl_scope_sigs(scope); for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); if (ivl_signal_port(sig) == IVL_SIP_NONE) { vhdl_type *sigtype = vhdl_type::type_for( ivl_signal_width(sig), ivl_signal_signed(sig) != 0); string signame(make_safe_name(sig)); func->get_scope()->add_decl( new vhdl_var_decl(signame, sigtype)); remember_signal(sig, func->get_scope()); rename_signal(sig, signame); } } // Non-blocking assignment not allowed in functions func->get_scope()->set_allow_signal_assignment(false); set_active_entity(ent); { draw_stmt(func, func->get_container(), ivl_scope_def(scope)); } set_active_entity(NULL); // Add a forward declaration too in case it is called by // another function that has already been added ent->get_arch()->get_scope()->add_forward_decl (new vhdl_forward_fdecl(func)); ostringstream ss; ss << "Generated from function " << funcname << " at " << ivl_scope_def_file(scope) << ":" << ivl_scope_def_lineno(scope); func->set_comment(ss.str().c_str()); ent->get_arch()->get_scope()->add_decl(func); return 0; } /* * Create the signals necessary to expand this task later. */ static int draw_task(ivl_scope_t scope, ivl_scope_t parent) { assert(ivl_scope_type(scope) == IVL_SCT_TASK); // Find the containing entity vhdl_entity *ent = find_entity(parent); assert(ent); const char *taskname = ivl_scope_tname(scope); int nsigs = ivl_scope_sigs(scope); for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); vhdl_type *sigtype = vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0); string signame(make_safe_name(sig)); // Check this signal isn't declared in the outer scope if (ent->get_arch()->get_scope()->have_declared(signame)) { signame += "_"; signame += taskname; } vhdl_signal_decl *decl = new vhdl_signal_decl(signame, sigtype); ostringstream ss; ss << "Declared at " << ivl_signal_file(sig) << ":" << ivl_signal_lineno(sig) << " (in task " << taskname << ")"; decl->set_comment(ss.str().c_str()); ent->get_arch()->get_scope()->add_decl(decl); remember_signal(sig, ent->get_arch()->get_scope()); rename_signal(sig, signame); } return 0; } /* * Create an empty VHDL entity for a Verilog module. */ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) { assert(ivl_scope_type(scope) == IVL_SCT_MODULE); // The type name will become the entity name const string tname = valid_entity_name(ivl_scope_tname(scope)); // Verilog does not have the entity/architecture distinction // so we always create a pair and associate the architecture // with the entity for convenience (this also means that we // retain a 1-to-1 mapping of scope to VHDL element) vhdl_arch *arch = new vhdl_arch(tname, "from_verilog"); vhdl_entity *ent = new vhdl_entity(tname, arch, depth); // Calculate the VHDL units to use for time values ent->set_time_units(ivl_scope_time_units(scope), ivl_scope_time_precision(scope)); // Build a comment to add to the entity/architecture ostringstream ss; ss << "Generated from Verilog module " << ivl_scope_tname(scope) << " (" << ivl_scope_def_file(scope) << ":" << ivl_scope_def_lineno(scope) << ")"; unsigned nparams = ivl_scope_params(scope); for (unsigned i = 0; i < nparams; i++) { ivl_parameter_t param = ivl_scope_param(scope, i); ss << "\n " << ivl_parameter_basename(param) << " = "; ivl_expr_t value = ivl_parameter_expr(param); switch (ivl_expr_type(value)) { case IVL_EX_STRING: ss << "\"" << ivl_expr_string(value) << "\""; break; case IVL_EX_NUMBER: ss << ivl_expr_uvalue(value); break; case IVL_EX_REALNUM: ss << ivl_expr_dvalue(value); break; default: assert(false); } } arch->set_comment(ss.str()); ent->set_comment(ss.str()); remember_entity(ent, scope); } /* * A first pass through the hierarchy: create VHDL entities for * each unique Verilog module type. */ extern "C" int draw_skeleton_scope(ivl_scope_t scope, void *) { static int depth = 0; if (seen_this_scope_type(scope)) return 0; // Already generated a skeleton for this scope type debug_msg("Initial visit to scope type %s at depth %d", ivl_scope_tname(scope), depth); switch (ivl_scope_type(scope)) { case IVL_SCT_MODULE: create_skeleton_entity_for(scope, depth); break; case IVL_SCT_FORK: error("No translation for fork statements yet"); return 1; default: // The other scope types are expanded later on break; } ++depth; int rc = ivl_scope_children(scope, draw_skeleton_scope, NULL); --depth; return rc; } extern "C" int draw_all_signals(ivl_scope_t scope, void *) { if (!is_default_scope_instance(scope)) return 0; // Not interested in this instance if (ivl_scope_type(scope) == IVL_SCT_MODULE) { vhdl_entity *ent = find_entity(scope); assert(ent); declare_signals(ent, scope); } else if (ivl_scope_type(scope) == IVL_SCT_GENERATE) { // Because generate scopes don't appear in the // output VHDL all their signals are added to the // containing entity (after being uniqued) ivl_scope_t parent = ivl_scope_parent(scope); while (ivl_scope_type(parent) == IVL_SCT_GENERATE) parent = ivl_scope_parent(scope); vhdl_entity* ent = find_entity(parent); assert(ent); declare_signals(ent, scope); } return ivl_scope_children(scope, draw_all_signals, scope); } /* * Draw all tasks and functions in the hierarchy. */ extern "C" int draw_functions(ivl_scope_t scope, void *_parent) { if (!is_default_scope_instance(scope)) return 0; // Not interested in this instance ivl_scope_t parent = static_cast(_parent); if (ivl_scope_type(scope) == IVL_SCT_FUNCTION) { if (draw_function(scope, parent) != 0) return 1; } else if (ivl_scope_type(scope) == IVL_SCT_TASK) { if (draw_task(scope, parent) != 0) return 1; } return ivl_scope_children(scope, draw_functions, scope); } /* * Make concurrent assignments for constants in nets. This works * bottom-up so that the driver is in the lowest instance it can. * This also has the side effect of generating all the necessary * nexus code. */ extern "C" int draw_constant_drivers(ivl_scope_t scope, void *) { if (!is_default_scope_instance(scope)) return 0; // Not interested in this instance ivl_scope_children(scope, draw_constant_drivers, scope); if (ivl_scope_type(scope) == IVL_SCT_MODULE) { vhdl_entity *ent = find_entity(scope); assert(ent); int nsigs = ivl_scope_sigs(scope); for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); for (unsigned j = ivl_signal_array_base(sig); j < ivl_signal_array_count(sig); j++) { // Make sure the nexus code is generated ivl_nexus_t nex = ivl_signal_nex(sig, j); if (!nex) continue; // skip virtual pins seen_nexus(nex); nexus_private_t *priv = static_cast(ivl_nexus_get_private(nex)); assert(priv); vhdl_scope *arch_scope = ent->get_arch()->get_scope(); if (priv->const_driver && ivl_signal_port(sig) != IVL_SIP_INPUT) { // Don't drive inputs assert(j == 0); // TODO: Make work for more words vhdl_var_ref *ref = nexus_to_var_ref(arch_scope, nex); ent->get_arch()->add_stmt (new vhdl_cassign_stmt(ref, priv->const_driver)); priv->const_driver = NULL; } // Connect up any signals which are wired together in the // same nexus scope_nexus_t *sn = visible_nexus(priv, arch_scope); // Make sure we don't drive inputs if (ivl_signal_port(sn->sig) != IVL_SIP_INPUT) { for (list::const_iterator it = sn->connect.begin(); it != sn->connect.end(); ++it) { vhdl_type* rtype = vhdl_type::type_for(ivl_signal_width(sn->sig), ivl_signal_signed(sn->sig)); vhdl_type* ltype = vhdl_type::type_for(ivl_signal_width(*it), ivl_signal_signed(*it)); vhdl_var_ref *rref = new vhdl_var_ref(get_renamed_signal(sn->sig).c_str(), rtype); vhdl_var_ref *lref = new vhdl_var_ref(get_renamed_signal(*it).c_str(), ltype); // Make sure the LHS and RHS have the same type vhdl_expr* rhs = rref->cast(lref->get_type()); ent->get_arch()->add_stmt(new vhdl_cassign_stmt(lref, rhs)); } } sn->connect.clear(); } } } return 0; } extern "C" int draw_all_logic_and_lpm(ivl_scope_t scope, void *) { if (!is_default_scope_instance(scope)) return 0; // Not interested in this instance if (ivl_scope_type(scope) == IVL_SCT_MODULE) { vhdl_entity *ent = find_entity(scope); assert(ent); set_active_entity(ent); { declare_logic(ent->get_arch(), scope); declare_lpm(ent->get_arch(), scope); } set_active_entity(NULL); } return ivl_scope_children(scope, draw_all_logic_and_lpm, scope); } extern "C" int draw_hierarchy(ivl_scope_t scope, void *_parent) { if (ivl_scope_type(scope) == IVL_SCT_MODULE && _parent) { ivl_scope_t parent = static_cast(_parent); // Skip over any containing generate scopes while (ivl_scope_type(parent) == IVL_SCT_GENERATE) parent = ivl_scope_parent(parent); if (!is_default_scope_instance(parent)) return 0; // Not generating code for the parent instance so // don't generate for the child vhdl_entity *ent = find_entity(scope); assert(ent); vhdl_entity *parent_ent = find_entity(parent); assert(parent_ent); vhdl_arch *parent_arch = parent_ent->get_arch(); assert(parent_arch != NULL); // Create a forward declaration for it vhdl_scope *parent_scope = parent_arch->get_scope(); if (!parent_scope->have_declared(ent->get_name())) { vhdl_decl *comp_decl = vhdl_component_decl::component_decl_for(ent); parent_arch->get_scope()->add_decl(comp_decl); } // And an instantiation statement string inst_name = ivl_scope_basename(scope); inst_name += genvar_unique_suffix(ivl_scope_parent(scope)); if (inst_name == ent->get_name() || parent_scope->name_collides(inst_name) || find_entity(inst_name) != NULL || is_vhdl_reserved_word(inst_name)) { // Would produce an invalid instance name inst_name += "_inst"; } // Need to replace any [ and ] characters that result // from generate statements string::size_type loc = inst_name.find('[', 0); if (loc != string::npos) inst_name.erase(loc, 1); loc = inst_name.find(']', 0); if (loc != string::npos) inst_name.erase(loc, 1); // No leading or trailing underscores if (inst_name[0] == '_') inst_name = "inst" + inst_name; if (*inst_name.rbegin() == '_') inst_name += "inst"; // Can't have two consecutive underscores replace_consecutive_underscores(inst_name); // Make sure the name doesn't collide with anything we've // already declared avoid_name_collision(inst_name, parent_arch->get_scope()); vhdl_comp_inst *inst = new vhdl_comp_inst(inst_name.c_str(), ent->get_name().c_str()); port_map(scope, parent_ent, inst); ostringstream ss; ss << "Generated from instantiation at " << ivl_scope_file(scope) << ":" << ivl_scope_lineno(scope); inst->set_comment(ss.str().c_str()); parent_arch->add_stmt(inst); } return ivl_scope_children(scope, draw_hierarchy, scope); } int draw_scope(ivl_scope_t scope, void *_parent) { int rc = draw_skeleton_scope(scope, _parent); if (rc != 0) return rc; rc = draw_all_signals(scope, _parent); if (rc != 0) return rc; rc = draw_all_logic_and_lpm(scope, _parent); if (rc != 0) return rc; rc = draw_hierarchy(scope, _parent); if (rc != 0) return rc; rc = draw_functions(scope, _parent); if (rc != 0) return rc; rc = draw_constant_drivers(scope, _parent); if (rc != 0) return rc; return 0; } iverilog-10_1/tgt-vhdl/state.cc000066400000000000000000000223361265551621300165300ustar00rootroot00000000000000/* * Managing global state for the VHDL code generator. * * Copyright (C) 2009 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "state.hh" #include "vhdl_syntax.hh" #include "vhdl_target.h" #include #include #include #include #include #include using namespace std; /* * This file stores all the global state required during VHDL code * generation. At present we store the following: * * - A mapping from Verilog signals to the VHDL scope (entity, etc.) * where it is found, and the name of the corresponding VHDL signal. * This allows us to support renaming invalid Verilog signal names * to valid VHDL ones. * * - The set of all VHDL entities generated. * * - The currently active entity. "Active" here means that we are * currently generating code for a process inside the corresponding * scope. This is useful, for example, if a statement or expression * in a process needs to add are referencing something in the containing * architecture object. */ /* * Maps a signal to the scope it is defined within. Also * provides a mechanism for renaming signals -- i.e. when * an output has the same name as register: valid in Verilog * but not in VHDL, so two separate signals need to be * defined. */ struct signal_defn_t { std::string renamed; // The name of the VHDL signal vhdl_scope *scope; // The scope where it is defined }; // All entities to emit. // These are stored in a list rather than a set so the first // entity added will correspond to the first (top) Verilog module // encountered and hence it will appear first in the output file. static entity_list_t g_entities; // Store the mapping of ivl scope names to entity names typedef map scope_name_map_t; static scope_name_map_t g_scope_names; typedef std::map signal_defn_map_t; static signal_defn_map_t g_known_signals; static vhdl_entity *g_active_entity = NULL; // Set of scopes that are treated as the default examples of // that type. Any other scopes of the same type are ignored. typedef vector default_scopes_t; static default_scopes_t g_default_scopes; // True if signal `sig' has already been encountered by the code // generator. This means we have already assigned it to a VHDL code // object and possibly renamed it. bool seen_signal_before(ivl_signal_t sig) { return g_known_signals.find(sig) != g_known_signals.end(); } // Remember the association of signal to a VHDL code object (typically // an entity). void remember_signal(ivl_signal_t sig, vhdl_scope *scope) { assert(!seen_signal_before(sig)); signal_defn_t defn = { ivl_signal_basename(sig), scope }; g_known_signals[sig] = defn; } // Change the VHDL name of a Verilog signal. void rename_signal(ivl_signal_t sig, const std::string &renamed) { assert(seen_signal_before(sig)); g_known_signals[sig].renamed = renamed; } // Given a Verilog signal, return the VHDL code object where it should // be defined. Note that this can return a NULL pointer if `sig' hasn't // be encountered yet. vhdl_scope *find_scope_for_signal(ivl_signal_t sig) { if (seen_signal_before(sig)) return g_known_signals[sig].scope; else return NULL; } // Get the name of the VHDL signal corresponding to Verilog signal `sig'. const std::string &get_renamed_signal(ivl_signal_t sig) { assert(seen_signal_before(sig)); return g_known_signals[sig].renamed; } // TODO: Can we dispose of this??? // -> This is only used in logic.cc to get the type of a signal connected // to a logic device -> we should be able to get this from the nexus ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope) { signal_defn_map_t::const_iterator it; for (it = g_known_signals.begin(); it != g_known_signals.end(); ++it) { if (((*it).second.scope == scope || (*it).second.scope == scope->get_parent()) && (*it).second.renamed == name) return (*it).first; } assert(false); return NULL; } // Compare the name of an entity against a string struct cmp_ent_name { cmp_ent_name(const string& n) : name_(n) {} bool operator()(const vhdl_entity* ent) const { return ent->get_name() == name_; } const string& name_; }; // Find an entity given its name. vhdl_entity* find_entity(const string& name) { entity_list_t::const_iterator it = find_if(g_entities.begin(), g_entities.end(), cmp_ent_name(name)); if (it != g_entities.end()) return *it; else return NULL; } // Find a VHDL entity given a Verilog module scope. The VHDL entity // name should be the same as the Verilog module type name. // Note that this will return NULL if no entity has been recorded // for this scope type. vhdl_entity* find_entity(ivl_scope_t scope) { // Skip over generate scopes while (ivl_scope_type(scope) == IVL_SCT_GENERATE) scope = ivl_scope_parent(scope); assert(ivl_scope_type(scope) == IVL_SCT_MODULE); if (is_default_scope_instance(scope)) { scope_name_map_t::iterator it = g_scope_names.find(scope); if (it != g_scope_names.end()) return find_entity((*it).second); else return NULL; } else { const char *tname = ivl_scope_tname(scope); for (scope_name_map_t::iterator it = g_scope_names.begin(); it != g_scope_names.end(); ++it) { if (strcmp(tname, ivl_scope_tname((*it).first)) == 0) return find_entity((*it).second); } return NULL; } } // Add an entity/architecture pair to the list of entities to emit. void remember_entity(vhdl_entity* ent, ivl_scope_t scope) { g_entities.push_back(ent); g_scope_names[scope] = ent->get_name(); } // Print all VHDL entities, in order, to the specified output stream. void emit_all_entities(std::ostream& os, int max_depth) { for (entity_list_t::iterator it = g_entities.begin(); it != g_entities.end(); ++it) { if ((max_depth == 0 || (*it)->depth < max_depth)) (*it)->emit(os); } } // Release all memory for the VHDL objects. No vhdl_element pointers // will be valid after this call. void free_all_vhdl_objects() { int freed = vhdl_element::free_all_objects(); debug_msg("Deallocated %d VHDL syntax objects", freed); size_t total = vhdl_element::total_allocated(); debug_msg("%d total bytes used for VHDL syntax objects", total); g_entities.clear(); } // Return the currently active entity vhdl_entity *get_active_entity() { return g_active_entity; } // Change the currently active entity void set_active_entity(vhdl_entity *ent) { g_active_entity = ent; } /* * True if two scopes have the same type name. */ static bool same_scope_type_name(ivl_scope_t a, ivl_scope_t b) { if (strcmp(ivl_scope_tname(a), ivl_scope_tname(b)) != 0) return false; unsigned nparams_a = ivl_scope_params(a); unsigned nparams_b = ivl_scope_params(b); if (nparams_a != nparams_b) return false; for (unsigned i = 0; i < nparams_a; i++) { ivl_parameter_t param_a = ivl_scope_param(a, i); ivl_parameter_t param_b = ivl_scope_param(b, i); if (strcmp(ivl_parameter_basename(param_a), ivl_parameter_basename(param_b)) != 0) return false; ivl_expr_t value_a = ivl_parameter_expr(param_a); ivl_expr_t value_b = ivl_parameter_expr(param_b); if (ivl_expr_type(value_a) != ivl_expr_type(value_b)) return false; switch (ivl_expr_type(value_a)) { case IVL_EX_STRING: if (strcmp(ivl_expr_string(value_a), ivl_expr_string(value_b)) != 0) return false; break; case IVL_EX_NUMBER: if (ivl_expr_uvalue(value_a) != ivl_expr_uvalue(value_b)) return false; break; default: assert(false); } } return true; } /* * True if we have already seen a scope with this type before. * If the result is `false' then s is stored in the set of seen * scopes. */ bool seen_this_scope_type(ivl_scope_t s) { if (find_if(g_default_scopes.begin(), g_default_scopes.end(), bind1st(ptr_fun(same_scope_type_name), s)) == g_default_scopes.end()) { g_default_scopes.push_back(s); return false; } else return true; } /* * True if this scope is the default example of this scope type. * All other instances of this scope type are ignored. */ bool is_default_scope_instance(ivl_scope_t s) { return find(g_default_scopes.begin(), g_default_scopes.end(), s) != g_default_scopes.end(); } iverilog-10_1/tgt-vhdl/state.hh000066400000000000000000000036631265551621300165440ustar00rootroot00000000000000/* * Managing global state for the VHDL code generator. * * Copyright (C) 2009 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INC_VHDL_STATE_HH #define INC_VHDL_STATE_HH #include "ivl_target.h" #include #include class vhdl_scope; class vhdl_entity; // Mapping of Verilog to VHDL signals bool seen_signal_before(ivl_signal_t sig); void remember_signal(ivl_signal_t sig, vhdl_scope *scope); void rename_signal(ivl_signal_t sig, const std::string &renamed); vhdl_scope *find_scope_for_signal(ivl_signal_t sig); const std::string &get_renamed_signal(ivl_signal_t sig); ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope); // Manage the set of VHDL entities void remember_entity(vhdl_entity *ent, ivl_scope_t scope); vhdl_entity* find_entity(ivl_scope_t scope); vhdl_entity* find_entity(const std::string& name); void emit_all_entities(std::ostream& os, int max_depth); void free_all_vhdl_objects(); // Get and set the active entity vhdl_entity *get_active_entity(); void set_active_entity(vhdl_entity *ent); // Manage mapping of scopes to a single VHDL entity bool is_default_scope_instance(ivl_scope_t s); bool seen_this_scope_type(ivl_scope_t s); #endif // #ifndef INC_VHDL_STATE_HH iverilog-10_1/tgt-vhdl/stmt.cc000066400000000000000000001576261265551621300164120ustar00rootroot00000000000000/* * VHDL code generation for statements. * * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "state.hh" #include #include #include #include #include #include #include #include static void emit_wait_for_0(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, vhdl_expr *expr); /* * VHDL has no real equivalent of Verilog's $finish task. The * current solution is to use `assert false ...' to terminate * the simulator. This isn't great, as the simulator will * return a failure exit code when in fact it completed * successfully. * * An alternative is to use the VHPI interface supported by * some VHDL simulators and implement the $finish functionality * in C. This function can be enabled with the flag * -puse-vhpi-finish=1. */ static int draw_stask_finish(vhdl_procedural *, stmt_container *container, ivl_statement_t) { const char *use_vhpi = ivl_design_flag(get_vhdl_design(), "use-vhpi-finish"); if (strcmp(use_vhpi, "1") == 0) { //get_active_entity()->requires_package("work.Verilog_Support"); container->add_stmt(new vhdl_pcall_stmt("work.Verilog_Support.Finish")); } else { container->add_stmt( new vhdl_report_stmt(new vhdl_const_string("SIMULATION FINISHED"), SEVERITY_FAILURE)); } return 0; } static char parse_octal(const char *p) { assert(*p && *(p+1) && *(p+2)); assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+2))); return (*p - '0') * 64 + (*(p+1) - '0') * 8 + (*(p+2) - '0') * 1; } // Generate VHDL report statements for Verilog $display/$write static int draw_stask_display(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { vhdl_binop_expr *text = new vhdl_binop_expr(VHDL_BINOP_CONCAT, vhdl_type::string()); const int count = ivl_stmt_parm_count(stmt); int i = 0; while (i < count) { // $display may have an empty parameter, in which case // the expression will be null // The behaviour here seems to be to output a space ivl_expr_t net = ivl_stmt_parm(stmt, i++); if (net == NULL) { text->add_expr(new vhdl_const_string(" ")); continue; } if (ivl_expr_type(net) == IVL_EX_STRING) { ostringstream ss; for (const char *p = ivl_expr_string(net); *p; p++) { if (*p == '\\') { // Octal escape char ch = parse_octal(p+1); if (ch == '\n') { // Is there a better way of handling newlines? // Maybe generate another report statement } else ss << ch; p += 3; } else if (*p == '%' && *(++p) != '%') { // Flush the output string up to this point text->add_expr(new vhdl_const_string(ss.str())); ss.str(""); // Skip over width for now while (isdigit(*p)) ++p; switch (*p) { case 'm': // TODO: we can get the module name via attributes cerr << "Warning: no VHDL translation for %m format code" << endl; break; default: { assert(i < count); ivl_expr_t netp = ivl_stmt_parm(stmt, i++); assert(netp); vhdl_expr *base = translate_expr(netp); if (NULL == base) return 1; emit_wait_for_0(proc, container, stmt, base); text->add_expr(base->cast(text->get_type())); } } } else ss << *p; } // Emit any non-empty string data left in the buffer if (!ss.str().empty()) text->add_expr(new vhdl_const_string(ss.str())); } else { vhdl_expr *base = translate_expr(net); if (NULL == base) return 1; emit_wait_for_0(proc, container, stmt, base); text->add_expr(base->cast(text->get_type())); } } if (count == 0) text->add_expr(new vhdl_const_string("")); container->add_stmt(new vhdl_report_stmt(text)); return 0; } /* * Generate VHDL for system tasks (like $display). Not all of * these are supported. */ static int draw_stask(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { const char *name = ivl_stmt_name(stmt); if (strcmp(name, "$display") == 0) return draw_stask_display(proc, container, stmt); else if (strcmp(name, "$write") == 0) return draw_stask_display(proc, container, stmt); else if (strcmp(name, "$finish") == 0) return draw_stask_finish(proc, container, stmt); else { vhdl_seq_stmt *result = new vhdl_null_stmt(); ostringstream ss; ss << "Unsupported system task " << name << " omitted here (" << ivl_stmt_file(stmt) << ":" << ivl_stmt_lineno(stmt) << ")"; result->set_comment(ss.str()); container->add_stmt(result); cerr << "Warning: no VHDL translation for system task " << name << endl; return 0; } } /* * Generate VHDL for a block of Verilog statements. If this block * doesn't have its own scope then this function does nothing, other * than recursively translate the block's statements and add them * to the process. This is OK as the stmt_container class behaves * like a Verilog block. * * If this block has its own scope with local variables then these * are added to the process as local variables and the statements * are generated as above. */ static int draw_block(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { ivl_scope_t block_scope = ivl_stmt_block_scope(stmt); if (block_scope) { int nsigs = ivl_scope_sigs(block_scope); for (int i = 0; i < nsigs; i++) { ivl_signal_t sig = ivl_scope_sig(block_scope, i); remember_signal(sig, proc->get_scope()); vhdl_type* type = vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig)); proc->get_scope()->add_decl (new vhdl_var_decl(make_safe_name(sig), type)); } } int count = ivl_stmt_block_count(stmt); for (int i = 0; i < count; i++) { ivl_statement_t stmt_i = ivl_stmt_block_stmt(stmt, i); if (draw_stmt(proc, container, stmt_i, is_last && i == count - 1) != 0) return 1; } return 0; } /* * A no-op statement. This corresponds to a `null' statement in VHDL. */ static int draw_noop(vhdl_procedural *, stmt_container *container, ivl_statement_t) { container->add_stmt(new vhdl_null_stmt()); return 0; } static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope) { ivl_signal_t sig = ivl_lval_sig(lval); if (!sig) { error("Only signals as lvals supported at the moment"); return NULL; } vhdl_expr *base = NULL; ivl_expr_t e_off = ivl_lval_part_off(lval); if (NULL == e_off) e_off = ivl_lval_idx(lval); if (e_off) { if ((base = translate_expr(e_off)) == NULL) return NULL; vhdl_type integer(VHDL_TYPE_INTEGER); base = base->cast(&integer); } unsigned lval_width = ivl_lval_width(lval); string signame(get_renamed_signal(sig)); vhdl_decl *decl = scope->get_decl(signame); assert(decl); // Verilog allows assignments to elements that are constant in VHDL: // function parameters, for example // To work around this we generate a local variable to shadow the // constant and assign to that if (decl->assignment_type() == vhdl_decl::ASSIGN_CONST) { const string shadow_name = signame + "_Shadow"; vhdl_var_decl* shadow_decl = new vhdl_var_decl(shadow_name, decl->get_type()); shadow_decl->set_initial (new vhdl_var_ref(signame, decl->get_type())); scope->add_decl(shadow_decl); // Make sure all future references to this signal use the // shadow variable rename_signal(sig, shadow_name); // ...and use this new variable as the assignment LHS decl = shadow_decl; } vhdl_type *ltype = new vhdl_type(*decl->get_type()); vhdl_var_ref *lval_ref = new vhdl_var_ref(decl->get_name(), ltype); if (base) { if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY) lval_ref->set_slice(base, 0); else if (ivl_signal_width(sig) > 1) lval_ref->set_slice(base, lval_width - 1); } return lval_ref; } static bool assignment_lvals(ivl_statement_t stmt, vhdl_procedural *proc, list &lvals) { int nlvals = ivl_stmt_lvals(stmt); for (int i = 0; i < nlvals; i++) { ivl_lval_t lval = ivl_stmt_lval(stmt, i); vhdl_var_ref *lhs = make_assign_lhs(lval, proc->get_scope()); if (NULL == lhs) return false; lvals.push_back(lhs); } return true; } /* * Generate the right sort of assignment statement for assigning * `lhs' to `rhs'. */ static vhdl_abstract_assign_stmt * assign_for(vhdl_decl::assign_type_t atype, vhdl_var_ref *lhs, vhdl_expr *rhs) { switch (atype) { case vhdl_decl::ASSIGN_BLOCK: case vhdl_decl::ASSIGN_CONST: return new vhdl_assign_stmt(lhs, rhs); case vhdl_decl::ASSIGN_NONBLOCK: return new vhdl_nbassign_stmt(lhs, rhs); } assert(false); return NULL; } /* * Check that this assignment type is valid within the context of `proc'. * For example, a <= assignment is not valid within a function. */ bool check_valid_assignment(vhdl_decl::assign_type_t atype, vhdl_procedural *proc, ivl_statement_t stmt) { if (atype == vhdl_decl::ASSIGN_NONBLOCK && !proc->get_scope()->allow_signal_assignment()) { error("Unable to translate assignment at %s:%d\n" " Translating this would require generating a non-blocking (<=)\n" " assignment in a VHDL context where this is disallowed (e.g.\n" " a function).", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); return false; } else return true; } // Generate a "wait for 0 ns" statement to emulate the behaviour of // Verilog blocking assignment using VHDL signals. This is only generated // if we read from the target of a blocking assignment in the same // process (i.e. it is only generated when required, not for every // blocking assignment). An example: // // begin // x = 5; // if (x == 2) // y = 7; // end // // Becomes: // // x <= 5; // wait for 0 ns; -- Required to implement assignment semantics // if x = 2 then // y <= 7; -- No need for wait here, not read // end if; // static void emit_wait_for_0(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, vhdl_expr *expr) { vhdl_var_set_t read; expr->find_vars(read); bool need_wait_for_0 = false; for (vhdl_var_set_t::const_iterator it = read.begin(); it != read.end(); ++it) { if (proc->is_blocking_target(*it)) need_wait_for_0 = true; } stmt_container::stmt_list_t &stmts = container->get_stmts(); bool last_was_wait = !stmts.empty() && dynamic_cast(stmts.back()); if (need_wait_for_0 && !last_was_wait) { debug_msg("Generated wait-for-0 for %s:%d", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vhdl_seq_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_FOR0); ostringstream ss; ss << "Read target of blocking assignment (" << ivl_stmt_file(stmt) << ":" << ivl_stmt_lineno(stmt) << ")"; wait->set_comment(ss.str()); container->add_stmt(wait); proc->added_wait_stmt(); } } // Generate an assignment of type T for the Verilog statement stmt. // If a statement was generated then `assign_type' will contain the // type of assignment that was generated; this should be initialised // to some sensible default. void make_assignment(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool emul_blocking, vhdl_decl::assign_type_t& assign_type) { list lvals; if (!assignment_lvals(stmt, proc, lvals)) return; vhdl_expr *rhs, *rhs2 = NULL; ivl_expr_t rval = ivl_stmt_rval(stmt); if (ivl_expr_type(rval) == IVL_EX_TERNARY) { rhs = translate_expr(ivl_expr_oper2(rval)); rhs2 = translate_expr(ivl_expr_oper3(rval)); if (rhs2 == NULL) return; } else rhs = translate_expr(rval); if (rhs == NULL) return; emit_wait_for_0(proc, container, stmt, rhs); if (rhs2) emit_wait_for_0(proc, container, stmt, rhs2); if (lvals.size() == 1) { vhdl_var_ref *lhs = lvals.front(); rhs = rhs->cast(lhs->get_type()); ivl_expr_t i_delay; vhdl_expr *after = NULL; if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) { after = translate_time_expr(i_delay); if (after == NULL) return; emit_wait_for_0(proc, container, stmt, after); } // Find the declaration of the LHS so we know what type // of assignment statement to generate (is it a signal, // a variable, etc?) vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name()); assign_type = decl->assignment_type(); if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) proc->add_blocking_target(lhs); // A small optimisation is to expand ternary RHSs into an // if statement (eliminates a function call and produces // more idiomatic code) if (ivl_expr_type(rval) == IVL_EX_TERNARY) { rhs2 = rhs2->cast(lhs->get_type()); vhdl_var_ref *lhs2 = make_assign_lhs(ivl_stmt_lval(stmt, 0), proc->get_scope()); vhdl_expr *test = translate_expr(ivl_expr_oper1(rval)); if (NULL == test) return; emit_wait_for_0(proc, container, stmt, test); if (!check_valid_assignment(decl->assignment_type(), proc, stmt)) return; vhdl_if_stmt *vhdif = new vhdl_if_stmt(test); // True part { vhdl_abstract_assign_stmt *a = assign_for(decl->assignment_type(), lhs, rhs); if (after) a->set_after(after); vhdif->get_then_container()->add_stmt(a); } // False part { vhdl_abstract_assign_stmt *a = assign_for(decl->assignment_type(), lhs2, rhs2); if (after) a->set_after(translate_time_expr(i_delay)); vhdif->get_else_container()->add_stmt(a); } container->add_stmt(vhdif); return; } // Where possible, move constant assignments into the // declaration as initialisers. This optimisation is only // performed on assignments of constant values to prevent // ordering problems. // This also has another application: If this is an `initial' // process and we haven't yet generated a `wait' statement then // moving the assignment to the initialization preserves the // expected Verilog behaviour: VHDL does not distinguish // `initial' and `always' processes so an `always' process might // be activated before an `initial' process at time 0. The // `always' process may then use the uninitialised signal value. // The second test ensures that we only try to initialise // internal signals not ports ivl_lval_t lval = ivl_stmt_lval(stmt, 0); if (proc->get_scope()->initializing() && ivl_signal_port(ivl_lval_sig(lval)) == IVL_SIP_NONE && !decl->has_initial() && rhs->constant() && decl->get_type()->get_name() != VHDL_TYPE_ARRAY) { // If this assignment is not in the top-level container // it will not be made on all paths through the code // This precludes any future extraction of an initialiser if (container != proc->get_container()) decl->set_initial(NULL); // Default initial value else { decl->set_initial(rhs); proc->get_scope()->hoisted_initialiser(true); delete lhs; return; } } if (!check_valid_assignment(decl->assignment_type(), proc, stmt)) return; vhdl_abstract_assign_stmt *a = assign_for(decl->assignment_type(), lhs, rhs); container->add_stmt(a); a->set_after(after); } else { // Multiple lvals are implemented by first assigning the complete // RHS to a temporary, and then assigning each lval in turn as // bit-selects of the temporary static int tmp_count = 0; ostringstream ss; ss << "Verilog_Assign_Tmp_" << tmp_count++; vhdl_decl* tmp_decl = new vhdl_var_decl(ss.str(), rhs->get_type()); proc->get_scope()->add_decl(tmp_decl); container->add_stmt(new vhdl_assign_stmt(tmp_decl->make_ref(), rhs)); list::iterator it; int width_so_far = 0; for (it = lvals.begin(); it != lvals.end(); ++it) { vhdl_var_ref *tmp_rhs = tmp_decl->make_ref(); int lval_width = (*it)->get_type()->get_width(); vhdl_expr *slice_base = new vhdl_const_int(width_so_far); tmp_rhs->set_slice(slice_base, lval_width - 1); ivl_expr_t i_delay; vhdl_expr *after = NULL; if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) { after = translate_time_expr(i_delay); if (after == NULL) return; emit_wait_for_0(proc, container, stmt, after); } // Find the declaration of the LHS so we know what type // of assignment statement to generate (is it a signal, // a variable, etc?) vhdl_decl *decl = proc->get_scope()->get_decl((*it)->get_name()); assign_type = decl->assignment_type(); if (!check_valid_assignment(decl->assignment_type(), proc, stmt)) return; vhdl_abstract_assign_stmt *a = assign_for(decl->assignment_type(), *it, tmp_rhs); if (after) a->set_after(after); container->add_stmt(a); width_so_far += lval_width; if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) proc->add_blocking_target(*it); } } } /* * A non-blocking assignment inside a process. The semantics for * this are essentially the same as VHDL's non-blocking signal * assignment. */ static int draw_nbassign(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { assert(proc->get_scope()->allow_signal_assignment()); vhdl_decl::assign_type_t ignored; make_assignment(proc, container, stmt, false, ignored); return 0; } static int draw_assign(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { vhdl_decl::assign_type_t assign_type = vhdl_decl::ASSIGN_NONBLOCK; bool emulate_blocking = proc->get_scope()->allow_signal_assignment(); make_assignment(proc, container, stmt, emulate_blocking, assign_type); return 0; } /* * Delay statements are equivalent to the `wait for' form of the * VHDL wait statement. */ static int draw_delay(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { // This currently ignores the time units and precision // of the enclosing scope // A neat way to do this would be to make these values // constants in the scope (type is Time), and have the // VHDL wait statement compute the value from that. // The other solution is to add them as parameters to // the vhdl_process class vhdl_expr *time; if (ivl_statement_type(stmt) == IVL_ST_DELAY) { uint64_t value = ivl_stmt_delay_val(stmt); time = scale_time(get_active_entity(), value); } else { time = translate_time_expr(ivl_stmt_delay_expr(stmt)); if (NULL == time) return 1; } ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt); vhdl_wait_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_FOR, time); // Remember that we needed a wait statement so if this is // a process it cannot have a sensitivity list proc->added_wait_stmt(); container->add_stmt(wait); // Expand the sub-statement as well // Often this would result in a useless `null' statement which // is caught here instead if (ivl_statement_type(sub_stmt) != IVL_ST_NOOP) draw_stmt(proc, container, sub_stmt); // Any further assignments occur after simulation time 0 // so they cannot be used to initialise signal declarations // (if this scope is an initial process) proc->get_scope()->set_initializing(false); return 0; } /* * Build a set of all the nexuses referenced by signals in `expr'. */ static void get_nexuses_from_expr(ivl_expr_t expr, set &out) { switch (ivl_expr_type(expr)) { case IVL_EX_SIGNAL: out.insert(ivl_signal_nex(ivl_expr_signal(expr), 0)); break; case IVL_EX_TERNARY: get_nexuses_from_expr(ivl_expr_oper3(expr), out); case IVL_EX_BINARY: get_nexuses_from_expr(ivl_expr_oper2(expr), out); case IVL_EX_UNARY: get_nexuses_from_expr(ivl_expr_oper1(expr), out); break; default: break; } } /* * Attempt to identify common forms of wait statements and produce * more idiomatic VHDL than would be produced by the generic * draw_wait function. The main application of this is a input to * synthesis tools that don't synthesise the full VHDL language. * If none of these patterns are matched, the function returns false * and the default draw_wait is used. * * Current patterns: * always @(posedge A or posedge B) * if (A) * ... * else * ... * * This is assumed to be the template for a FF with asynchronous * reset. A is assumed to be the reset as it is dominant. This will * produce the following VHDL: * * process (A, B) is * begin * if A = '1' then * ... * else if rising_edge(B) then * ... * end if; * end process; */ static bool draw_synthesisable_wait(vhdl_process *proc, stmt_container *container, ivl_statement_t stmt) { // At the moment this only detects FFs with an asynchronous reset // All other code will fall back on the default draw_wait // Store a set of the edge triggered signals // The second item is true if this is positive-edge set edge_triggered; const int nevents = ivl_stmt_nevent(stmt); for (int i = 0; i < nevents; i++) { ivl_event_t event = ivl_stmt_events(stmt, i); if (ivl_event_nany(event) > 0) return false; int npos = ivl_event_npos(event); for (int j = 0; j < npos; j++) edge_triggered.insert(ivl_event_pos(event, j)); int nneg = ivl_event_nneg(event); for (int j = 0; j < nneg; j++) edge_triggered.insert(ivl_event_neg(event, j)); } // If we're edge-sensitive to less than two signals this doesn't // match the expected template, so use the default draw_wait if (edge_triggered.size() < 2) return false; // Now check to see if the immediately embedded statement is an `if' ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt); if (ivl_statement_type(sub_stmt) != IVL_ST_CONDIT) return false; // The if should have two branches: one is the reset branch and // one is the clocked branch if (ivl_stmt_cond_false(sub_stmt) == NULL) return false; // Check the first branch of the if statement // If it matches exactly one of the edge-triggered signals then assume // this is the (dominant) reset branch set test_nexuses; get_nexuses_from_expr(ivl_stmt_cond_expr(sub_stmt), test_nexuses); // If the test is not a simple function of one variable then this // template will not work if (test_nexuses.size() != 1) return false; // Now subtracting this set from the set of edge triggered events // should leave just one nexus, which is hopefully the clock. // If not, then we fall back on the default draw_wait set clock_net; set_difference(edge_triggered.begin(), edge_triggered.end(), test_nexuses.begin(), test_nexuses.end(), inserter(clock_net, clock_net.begin())); if (clock_net.size() != 1) return false; // Build a VHDL `if' statement to model this vhdl_expr *reset_test = translate_expr(ivl_stmt_cond_expr(sub_stmt)); vhdl_if_stmt *body = new vhdl_if_stmt(reset_test); // Draw the reset branch draw_stmt(proc, body->get_then_container(), ivl_stmt_cond_true(sub_stmt)); // Build a test for the clock event vhdl_fcall *edge = NULL; ivl_nexus_t the_clock_net = *clock_net.begin(); for (int i = 0; i < nevents; i++) { ivl_event_t event = ivl_stmt_events(stmt, i); const unsigned npos = ivl_event_npos(event); for (unsigned j = 0; j < npos; j++) { if (ivl_event_pos(event, j) == the_clock_net) edge = new vhdl_fcall("rising_edge", vhdl_type::boolean()); } const unsigned nneg = ivl_event_nneg(event); for (unsigned j = 0; j < nneg; j++) if (ivl_event_neg(event, j) == the_clock_net) edge = new vhdl_fcall("falling_edge", vhdl_type::boolean()); } assert(edge); edge->add_expr(nexus_to_var_ref(proc->get_scope(), *clock_net.begin())); // Draw the clocked branch // For an asynchronous reset we just want this around the else branch, stmt_container *else_container = body->add_elsif(edge); draw_stmt(proc, else_container, ivl_stmt_cond_false(sub_stmt)); if (proc->contains_wait_stmt()) { // Expanding the body produced a `wait' statement which can't // be included in a sensitised process so undo all this work // and fall back on the default draw_wait delete body; return false; } else container->add_stmt(body); // Add all the edge triggered signals to the sensitivity list for (set::const_iterator it = edge_triggered.begin(); it != edge_triggered.end(); ++it) { // Get the signal that represents this nexus in this scope vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), *it); proc->add_sensitivity(ref->get_name()); // Don't need the reference any more delete ref; } // Don't bother with the default draw_wait return true; } /* * A wait statement waits for a level change on a @(..) list of * signals. The logic here might seem a little bit convoluted, * it attempts to always produce something that will simulate * correctly, and tries to produce something that will also * synthesise correctly (although not at the expense of simulation * accuracy). * * The difficulty stems from VHDL's restriction that a process with * a sensitivity list may not contain any `wait' statements: we need * to generate these to accurately model some Verilog statements. * * The steps followed are: * 1) Determine whether this is the top-level statement in the process * 2) If this is top-level, call draw_synthesisable_wait to see if the * process and wait statement match any templates for which we know * how to produce good, idiomatic synthesisable VHDL (e.g. FF with * async reset) * 3) Determine whether the process is combinatorial (purely level * sensitive), or sequential (edge sensitive) * 4) Draw all of the statements in the body * 5) One of the following will be true: * A) The process is combinatorial, top-level, and there are * no `wait' statements in the body: add all the level-sensitive * signals to the VHDL sensitivity list * B) The process is combinatorial, and there *are* `wait' * statements in the body or it is not top-level: generate * a VHDL `wait-on' statement at the end of the body containing * the level-sensitive signals * C) The process is sequential, top-level, and there are * no `wait' statements in the body: build an `if' statement * with the edge-detecting expression and wrap the process * in it. * D) The process is sequential, there *are* `wait' statements * in the body, or it is not top-level: generate a VHDL * `wait-until' with the edge-detecting expression and add * it before the body of the wait event. */ static int draw_wait(vhdl_procedural *_proc, stmt_container *container, ivl_statement_t stmt) { // Wait statements only occur in processes vhdl_process *proc = dynamic_cast(_proc); assert(proc); // Catch not process // If this container is the top-level statement (i.e. it is the // first thing inside a process) then we can extract these // events out into the sensitivity list as long as we haven't // promoted any preceding assignments to initialisers bool is_top_level = container == proc->get_container() && container->empty() && !proc->get_scope()->hoisted_initialiser(); // See if this can be implemented in a more idiomatic way before we // fall back on the generic translation if (is_top_level && draw_synthesisable_wait(proc, container, stmt)) return 0; int nevents = ivl_stmt_nevent(stmt); bool combinatorial = true; // True if no negedge/posedge events for (int i = 0; i < nevents; i++) { ivl_event_t event = ivl_stmt_events(stmt, i); if (ivl_event_npos(event) > 0 || ivl_event_nneg(event) > 0) combinatorial = false; } if (combinatorial) { // If the process has no wait statement in its body then // add all the events to the sensitivity list, otherwise // build a wait-on statement at the end of the process draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true); vhdl_wait_stmt *wait = NULL; if (proc->contains_wait_stmt() || !is_top_level) wait = new vhdl_wait_stmt(VHDL_WAIT_ON); for (int i = 0; i < nevents; i++) { ivl_event_t event = ivl_stmt_events(stmt, i); int nany = ivl_event_nany(event); for (int j = 0; j < nany; j++) { ivl_nexus_t nexus = ivl_event_any(event, j); vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus); if (wait) wait->add_sensitivity(ref->get_name()); else proc->add_sensitivity(ref->get_name()); delete ref; } } if (wait) container->add_stmt(wait); } else { // Build a test expression to represent the edge event // If this process contains no `wait' statements and this // is the top-level container, then we // wrap it in an `if' statement with this test and add the // edge triggered signals to the sensitivity, otherwise // build a `wait until' statement at the top of the process vhdl_binop_expr *test = new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean()); stmt_container tmp_container; draw_stmt(proc, &tmp_container, ivl_stmt_sub_stmt(stmt), true); for (int i = 0; i < nevents; i++) { ivl_event_t event = ivl_stmt_events(stmt, i); int nany = ivl_event_nany(event); for (int j = 0; j < nany; j++) { ivl_nexus_t nexus = ivl_event_any(event, j); vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus); ref->set_name(ref->get_name() + "'Event"); test->add_expr(ref); if (!proc->contains_wait_stmt() && is_top_level) proc->add_sensitivity(ref->get_name()); } int nneg = ivl_event_nneg(event); for (int j = 0; j < nneg; j++) { ivl_nexus_t nexus = ivl_event_neg(event, j); vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus); vhdl_fcall *detect = new vhdl_fcall("falling_edge", vhdl_type::boolean()); detect->add_expr(ref); test->add_expr(detect); if (!proc->contains_wait_stmt() && is_top_level) proc->add_sensitivity(ref->get_name()); } int npos = ivl_event_npos(event); for (int j = 0; j < npos; j++) { ivl_nexus_t nexus = ivl_event_pos(event, j); vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus); vhdl_fcall *detect = new vhdl_fcall("rising_edge", vhdl_type::boolean()); detect->add_expr(ref); test->add_expr(detect); if (!proc->contains_wait_stmt() && is_top_level) proc->add_sensitivity(ref->get_name()); } } if (proc->contains_wait_stmt() || !is_top_level) { container->add_stmt(new vhdl_wait_stmt(VHDL_WAIT_UNTIL, test)); container->move_stmts_from(&tmp_container); } else { // Wrap the whole process body in an `if' statement to detect // the edge event vhdl_if_stmt *edge_detect = new vhdl_if_stmt(test); // Move all the statements from the process body into the `if' // statement edge_detect->get_then_container()->move_stmts_from(&tmp_container); container->add_stmt(edge_detect); } } return 0; } static int draw_if(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt)); if (NULL == test) return 1; emit_wait_for_0(proc, container, stmt, test); vhdl_if_stmt *vhdif = new vhdl_if_stmt(test); container->add_stmt(vhdif); ivl_statement_t cond_true_stmt = ivl_stmt_cond_true(stmt); if (cond_true_stmt) draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last); ivl_statement_t cond_false_stmt = ivl_stmt_cond_false(stmt); if (cond_false_stmt) draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last); return 0; } static vhdl_var_ref *draw_case_test(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt)); if (NULL == test) return NULL; // VHDL case expressions are required to be quite simple: variable // references or slices. So we may need to create a temporary // variable to hold the result of the expression evaluation if (typeid(*test) != typeid(vhdl_var_ref)) { const char *tmp_name = "Verilog_Case_Ex"; vhdl_type *test_type = new vhdl_type(*test->get_type()); if (!proc->get_scope()->have_declared(tmp_name)) { proc->get_scope()->add_decl (new vhdl_var_decl(tmp_name, new vhdl_type(*test_type))); } vhdl_var_ref *tmp_ref = new vhdl_var_ref(tmp_name, NULL); container->add_stmt(new vhdl_assign_stmt(tmp_ref, test)); return new vhdl_var_ref(tmp_name, test_type); } else return dynamic_cast(test); } static int draw_case(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { vhdl_var_ref *test = draw_case_test(proc, container, stmt); if (NULL == test) return 1; vhdl_case_stmt *vhdlcase = new vhdl_case_stmt(test); container->add_stmt(vhdlcase); // VHDL is more strict than Verilog about covering every // possible case. So make sure we add an 'others' branch // if there isn't a default one. bool have_others = false; int nbranches = ivl_stmt_case_count(stmt); for (int i = 0; i < nbranches; i++) { vhdl_expr *when; ivl_expr_t net = ivl_stmt_case_expr(stmt, i); if (net) { when = translate_expr(net)->cast(test->get_type()); if (NULL == when) return 1; } else { when = new vhdl_var_ref("others", NULL); have_others = true; } vhdl_case_branch *branch = new vhdl_case_branch(when); vhdlcase->add_branch(branch); ivl_statement_t stmt_i = ivl_stmt_case_stmt(stmt, i); draw_stmt(proc, branch->get_container(), stmt_i, is_last); } if (!have_others) { vhdl_case_branch *others = new vhdl_case_branch(new vhdl_var_ref("others", NULL)); others->get_container()->add_stmt(new vhdl_null_stmt()); vhdlcase->add_branch(others); } return 0; } /* * Check to see if the given number (expression) can be represented * accurately in a long value. */ static bool number_is_long(ivl_expr_t expr) { ivl_expr_type_t type = ivl_expr_type(expr); assert(type == IVL_EX_NUMBER || type == IVL_EX_ULONG); // Make sure the ULONG can be represented correctly in a long. if (type == IVL_EX_ULONG) { unsigned long val = ivl_expr_uvalue(expr); if (val > static_cast(numeric_limits::max())) { return false; } return true; } // Check to see if the number actually fits in a long. unsigned nbits = ivl_expr_width(expr); if (nbits >= 8*sizeof(long)) { const char*bits = ivl_expr_bits(expr); char pad_bit = bits[nbits-1]; for (unsigned idx = 8*sizeof(long); idx < nbits; idx++) { if (bits[idx] != pad_bit) return false; } } return true; } /* * Return the given number (expression) as a signed long value. * * Make sure to call number_is_long() first to verify that the number * can be represented accurately in a long value. */ static long get_number_as_long(ivl_expr_t expr) { long imm = 0; switch (ivl_expr_type(expr)) { case IVL_EX_ULONG: imm = ivl_expr_uvalue(expr); break; case IVL_EX_NUMBER: { const char*bits = ivl_expr_bits(expr); unsigned nbits = ivl_expr_width(expr); if (nbits > 8*sizeof(long)) nbits = 8*sizeof(long); for (unsigned idx = 0; idx < nbits; idx++) { switch (bits[idx]) { case '0': break; case '1': imm |= 1L << idx; break; default: assert(0); } if (ivl_expr_signed(expr) && bits[nbits-1] == '1' && nbits < 8*sizeof(long)) imm |= -1L << nbits; } break; } default: assert(0); } return imm; } /* * Build a check against a constant 'x'. This is for an out of range * or undefined select. */ static void check_against_x(vhdl_binop_expr *all, vhdl_var_ref *test, ivl_expr_t expr, unsigned width, unsigned base, bool is_casez) { if (is_casez) { // For a casez we need to check against 'x'. for (unsigned i = 0; i < ivl_expr_width(expr); i++) { vhdl_binop_expr *sub_expr = new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean()); vhdl_type *type; vhdl_var_ref *ref; // Check if the test bit is 'z'. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); vhdl_binop_expr *cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('z')); sub_expr->add_expr(cmp); // Compare the test bit against a constant 'x'. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('x')); sub_expr->add_expr(cmp); all->add_expr(sub_expr); } } else { // For a casex 'x' is a don't care, so just put 'true'. all->add_expr(new vhdl_const_bool(true)); } } /* * Build the test signal to constant bits check. */ static void process_number(vhdl_binop_expr *all, vhdl_var_ref *test, ivl_expr_t expr, unsigned width, unsigned base, bool is_casez) { const char *bits = ivl_expr_bits(expr); bool just_dont_care = true; for (unsigned i = 0; i < ivl_expr_width(expr); i++) { switch (bits[i]) { case 'x': if (is_casez) break; case '?': case 'z': continue; // Ignore these. } vhdl_binop_expr *sub_expr = new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean()); vhdl_type *type; vhdl_var_ref *ref; // Check if the test bit is 'z'. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); vhdl_binop_expr *cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('z')); sub_expr->add_expr(cmp); // If this is a casex statement check if the test bit is 'x'. if (!is_casez) { type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('x')); sub_expr->add_expr(cmp); } // Compare the bit against the constant value. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit(bits[i])); sub_expr->add_expr(cmp); all->add_expr(sub_expr); just_dont_care = false; } // If there are no bits comparisons then just put a True if (just_dont_care) { all->add_expr(new vhdl_const_bool(true)); } } /* * Build the test signal to label signal check. */ static bool process_signal(vhdl_binop_expr *all, vhdl_var_ref *test, ivl_expr_t expr, unsigned width, unsigned base, bool is_casez, unsigned swid, long sbase) { // If the word or dimensions are not zero then we have an array. if (ivl_expr_oper1(expr) != 0 || ivl_signal_dimensions(ivl_expr_signal(expr)) != 0) { error("Sorry, array selects are not currently allowed in this " "context."); return true; } unsigned ewid = ivl_expr_width(expr); if (sizeof(unsigned) >= sizeof(long)) { // Since we will be casting i (constrained by swid) to a long make sure // it will fit into a long. This is actually off by one, but this is the // best we can do since on 32 bit machines an unsigned and long are the // same size. assert(swid <= static_cast(numeric_limits::max())); // We are also going to cast ewid to long so check it as well. assert(ewid <= static_cast(numeric_limits::max())); } for (unsigned i = 0; i < swid; i++) { // Generate a comparison for this bit position vhdl_binop_expr *cmp; vhdl_type *type; vhdl_var_ref *ref; // Check if this is an out of bounds access. If this is a casez // then check against a constant 'x' for the out of bound bits // otherwise skip the check (casex). if (static_cast(i) + sbase >= static_cast(ewid) || static_cast(i) + sbase < 0) { if (is_casez) { // Get the current test bit. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); // Compare the bit against 'x'. cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('x')); all->add_expr(cmp); continue; } else { // The compiler replaces a completely out of range select // with a constant so we know there will be at least one // valid bit here. We don't need a just_dont_care test. continue; } } vhdl_binop_expr *sub_expr = new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean()); vhdl_var_ref *bit; // Get the current expression bit. // Why can we reuse the expression bit, but not the condition bit? type = vhdl_type::nunsigned(ivl_expr_width(expr)); bit = new vhdl_var_ref(ivl_expr_name(expr), type); bit->set_slice(new vhdl_const_int(i+sbase)); // Check if the expression bit is 'z'. cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(bit); cmp->add_expr(new vhdl_const_bit('z')); sub_expr->add_expr(cmp); // If this is a casex statement check if the expression bit is 'x'. if (!is_casez) { cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(bit); cmp->add_expr(new vhdl_const_bit('x')); sub_expr->add_expr(cmp); } // Check if the test bit is 'z'. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('z')); sub_expr->add_expr(cmp); // If this is a casex statement check if the test bit is 'x'. if (!is_casez) { type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(new vhdl_const_bit('x')); sub_expr->add_expr(cmp); } // Next check if the test and expression bits are equal. type = vhdl_type::nunsigned(width); ref = new vhdl_var_ref(test->get_name().c_str(), type); ref->set_slice(new vhdl_const_int(i+base)); cmp = new vhdl_binop_expr(VHDL_BINOP_EQ, vhdl_type::boolean()); cmp->add_expr(ref); cmp->add_expr(bit); sub_expr->add_expr(cmp); all->add_expr(sub_expr); } return false; } /* * These are the constructs that we allow in a casex/z label * expression. Returns true on failure. */ static bool process_expr_bits(vhdl_binop_expr *all, vhdl_var_ref *test, ivl_expr_t expr, unsigned width, unsigned base, bool is_casez) { assert(ivl_expr_width(expr)+base <= width); switch (ivl_expr_type(expr)) { case IVL_EX_CONCAT: // Loop repeat number of times processing each sub element. for (unsigned repeat = 0; repeat < ivl_expr_repeat(expr); repeat++) { unsigned nparms = ivl_expr_parms(expr) - 1; for (unsigned parm = 0; parm <= nparms; parm++) { ivl_expr_t pexpr = ivl_expr_parm(expr, nparms-parm); if (process_expr_bits(all, test, pexpr, width, base, is_casez)) return true; base += ivl_expr_width(pexpr); } } break; case IVL_EX_NUMBER: process_number(all, test, expr, width, base, is_casez); break; case IVL_EX_SIGNAL: if (process_signal(all, test, expr, width, base, is_casez, ivl_expr_width(expr), 0)) return true; break; case IVL_EX_SELECT: { ivl_expr_t bexpr = ivl_expr_oper2(expr); if (ivl_expr_type(bexpr) != IVL_EX_NUMBER && ivl_expr_type(bexpr) != IVL_EX_ULONG) { error("Sorry, only constant bit/part selects are currently allowed " "in this context."); return true; } // If the number is out of bounds or an 'x' then check against 'x'. if (!number_is_long(bexpr)) { check_against_x(all, test, expr, width, base, is_casez); } else if (process_signal(all, test, ivl_expr_oper1(expr), width, base, is_casez, ivl_expr_width(expr), get_number_as_long(bexpr))) return true; break; } default: error("Sorry, expression type %d is not currently supported.", ivl_expr_type(expr)); return true; break; } return false; } /* * A casex/z statement cannot be directly translated to a VHDL case * statement as VHDL does not treat the don't-care bit as special. * The solution here is to generate an if statement from the casex/z * which compares only the non-don't-care bit positions. */ int draw_casezx(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { vhdl_var_ref *test = draw_case_test(proc, container, stmt); if (NULL == test) return 1; vhdl_if_stmt *result = NULL; int nbranches = ivl_stmt_case_count(stmt); bool is_casez = ivl_statement_type(stmt) == IVL_ST_CASEZ; for (int i = 0; i < nbranches; i++) { stmt_container *where = NULL; ivl_expr_t net = ivl_stmt_case_expr(stmt, i); if (net) { vhdl_binop_expr *all = new vhdl_binop_expr(VHDL_BINOP_AND, vhdl_type::boolean()); // The net must be something we can generate a comparison for. if (process_expr_bits(all, test, net, ivl_expr_width(net), 0, is_casez)) { error("%s:%d: Sorry, only case%s statements with simple " "expression labels can be translated to VHDL", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt), (is_casez ? "z" : "x")); delete all; return 1; } if (result) where = result->add_elsif(all); else { result = new vhdl_if_stmt(all); where = result->get_then_container(); } } else { // This the default case and therefore the `else' branch assert(result); where = result->get_else_container(); } // `where' now points to a branch of an if statement which // corresponds to this casex/z branch assert(where); draw_stmt(proc, where, ivl_stmt_case_stmt(stmt, i), is_last); } // Add a comment to say that this corresponds to a casex/z statement // as this may not be obvious ostringstream ss; ss << "Generated from case" << (is_casez ? 'z' : 'x') << " statement at " << ivl_stmt_file(stmt) << ":" << ivl_stmt_lineno(stmt); result->set_comment(ss.str()); container->add_stmt(result); // We don't actually use the generated `test' expression delete test; return 0; } int draw_while(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { // Generate the body inside a temporary container before // generating the test // The reason for this is that some of the signals in the // test might be renamed while expanding the body (e.g. if // we need to generate an assignment to a constant signal) stmt_container tmp_container; int rc = draw_stmt(proc, &tmp_container, ivl_stmt_sub_stmt(stmt)); if (rc != 0) return 1; vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt)); if (NULL == test) return 1; // The test must be a Boolean (and std_logic and (un)signed types // must be explicitly cast unlike in Verilog) vhdl_type boolean(VHDL_TYPE_BOOLEAN); test = test->cast(&boolean); emit_wait_for_0(proc, container, stmt, test); vhdl_while_stmt *loop = new vhdl_while_stmt(test); draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt)); emit_wait_for_0(proc, loop->get_container(), stmt, test); container->add_stmt(loop); return 0; } int draw_forever(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { vhdl_loop_stmt *loop = new vhdl_loop_stmt; container->add_stmt(loop); draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt)); return 0; } int draw_repeat(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { vhdl_expr *times = translate_expr(ivl_stmt_cond_expr(stmt)); if (NULL == times) return 1; vhdl_type integer(VHDL_TYPE_INTEGER); times = times->cast(&integer); const char *it_name = "Verilog_Repeat"; vhdl_for_stmt *loop = new vhdl_for_stmt(it_name, new vhdl_const_int(1), times); container->add_stmt(loop); draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt)); return 0; } /* * Tasks are difficult to translate to VHDL since they allow things * not allowed by VHDL's corresponding procedures (e.g. updating * global variables. The solution here is to expand tasks in-line. */ int draw_utask(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { ivl_scope_t tscope = ivl_stmt_call(stmt); // TODO: adding some comments to the output would be helpful // TODO: this completely ignores parameters! draw_stmt(proc, container, ivl_scope_def(tscope), false); return 0; } /* * Generate VHDL statements for the given Verilog statement and * add them to the given VHDL process. The container is the * location to add statements: e.g. the process body, a branch * of an if statement, etc. * * The flag is_last should be set if this is the final statement * in a block or process. It avoids generating useless `wait for 0ns' * statements if the next statement would be a wait anyway. */ int draw_stmt(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { assert(stmt); switch (ivl_statement_type(stmt)) { case IVL_ST_STASK: return draw_stask(proc, container, stmt); case IVL_ST_BLOCK: return draw_block(proc, container, stmt, is_last); case IVL_ST_NOOP: return draw_noop(proc, container, stmt); case IVL_ST_ASSIGN: return draw_assign(proc, container, stmt); case IVL_ST_ASSIGN_NB: return draw_nbassign(proc, container, stmt); case IVL_ST_DELAY: case IVL_ST_DELAYX: return draw_delay(proc, container, stmt); case IVL_ST_WAIT: return draw_wait(proc, container, stmt); case IVL_ST_CONDIT: return draw_if(proc, container, stmt, is_last); case IVL_ST_CASE: return draw_case(proc, container, stmt, is_last); case IVL_ST_WHILE: return draw_while(proc, container, stmt); case IVL_ST_FOREVER: return draw_forever(proc, container, stmt); case IVL_ST_REPEAT: return draw_repeat(proc, container, stmt); case IVL_ST_UTASK: return draw_utask(proc, container, stmt); case IVL_ST_FORCE: case IVL_ST_RELEASE: error("force/release statements cannot be translated to VHDL"); return 1; case IVL_ST_DISABLE: error("disable statement cannot be translated to VHDL"); return 1; case IVL_ST_CASEX: case IVL_ST_CASEZ: return draw_casezx(proc, container, stmt, is_last); case IVL_ST_FORK: error("fork statement cannot be translated to VHDL"); return 1; case IVL_ST_CASSIGN: case IVL_ST_DEASSIGN: error("continuous procedural assignment cannot be translated to VHDL"); return 1; default: error("No VHDL translation for statement at %s:%d (type = %d)", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt), ivl_statement_type(stmt)); return 1; } } iverilog-10_1/tgt-vhdl/support.cc000066400000000000000000000137111265551621300171210ustar00rootroot00000000000000/* * Support functions for VHDL output. * * Copyright (C) 2008-2009 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_target.h" #include "support.hh" #include "state.hh" #include #include void require_support_function(support_function_t f) { vhdl_scope *scope = get_active_entity()->get_arch()->get_scope(); if (!scope->have_declared(support_function::function_name(f))) scope->add_decl(new support_function(f)); } const char *support_function::function_name(support_function_t type) { switch (type) { case SF_UNSIGNED_TO_BOOLEAN: return "Unsigned_To_Boolean"; case SF_SIGNED_TO_BOOLEAN: return "Signed_To_Boolean"; case SF_BOOLEAN_TO_LOGIC: return "Boolean_To_Logic"; case SF_REDUCE_OR: return "Reduce_OR"; case SF_REDUCE_AND: return "Reduce_AND"; case SF_REDUCE_XOR: return "Reduce_XOR"; case SF_REDUCE_XNOR: return "Reduce_XNOR"; case SF_TERNARY_LOGIC: return "Ternary_Logic"; case SF_TERNARY_UNSIGNED: return "Ternary_Unsigned"; case SF_TERNARY_SIGNED: return "Ternary_Signed"; case SF_LOGIC_TO_INTEGER: return "Logic_To_Integer"; case SF_SIGNED_TO_LOGIC: return "Signed_To_Logic"; case SF_UNSIGNED_TO_LOGIC: return "Unsigned_To_Logic"; default: assert(false); } return "Invalid"; } vhdl_type *support_function::function_type(support_function_t type) { switch (type) { case SF_UNSIGNED_TO_BOOLEAN: case SF_SIGNED_TO_BOOLEAN: return vhdl_type::boolean(); case SF_BOOLEAN_TO_LOGIC: case SF_REDUCE_OR: case SF_REDUCE_AND: case SF_REDUCE_XOR: case SF_REDUCE_XNOR: case SF_TERNARY_LOGIC: case SF_SIGNED_TO_LOGIC: case SF_UNSIGNED_TO_LOGIC: return vhdl_type::std_logic(); case SF_TERNARY_SIGNED: return new vhdl_type(VHDL_TYPE_SIGNED); case SF_TERNARY_UNSIGNED: return new vhdl_type(VHDL_TYPE_UNSIGNED); case SF_LOGIC_TO_INTEGER: return vhdl_type::integer(); } assert(false); return vhdl_type::boolean(); } void support_function::emit_ternary(std::ostream &of, int level) const { of << nl_string(level) << "begin" << nl_string(indent(level)) << "if T then return X; else return Y; end if;"; } void support_function::emit_reduction(std::ostream &of, int level, const char *op, char unit) const { // Emit a VHDL function emulating a Verilog reduction operator // Where op is the corresponding VHDL operator and unit is the // right-unit of the operator of << "(X : std_logic_vector) return std_logic is" << nl_string(indent(level)) << "variable R : std_logic := '" << unit << "';" << nl_string(level) << "begin" << nl_string(indent(level)) << "for I in X'Range loop" << nl_string(indent(indent(level))) << "R := X(I) " << op << " R;" << nl_string(indent(level)) << "end loop;" << nl_string(indent(level)) << "return R;"; } void support_function::emit(std::ostream &of, int level) const { of << nl_string(level) << "function " << function_name(type_); switch (type_) { case SF_UNSIGNED_TO_BOOLEAN: of << "(X : unsigned) return Boolean is" << nl_string(level) << "begin" << nl_string(indent(level)) << "return X /= To_Unsigned(0, X'Length);"; break; case SF_SIGNED_TO_BOOLEAN: of << "(X : signed) return Boolean is" << nl_string(level) << "begin" << nl_string(indent(level)) << "return X /= To_Signed(0, X'Length);"; break; case SF_BOOLEAN_TO_LOGIC: of << "(B : Boolean) return std_logic is" << nl_string(level) << "begin" << nl_string(indent(level)) << "if B then" << nl_string(indent(indent(level))) << "return '1';" << nl_string(indent(level)) << "else" << nl_string(indent(indent(level))) << "return '0';" << nl_string(indent(level)) << "end if;"; break; case SF_UNSIGNED_TO_LOGIC: of << "(X : unsigned) return std_logic is" << nl_string(level) << "begin" << nl_string(indent(level)) << "return X(0);"; break; case SF_SIGNED_TO_LOGIC: of << "(X : signed) return std_logic is" << nl_string(level) << "begin" << nl_string(indent(level)) << "return X(0);"; break; case SF_REDUCE_OR: emit_reduction(of, level, "or", '0'); break; case SF_REDUCE_AND: emit_reduction(of, level, "and", '1'); break; case SF_REDUCE_XOR: emit_reduction(of, level, "xor", '0'); break; case SF_REDUCE_XNOR: emit_reduction(of, level, "xnor", '0'); break; case SF_TERNARY_LOGIC: of << "(T : Boolean; X, Y : std_logic) return std_logic is"; emit_ternary(of, level); break; case SF_TERNARY_SIGNED: of << "(T : Boolean; X, Y : signed) return signed is"; emit_ternary(of, level); break; case SF_TERNARY_UNSIGNED: of << "(T : Boolean; X, Y : unsigned) return unsigned is"; emit_ternary(of, level); break; case SF_LOGIC_TO_INTEGER: of << "(X : std_logic) return integer is" << nl_string(level) << "begin" << nl_string(indent(level)) << "if X = '1' then return 1; else return 0; end if;"; break; default: assert(false); } of << nl_string(level) << "end function;"; } iverilog-10_1/tgt-vhdl/support.hh000066400000000000000000000034251265551621300171340ustar00rootroot00000000000000/* * Support functions for VHDL output. * * Copyright (C) 2008-2010 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INC_SUPPORT_HH #define INC_SUPPORT_HH #include "vhdl_syntax.hh" enum support_function_t { SF_UNSIGNED_TO_BOOLEAN = 0, SF_SIGNED_TO_BOOLEAN, SF_BOOLEAN_TO_LOGIC, SF_REDUCE_OR, SF_REDUCE_AND, SF_REDUCE_XOR, SF_REDUCE_XNOR, SF_TERNARY_LOGIC, SF_TERNARY_UNSIGNED, SF_TERNARY_SIGNED, SF_LOGIC_TO_INTEGER, SF_SIGNED_TO_LOGIC, SF_UNSIGNED_TO_LOGIC }; class support_function : public vhdl_function { public: support_function(support_function_t type) : vhdl_function(function_name(type), function_type(type)), type_(type) {} void emit(std::ostream &of, int level) const; static const char *function_name(support_function_t type); static vhdl_type *function_type(support_function_t type); private: void emit_ternary(std::ostream &of, int level) const; void emit_reduction(std::ostream &of, int level, const char *op, char unit) const; support_function_t type_; }; #endif iverilog-10_1/tgt-vhdl/vhdl-s.conf000066400000000000000000000001471265551621300171410ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle -t:dll flag:DLL=vhdl.tgt iverilog-10_1/tgt-vhdl/vhdl.cc000066400000000000000000000102051265551621300163350ustar00rootroot00000000000000/* * VHDL code generator for Icarus Verilog. * * Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "version_base.h" #include "version_tag.h" #include "vhdl_target.h" #include "state.hh" #include #include #include #include #include #include #include static const char*version_string = "Icarus Verilog VHDL Code Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; static int g_errors = 0; // Total number of errors encountered static ivl_design_t g_design; /* * Called when an unrecoverable problem is encountered. */ void error(const char *fmt, ...) { std::va_list args; va_start(args, fmt); std::printf("VHDL conversion error: "); // Source/line number? std::vprintf(fmt, args); std::putchar('\n'); va_end(args); g_errors++; } /* * Print a message only if -pdebug was specified. */ void debug_msg(const char *fmt, ...) { std::va_list args; va_start(args, fmt); if (std::strcmp(ivl_design_flag(g_design, "debug"), "")) { std::fputs("[DEBUG] ", stdout); std::vprintf(fmt, args); std::putchar('\n'); } va_end(args); } ivl_design_t get_vhdl_design() { return g_design; } extern "C" int target_design(ivl_design_t des) { ivl_scope_t *roots; unsigned int nroots; ivl_design_roots(des, &roots, &nroots); g_design = des; for (unsigned int i = 0; i < nroots; i++) draw_scope(roots[i], NULL); // Only generate processes if there were no errors generating entities // (otherwise the necessary information won't be present) if (0 == g_errors) ivl_design_process(des, draw_process, NULL); // Write the generated elements to the output file // only if there were no errors generating entities or processes if (0 == g_errors) { const char *ofname = ivl_design_flag(des, "-o"); ofstream outfile(ofname); outfile << "-- This VHDL was converted from Verilog using the" << endl << "-- Icarus Verilog VHDL Code Generator " VERSION " (" VERSION_TAG ")" << endl << endl; // If the user passed -pdepth=N then only emit entities with // depth < N // I.e. -pdepth=1 emits only the top-level entity // If max_depth is zero then all entities will be emitted // (This is handy since it means we can use atoi ;-) int max_depth = std::atoi(ivl_design_flag(des, "depth")); emit_all_entities(outfile, max_depth); } // Clean up free_all_vhdl_objects(); return g_errors; } extern "C" const char* target_query(const char*key) { if (strcmp(key, "version") == 0) return version_string; return 0; } iverilog-10_1/tgt-vhdl/vhdl.conf000066400000000000000000000000611265551621300166740ustar00rootroot00000000000000functor:cprop functor:nodangle flag:DLL=vhdl.tgt iverilog-10_1/tgt-vhdl/vhdl_config.h.in000066400000000000000000000022701265551621300201340ustar00rootroot00000000000000/* vhdl_config.h.in. Generated from configure.in by autoheader. */ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS iverilog-10_1/tgt-vhdl/vhdl_element.cc000066400000000000000000000114471265551621300200570ustar00rootroot00000000000000/* * VHDL abstract syntax elements. * * Copyright (C) 2008-2011 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_element.hh" #include #include #include #include #include #include using namespace std; static const int VHDL_INDENT = 2; // Spaces to indent int indent(int level) { return level + VHDL_INDENT; } std::string nl_string(int level) { std::ostringstream ss; newline(ss, level); return ss.str(); } /* * Emit a newline and indent to the correct level. */ void newline(std::ostream &of, int level) { of << std::endl; while (level--) of << ' '; } void blank_line(std::ostream &of, int level) { of << std::endl; newline(of, level); } // The array of all vhdl_elements allocated so we can quickly // clean them up just before the code generator exits vector vhdl_element::allocated_; // Just a counter of total bytes allocated for statistics size_t vhdl_element::total_alloc_(0); void vhdl_element::set_comment(std::string comment) { comment_ = comment; } /* * Draw the comment for any element. The comment is either on * a line before the element (end_of_line is false) or at the * end of the line containing the element (end_of_line is true). */ void vhdl_element::emit_comment(std::ostream &of, int level, bool end_of_line) const { if (! comment_.empty()) { if (end_of_line) of << " -- " << comment_; else { // Comment may contain embedded newlines of << "-- "; for (string::const_iterator it = comment_.begin(); it != comment_.end(); ++it) { if (*it == '\n') { newline(of, level); of << "-- "; } else of << *it; } newline(of, level); } } } void vhdl_element::print() const { emit(std::cout, 0); std::cout << std::endl; } // Trap allocations of vhdl_element subclasses. // This records the pointer allocated in a static field of vhdl_element // so we can delete it just before the code generator exits. void* vhdl_element::operator new(size_t size) throw (bad_alloc) { // Let the default new handle the allocation void* ptr = ::operator new(size); // Remember this element so we can delete it later vhdl_element* elem = static_cast(ptr); allocated_.push_back(elem); total_alloc_ += size; return ptr; } // Explicitly delete a vhdl_element object. // This just sets the corresponding pointer in vhdl_element::allocated_ // to NULL (since it's safe to delete a NULL pointer). void vhdl_element::operator delete(void* ptr) { // Let the default delete handle the deallocation ::operator delete(ptr); // Remember that we've already deleted this pointer so we don't // delete it again in the call to free_all_objects vector::iterator it = find(allocated_.begin(), allocated_.end(), static_cast(ptr)); if (it != allocated_.end()) { *it = NULL; // It's safe to delete a NULL pointer and much cheaper // than removing an element from the middle of a vector } else { // This shouldn't really happen but it's harmless cerr << "??? vhdl_element::operator delete called on an object not " << "allocated by vhdl_element::operator new" << endl; } } // Return the total number of bytes our custom operator new has seen. size_t vhdl_element::total_allocated() { return total_alloc_; } // Free every object derived from vhdl_element that has not yet been // explicitly deallocated. // Any pointers to vhdl_elements will be invalid after this call! // Returns the number of objects freed. int vhdl_element::free_all_objects() { for (vector::iterator it = allocated_.begin(); it != allocated_.end(); ++it) { if (*it) ::operator delete(*it); // Explicitly use the default delete } int freed = allocated_.size(); // Just in case we want to allocated any more vhdl_element objects allocated_.clear(); return freed; } iverilog-10_1/tgt-vhdl/vhdl_element.hh000066400000000000000000000050121265551621300200600ustar00rootroot00000000000000/* * VHDL abstract syntax elements. * * Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INC_VHDL_ELEMENT_HH #define INC_VHDL_ELEMENT_HH #include #include #include #include #include typedef std::list string_list_t; // Any VHDL syntax element. Each element can also contain a comment. // // Memory management is handled specially for vhdl_element subclasses: // The vast majority of vhdl_elements will be created during code generation // and persist until after they have been printed, at which point *all* // vhdl_element objects should be destroyed. To support this all allocations // of vhdl_element subclasses call a special operator new which records // the pointer allocated so we can ensure that it is disposed of when // the code generator completes -- by free_all_objects. // // The two big advantages of this are that we don't have to worry about // memory leaks of vhdl_element objects, and we can freely share pointers // between different parts of the AST. class vhdl_element { public: virtual ~vhdl_element() {} void* operator new(size_t size) throw (std::bad_alloc); void operator delete(void* ptr); virtual void emit(std::ostream &of, int level=0) const = 0; void print() const; void set_comment(std::string comment); static int free_all_objects(); static size_t total_allocated(); protected: void emit_comment(std::ostream &of, int level, bool end_of_line=false) const; private: std::string comment_; static std::vector allocated_; static size_t total_alloc_; }; typedef std::list element_list_t; int indent(int level); void newline(std::ostream &of, int level); std::string nl_string(int level); void blank_line(std::ostream &of, int level); #endif iverilog-10_1/tgt-vhdl/vhdl_helper.hh000066400000000000000000000035441265551621300177160ustar00rootroot00000000000000/* * Helper functions for VHDL syntax elements. * * Copyright (C) 2008-2012 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INC_VHDL_HELPER_HH #define INC_VHDL_HELPER_HH #include #include #include template void emit_children(std::ostream &of, const std::list &children, int level, const char *delim = "", bool trailing_newline = true) { // Don't indent if there are no children if (children.empty()) newline(of, level); else { typename std::list::const_iterator it; int sz = children.size(); for (it = children.begin(); it != children.end(); ++it) { newline(of, indent(level)); (*it)->emit(of, indent(level)); if (--sz > 0) of << delim; } if (trailing_newline) newline(of, level); } } static inline char vl_to_vhdl_bit(char bit) { switch (bit) { case '0': case 'Z': case '1': return bit; case 'z': return 'Z'; case 'x': case 'X': return 'U'; case '?': return '-'; } assert(false); return 0; } #endif iverilog-10_1/tgt-vhdl/vhdl_syntax.cc000066400000000000000000000632771265551621300177640ustar00rootroot00000000000000/* * VHDL abstract syntax elements. * * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_syntax.hh" #include "vhdl_helper.hh" #include #include #include #include #include #include using namespace std; vhdl_scope::vhdl_scope() : parent_(NULL), init_(false), sig_assign_(true), hoisted_init_(false) { } vhdl_scope::~vhdl_scope() { } void vhdl_scope::set_initializing(bool i) { init_ = i; if (parent_) parent_->set_initializing(i); } void vhdl_scope::add_decl(vhdl_decl *decl) { decls_.push_back(decl); } void vhdl_scope::add_forward_decl(vhdl_decl *decl) { decls_.push_front(decl); } vhdl_decl *vhdl_scope::get_decl(const std::string &name) const { decl_list_t::const_iterator it; for (it = decls_.begin(); it != decls_.end(); ++it) { if (strcasecmp((*it)->get_name().c_str(), name.c_str()) == 0) return *it; } return parent_ ? parent_->get_decl(name) : NULL; } bool vhdl_scope::have_declared(const std::string &name) const { return get_decl(name) != NULL; } // True if `name' differs in all but case from another declaration bool vhdl_scope::name_collides(const string& name) const { const vhdl_decl* decl = get_decl(name); if (decl) return strcasecmp(decl->get_name().c_str(), name.c_str()) == 0; else return false; } bool vhdl_scope::contained_within(const vhdl_scope *other) const { if (this == other) return true; else if (NULL == parent_) return false; else return parent_->contained_within(other); } vhdl_scope *vhdl_scope::get_parent() const { assert(parent_); return parent_; } bool vhdl_scope::hoisted_initialiser() const { return hoisted_init_; } void vhdl_scope::hoisted_initialiser(bool h) { hoisted_init_ = h; } vhdl_entity::vhdl_entity(const string& name, vhdl_arch *arch, int depth__) : depth(depth__), name_(name), arch_(arch), time_unit_(TIME_UNIT_NS) { arch->get_scope()->set_parent(&ports_); } vhdl_entity::~vhdl_entity() { } void vhdl_entity::add_port(vhdl_port_decl *decl) { ports_.add_decl(decl); } void vhdl_entity::emit(std::ostream &of, int level) const { // Pretty much every design will use std_logic so we // might as well include it by default of << "library ieee;" << std::endl; of << "use ieee.std_logic_1164.all;" << std::endl; of << "use ieee.numeric_std.all;" << std::endl; of << std::endl; emit_comment(of, level); of << "entity " << name_ << " is"; if (!ports_.empty()) { newline(of, indent(level)); of << "port ("; emit_children(of, ports_.get_decls(), indent(level), ";"); of << ");"; } newline(of, level); of << "end entity; "; blank_line(of, level); // Extra blank line after entities arch_->emit(of, level); } // Return a VHDL time constant scaled to the correct time scale // for this entity vhdl_const_time* scale_time(const vhdl_entity* ent, uint64_t t) { return new vhdl_const_time(t, ent->time_unit_); } // Work out the best VHDL units to use given the Verilog timescale void vhdl_entity::set_time_units(int units, int precision) { int vhdl_units = std::min(units, precision); if (vhdl_units >= -3) time_unit_ = TIME_UNIT_MS; else if (vhdl_units >= -6) time_unit_ = TIME_UNIT_US; else if (vhdl_units >= -9) time_unit_ = TIME_UNIT_NS; else time_unit_ = TIME_UNIT_PS; } vhdl_arch::~vhdl_arch() { } void vhdl_arch::add_stmt(vhdl_process *proc) { proc->get_scope()->set_parent(&scope_); stmts_.push_back(proc); } void vhdl_arch::add_stmt(vhdl_conc_stmt *stmt) { stmts_.push_back(stmt); } void vhdl_arch::emit(std::ostream &of, int level) const { emit_comment(of, level); of << "architecture " << name_ << " of " << entity_; of << " is"; emit_children(of, scope_.get_decls(), level); of << "begin"; emit_children(of, stmts_, level); of << "end architecture;"; blank_line(of, level); // Extra blank line after architectures; } void vhdl_procedural::add_blocking_target(vhdl_var_ref* ref) { blocking_targets_.insert(ref->get_name()); } bool vhdl_procedural::is_blocking_target(vhdl_var_ref* ref) const { return blocking_targets_.find(ref->get_name()) != blocking_targets_.end(); } void vhdl_process::add_sensitivity(const std::string &name) { sens_.push_back(name); } void vhdl_process::emit(std::ostream &of, int level) const { // If there are no statements in the body, this process // can't possibly do anything, so don't bother to emit it if (stmts_.empty()) { of << "-- Removed one empty process"; newline(of, level); return; } newline(of, level); emit_comment(of, level); if (! name_.empty()) of << name_ << ": "; of << "process "; int num_sens = sens_.size(); if (num_sens > 0) { of << "("; string_list_t::const_iterator it; for (it = sens_.begin(); it != sens_.end(); ++it) { of << *it; if (--num_sens > 0) of << ", "; } of << ") "; } of << "is"; emit_children(of, scope_.get_decls(), level); of << "begin"; stmts_.emit(of, level); of << "end process;"; } stmt_container::~stmt_container() { } void stmt_container::add_stmt(vhdl_seq_stmt *stmt) { // Add a statement at the end of the block stmts_.push_back(stmt); } /* * Move all the statements from one container into another. * This is useful, for example, if we want to wrap a container * in an `if' statement. */ void stmt_container::move_stmts_from(stmt_container *other) { copy(other->stmts_.begin(), other->stmts_.end(), back_inserter(stmts_)); other->stmts_.clear(); } void stmt_container::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { // Iterate over each sub-statement and find all its // read/written variables for (stmt_list_t::const_iterator it = stmts_.begin(); it != stmts_.end(); ++it) (*it)->find_vars(read, write); } void stmt_container::emit(std::ostream &of, int level, bool newline) const { emit_children(of, stmts_, level, "", newline); } vhdl_comp_inst::vhdl_comp_inst(const char *inst_name, const char *comp_name) : comp_name_(comp_name), inst_name_(inst_name) { } vhdl_comp_inst::~vhdl_comp_inst() { } void vhdl_comp_inst::map_port(const string& name, vhdl_expr *expr) { port_map_t pmap = { name, expr }; mapping_.push_back(pmap); } void vhdl_comp_inst::emit(std::ostream &of, int level) const { newline(of, level); emit_comment(of, level); of << inst_name_ << ": " << comp_name_; // If there are no ports or generics we don't need to mention them... if (! mapping_.empty()) { newline(of, indent(level)); of << "port map ("; int sz = mapping_.size(); port_map_list_t::const_iterator it; for (it = mapping_.begin(); it != mapping_.end(); ++it) { newline(of, indent(indent(level))); of << (*it).name << " => "; (*it).expr->emit(of, level); if (--sz > 0) of << ","; } newline(of, indent(level)); of << ")"; } of << ";"; } vhdl_component_decl::vhdl_component_decl(const char *name) : vhdl_decl(name) { } /* * Create a component declaration for the given entity. */ vhdl_component_decl *vhdl_component_decl::component_decl_for(vhdl_entity *ent) { assert(ent != NULL); vhdl_component_decl *decl = new vhdl_component_decl (ent->get_name().c_str()); decl->ports_ = ent->get_scope()->get_decls(); return decl; } void vhdl_component_decl::emit(std::ostream &of, int level) const { newline(of, level); emit_comment(of, level); of << "component " << name_ << " is"; if (! ports_.empty()) { newline(of, indent(level)); of << "port ("; emit_children(of, ports_, indent(level), ";"); of << ");"; } newline(of, level); of << "end component;"; } vhdl_wait_stmt::~vhdl_wait_stmt() { } void vhdl_wait_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t&) { if (expr_) expr_->find_vars(read); } void vhdl_wait_stmt::emit(std::ostream &of, int level) const { of << "wait"; switch (type_) { case VHDL_WAIT_INDEF: break; case VHDL_WAIT_FOR: assert(expr_); of << " for "; expr_->emit(of, level); break; case VHDL_WAIT_FOR0: of << " for 0 ns"; break; case VHDL_WAIT_UNTIL: assert(expr_); of << " until "; expr_->emit(of, level); break; case VHDL_WAIT_ON: { of << " on "; string_list_t::const_iterator it = sensitivity_.begin(); while (it != sensitivity_.end()) { of << *it; if (++it != sensitivity_.end()) of << ", "; } } break; } of << ";"; emit_comment(of, level, true); } vhdl_decl::~vhdl_decl() { } // Make a reference object to this declaration vhdl_var_ref* vhdl_decl::make_ref() const { return new vhdl_var_ref(name_, type_); } const vhdl_type *vhdl_decl::get_type() const { assert(type_); return type_; } void vhdl_decl::set_initial(vhdl_expr *initial) { if (!has_initial_) { assert(initial_ == NULL); initial_ = initial; has_initial_ = true; } } void vhdl_port_decl::emit(std::ostream &of, int level) const { of << name_ << " : "; switch (mode_) { case VHDL_PORT_IN: of << "in "; break; case VHDL_PORT_OUT: of << "out "; break; case VHDL_PORT_INOUT: of << "inout "; break; case VHDL_PORT_BUFFER: of << "buffer "; break; } type_->emit(of, level); } // If this is an `out' port make it a `buffer' so we can read from it void vhdl_port_decl::ensure_readable() { if (mode_ == VHDL_PORT_OUT) mode_ = VHDL_PORT_BUFFER; } // A port is readable if it is not `out'. // We also make `buffer' ports not readable for these purposes since // buffers cannot be directly mapped to outputs without an intermediate // signal. bool vhdl_port_decl::is_readable() const { return mode_ != VHDL_PORT_OUT && mode_ != VHDL_PORT_BUFFER; } void vhdl_var_decl::emit(std::ostream &of, int level) const { of << "variable " << name_ << " : "; type_->emit(of, level); if (initial_) { of << " := "; initial_->emit(of, level); } of << ";"; emit_comment(of, level, true); } void vhdl_signal_decl::emit(std::ostream &of, int level) const { of << "signal " << name_ << " : "; type_->emit(of, level); if (initial_) { of << " := "; initial_->emit(of, level); } of << ";"; emit_comment(of, level, true); } void vhdl_type_decl::emit(std::ostream &of, int level) const { of << "type " << name_ << " is "; of << type_->get_type_decl_string() << ";"; emit_comment(of, level, true); } vhdl_expr::~vhdl_expr() { } void vhdl_expr_list::add_expr(vhdl_expr *e) { exprs_.push_back(e); } vhdl_expr_list::~vhdl_expr_list() { } void vhdl_expr_list::find_vars(vhdl_var_set_t& read) { for (list::const_iterator it = exprs_.begin(); it != exprs_.end(); ++it) (*it)->find_vars(read); } void vhdl_expr_list::emit(std::ostream &of, int level) const { of << "("; int size = exprs_.size(); std::list::const_iterator it; for (it = exprs_.begin(); it != exprs_.end(); ++it) { (*it)->emit(of, level); if (--size > 0) of << ", "; } of << ")"; } void vhdl_pcall_stmt::emit(std::ostream &of, int level) const { of << name_; if (!exprs_.empty()) exprs_.emit(of, level); of << ";"; } void vhdl_pcall_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t&) { exprs_.find_vars(read); } vhdl_var_ref::~vhdl_var_ref() { } void vhdl_var_ref::set_slice(vhdl_expr *s, int w) { assert(type_); slice_ = s; slice_width_ = w; vhdl_type_name_t tname = type_->get_name(); if (tname == VHDL_TYPE_ARRAY) { type_ = type_->get_base(); } else { assert(tname == VHDL_TYPE_UNSIGNED || tname == VHDL_TYPE_SIGNED); if (w > 0) type_ = new vhdl_type(tname, w); else type_ = vhdl_type::std_logic(); } } void vhdl_var_ref::find_vars(vhdl_var_set_t& read) { read.insert(this); } void vhdl_var_ref::emit(std::ostream &of, int level) const { of << name_; if (slice_) { of << "("; if (slice_width_ > 0) { slice_->emit(of, level); of << " + " << slice_width_ << " downto "; } slice_->emit(of, level); of << ")"; } } void vhdl_const_string::emit(std::ostream &of, int) const { of << "\"" << value_ << "\""; } void vhdl_null_stmt::emit(std::ostream &of, int level) const { of << "null;"; emit_comment(of, level, true); } void vhdl_fcall::find_vars(vhdl_var_set_t& read) { exprs_.find_vars(read); } void vhdl_fcall::emit(std::ostream &of, int level) const { of << name_; exprs_.emit(of, level); } vhdl_abstract_assign_stmt::~vhdl_abstract_assign_stmt() { } void vhdl_abstract_assign_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { lhs_->find_vars(write); rhs_->find_vars(read); } void vhdl_nbassign_stmt::emit(std::ostream &of, int level) const { lhs_->emit(of, level); of << " <= "; rhs_->emit(of, level); if (after_) { of << " after "; after_->emit(of, level); } of << ";"; } void vhdl_assign_stmt::emit(std::ostream &of, int level) const { lhs_->emit(of, level); of << " := "; rhs_->emit(of, level); of << ";"; } vhdl_const_bits::vhdl_const_bits(const char *value, int width, bool issigned, bool qualify) : vhdl_expr(issigned ? vhdl_type::nsigned(width) : vhdl_type::nunsigned(width), true), qualified_(qualify), signed_(issigned) { // Can't rely on value being NULL-terminated while (width--) value_.push_back(*value++); } // True if char is not '1' or '0' static bool is_meta_bit(char c) { return c != '1' && c != '0'; } // True if the bit strings contains characters other than '1' and '0' bool vhdl_const_bits::has_meta_bits() const { return find_if(value_.begin(), value_.end(), is_meta_bit) != value_.end(); } void vhdl_const_bits::emit(std::ostream &of, int) const { if (qualified_) of << (signed_ ? "signed" : "unsigned") << "'("; // If it's a width we can write in hex, prefer that over binary size_t bits = value_.size(); int64_t ival = bits_to_int(); if ((!signed_ || ival >= 0) && !has_meta_bits() && bits <= 64 && bits % 4 == 0) { of << "X\"" << hex << setfill('0') << setw(bits / 4) << ival; } else { of << "\""; std::string::const_reverse_iterator it; for (it = value_.rbegin(); it != value_.rend(); ++it) of << vl_to_vhdl_bit(*it); } of << (qualified_ ? "\")" : "\""); } void vhdl_const_bit::emit(std::ostream &of, int) const { of << "'" << vl_to_vhdl_bit(bit_) << "'"; } void vhdl_const_int::emit(std::ostream &of, int) const { of << dec << value_; // We need to find a way to display a comment, since $time, etc. add one. } void vhdl_const_bool::emit(std::ostream &of, int) const { of << (value_ ? "True" : "False"); } void vhdl_const_time::emit(std::ostream &of, int) const { of << dec << value_; switch (units_) { case TIME_UNIT_PS: of << " ps"; break; case TIME_UNIT_NS: of << " ns"; break; case TIME_UNIT_US: of << " us"; break; case TIME_UNIT_MS: of << " ms"; break; } } vhdl_cassign_stmt::~vhdl_cassign_stmt() { } void vhdl_cassign_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond) { when_part_t when = { value, cond, NULL }; whens_.push_back(when); } void vhdl_cassign_stmt::emit(std::ostream &of, int level) const { lhs_->emit(of, level); of << " <= "; if (!whens_.empty()) { for (std::list::const_iterator it = whens_.begin(); it != whens_.end(); ++it) { (*it).value->emit(of, level); of << " when "; (*it).cond->emit(of, level); of << " "; } of << "else "; } rhs_->emit(of, level); if (after_) { of << " after "; after_->emit(of, level); } of << ";"; } vhdl_report_stmt::vhdl_report_stmt(vhdl_expr *text, vhdl_severity_t severity) : severity_(severity), text_(text) { } void vhdl_report_stmt::emit(ostream& of, int level) const { of << "report "; text_->emit(of, level); if (severity_ != SEVERITY_NOTE) { const char *levels[] = { "note", "warning", "error", "failure" }; of << " severity " << levels[severity_]; } of << ";"; } void vhdl_report_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t&) { text_->find_vars(read); } vhdl_assert_stmt::vhdl_assert_stmt(const char *reason) : vhdl_report_stmt(new vhdl_const_string(reason), SEVERITY_FAILURE) { } void vhdl_assert_stmt::emit(std::ostream &of, int level) const { of << "assert false "; // TODO: Allow arbitrary expression vhdl_report_stmt::emit(of, level); } vhdl_if_stmt::vhdl_if_stmt(vhdl_expr *test) { // Need to ensure that the expression is Boolean vhdl_type boolean(VHDL_TYPE_BOOLEAN); test_ = test->cast(&boolean); } vhdl_if_stmt::~vhdl_if_stmt() { } stmt_container *vhdl_if_stmt::add_elsif(vhdl_expr *test) { elsif ef = { test, new stmt_container }; elsif_parts_.push_back(ef); return ef.container; } void vhdl_if_stmt::emit(std::ostream &of, int level) const { emit_comment(of, level); of << "if "; test_->emit(of, level); of << " then"; then_part_.emit(of, level); std::list::const_iterator it; for (it = elsif_parts_.begin(); it != elsif_parts_.end(); ++it) { of << "elsif "; (*it).test->emit(of, level); of << " then"; (*it).container->emit(of, level); } if (!else_part_.empty()) { of << "else"; else_part_.emit(of, level); } of << "end if;"; } void vhdl_if_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { test_->find_vars(read); then_part_.find_vars(read, write); else_part_.find_vars(read, write); for (list::const_iterator it = elsif_parts_.begin(); it != elsif_parts_.end(); ++it) { (*it).test->find_vars(read); (*it).container->find_vars(read, write); } } int vhdl_expr::paren_levels(0); void vhdl_expr::open_parens(std::ostream& of) { if (paren_levels++ > 0) of << "("; } void vhdl_expr::close_parens(std::ostream& of) { assert(paren_levels > 0); if (--paren_levels > 0) of << ")"; } vhdl_unaryop_expr::~vhdl_unaryop_expr() { } void vhdl_unaryop_expr::find_vars(vhdl_var_set_t& read) { operand_->find_vars(read); } void vhdl_unaryop_expr::emit(std::ostream &of, int level) const { open_parens(of); switch (op_) { case VHDL_UNARYOP_NOT: of << "not "; break; case VHDL_UNARYOP_NEG: of << "-"; break; } operand_->emit(of, level); close_parens(of); } vhdl_binop_expr::vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op, vhdl_expr *right, const vhdl_type *type) : vhdl_expr(type), op_(op) { add_expr(left); add_expr(right); } vhdl_binop_expr::~vhdl_binop_expr() { } void vhdl_binop_expr::add_expr(vhdl_expr *e) { operands_.push_back(e); } void vhdl_binop_expr::add_expr_front(vhdl_expr *e) { operands_.push_front(e); } void vhdl_binop_expr::find_vars(vhdl_var_set_t& read) { for (list::const_iterator it = operands_.begin(); it != operands_.end(); ++it) (*it)->find_vars(read); } void vhdl_binop_expr::emit(std::ostream &of, int level) const { open_parens(of); assert(! operands_.empty()); std::list::const_iterator it = operands_.begin(); (*it)->emit(of, level); while (++it != operands_.end()) { const char* ops[] = { "and", "or", "=", "/=", "+", "-", "*", "<", ">", "<=", ">=", "sll", "srl", "xor", "&", "nand", "nor", "xnor", "/", "mod", "**", "sra", NULL }; of << " " << ops[op_] << " "; (*it)->emit(of, level); } close_parens(of); } vhdl_bit_spec_expr::~vhdl_bit_spec_expr() { } void vhdl_bit_spec_expr::add_bit(int bit, vhdl_expr *e) { bit_map bm = { bit, e }; bits_.push_back(bm); } void vhdl_bit_spec_expr::emit(std::ostream &of, int level) const { of << "("; std::list::const_iterator it; it = bits_.begin(); while (it != bits_.end()) { of << (*it).bit << " => "; (*it).e->emit(of, level); if (++it != bits_.end()) of << ", "; } if (others_) { of << (bits_.empty() ? "" : ", ") << "others => "; others_->emit(of, level); } of << ")"; } vhdl_case_branch::~vhdl_case_branch() { } void vhdl_case_branch::emit(std::ostream &of, int level) const { of << "when "; when_->emit(of, level); of << " =>"; stmts_.emit(of, indent(level), false); } vhdl_case_stmt::~vhdl_case_stmt() { } void vhdl_case_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { test_->find_vars(read); for (case_branch_list_t::const_iterator it = branches_.begin(); it != branches_.end(); ++it) { (*it)->when_->find_vars(read); (*it)->stmts_.find_vars(read, write); } } void vhdl_case_stmt::emit(std::ostream &of, int level) const { of << "case "; test_->emit(of, level); of << " is"; newline(of, indent(level)); case_branch_list_t::const_iterator it; int n = branches_.size(); for (it = branches_.begin(); it != branches_.end(); ++it) { (*it)->emit(of, level); if (--n > 0) newline(of, indent(level)); else newline(of, level); } of << "end case;"; } vhdl_while_stmt::~vhdl_while_stmt() { } void vhdl_while_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { test_->find_vars(read); vhdl_loop_stmt::find_vars(read, write); } void vhdl_while_stmt::emit(std::ostream &of, int level) const { of << "while "; test_->emit(of, level); of << " "; vhdl_loop_stmt::emit(of, level); } void vhdl_loop_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { stmts_.find_vars(read, write); } void vhdl_loop_stmt::emit(std::ostream &of, int level) const { of << "loop"; stmts_.emit(of, level); of << "end loop;"; } vhdl_for_stmt::~vhdl_for_stmt() { } void vhdl_for_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { from_->find_vars(read); to_->find_vars(write); vhdl_loop_stmt::find_vars(read, write); } void vhdl_for_stmt::emit(std::ostream &of, int level) const { of << "for " << lname_ << " in "; from_->emit(of, level); of << " to "; to_->emit(of, level); of << " "; vhdl_loop_stmt::emit(of, level); } vhdl_function::vhdl_function(const char *name, vhdl_type *ret_type) : vhdl_decl(name, ret_type) { // A function contains two scopes: // scope_ = The parameters // variables_ = Local variables // A call to get_scope returns variables_ whose parent is scope_ variables_.set_parent(&scope_); } void vhdl_function::emit(std::ostream &of, int level) const { newline(of, level); emit_comment(of, level); of << "function " << name_ << " ("; emit_children(of, scope_.get_decls(), level, ";"); of << ") "; newline(of, level); of << "return " << type_->get_string() << " is"; emit_children(of, variables_.get_decls(), level); of << "begin"; stmts_.emit(of, level); of << " return " << name_ << "_Result;"; newline(of, level); of << "end function;"; } void vhdl_forward_fdecl::emit(std::ostream &of, int level) const { of << "function " << f_->get_name() << " ("; emit_children(of, f_->scope_.get_decls(), level, ";"); of << ") "; newline(of, level); of << "return " << f_->type_->get_string() << ";"; newline(of, level); } void vhdl_param_decl::emit(std::ostream &of, int level) const { of << name_ << " : "; type_->emit(of, level); } vhdl_with_select_stmt::~vhdl_with_select_stmt() { } void vhdl_with_select_stmt::emit(std::ostream &of, int level) const { of << "with "; test_->emit(of, level); of << " select"; emit_comment(of, level, true); newline(of, indent(level)); out_->emit(of, level); of << " <= "; when_list_t::const_iterator it = whens_.begin(); while (it != whens_.end()) { (*it).value->emit(of, level); if ((*it).delay) { of << " after "; (*it).delay->emit(of, level); } of << " when "; (*it).cond->emit(of, level); if (++it != whens_.end() || others_ != NULL) { of << ","; newline(of, indent(level)); } else of << ";"; } if (others_) { others_->emit(of, level); of << " when others;"; } } void vhdl_with_select_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay) { when_part_t when = { value, cond, delay }; whens_.push_back(when); } void vhdl_with_select_stmt::add_default(vhdl_expr* value) { others_ = value; } iverilog-10_1/tgt-vhdl/vhdl_syntax.hh000066400000000000000000000615021265551621300177630ustar00rootroot00000000000000/* * VHDL abstract syntax elements. * * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INC_VHDL_SYNTAX_HH #define INC_VHDL_SYNTAX_HH #include #include #include #include "vhdl_element.hh" #include "vhdl_type.hh" using namespace std; class vhdl_scope; class vhdl_entity; class vhdl_arch; class vhdl_var_ref; typedef set vhdl_var_set_t; class vhdl_expr : public vhdl_element { public: vhdl_expr(const vhdl_type* type, bool isconst=false) : type_(type), isconst_(isconst) {} virtual ~vhdl_expr(); const vhdl_type *get_type() const { return type_; } bool constant() const { return isconst_; } vhdl_expr *cast(const vhdl_type *to); virtual vhdl_expr *resize(int newwidth); virtual vhdl_expr *to_boolean(); virtual vhdl_expr *to_integer(); virtual vhdl_expr *to_std_logic(); virtual vhdl_expr *to_std_ulogic(); virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w); virtual vhdl_expr *to_string(); virtual void find_vars(vhdl_var_set_t&) {} protected: static void open_parens(ostream& of); static void close_parens(ostream& of); static int paren_levels; const vhdl_type *type_; bool isconst_; }; /* * A scalar or array variable reference. */ class vhdl_var_ref : public vhdl_expr { public: vhdl_var_ref(const string& name, const vhdl_type *type, vhdl_expr *slice = NULL) : vhdl_expr(type), name_(name), slice_(slice), slice_width_(0) {} ~vhdl_var_ref(); void emit(std::ostream &of, int level) const; const std::string &get_name() const { return name_; } void set_name(const std::string &name) { name_ = name; } void set_slice(vhdl_expr *s, int w=0); void find_vars(vhdl_var_set_t& read); private: std::string name_; vhdl_expr *slice_; unsigned slice_width_; }; enum vhdl_binop_t { VHDL_BINOP_AND = 0, VHDL_BINOP_OR, VHDL_BINOP_EQ, VHDL_BINOP_NEQ, VHDL_BINOP_ADD, VHDL_BINOP_SUB, VHDL_BINOP_MULT, VHDL_BINOP_LT, VHDL_BINOP_GT, VHDL_BINOP_LEQ, VHDL_BINOP_GEQ, VHDL_BINOP_SL, VHDL_BINOP_SR, VHDL_BINOP_XOR, VHDL_BINOP_CONCAT, VHDL_BINOP_NAND, VHDL_BINOP_NOR, VHDL_BINOP_XNOR, VHDL_BINOP_DIV, VHDL_BINOP_MOD, VHDL_BINOP_POWER, VHDL_BINOP_SRA }; /* * A binary expression contains a list of operands rather * than just two: this is to model n-input gates and the * like. A second constructor is provided to handle the * common case of a true binary expression. */ class vhdl_binop_expr : public vhdl_expr { public: vhdl_binop_expr(vhdl_binop_t op, const vhdl_type *type) : vhdl_expr(type), op_(op) {} vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op, vhdl_expr *right, const vhdl_type *type); ~vhdl_binop_expr(); void add_expr(vhdl_expr *e); void add_expr_front(vhdl_expr *e); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read); private: std::list operands_; vhdl_binop_t op_; }; enum vhdl_unaryop_t { VHDL_UNARYOP_NOT, VHDL_UNARYOP_NEG }; class vhdl_unaryop_expr : public vhdl_expr { public: vhdl_unaryop_expr(vhdl_unaryop_t op, vhdl_expr *operand, vhdl_type *type) : vhdl_expr(type), op_(op), operand_(operand) {} ~vhdl_unaryop_expr(); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read); private: vhdl_unaryop_t op_; vhdl_expr *operand_; }; /* * An expression like (0 => '1', 2 => '0', others => 'Z') */ class vhdl_bit_spec_expr : public vhdl_expr { public: vhdl_bit_spec_expr(vhdl_type *type, vhdl_expr *others) : vhdl_expr(type), others_(others) {} ~vhdl_bit_spec_expr(); void add_bit(int bit, vhdl_expr *e); void emit(std::ostream &of, int level) const; private: vhdl_expr *others_; struct bit_map { int bit; vhdl_expr *e; }; std::list bits_; }; class vhdl_const_string : public vhdl_expr { public: vhdl_const_string(const string& value) : vhdl_expr(vhdl_type::string(), true), value_(value) {} void emit(std::ostream &of, int level) const; private: std::string value_; }; class vhdl_const_bits : public vhdl_expr { public: vhdl_const_bits(const char *value, int width, bool issigned, bool qualify=false); void emit(std::ostream &of, int level) const; const std::string &get_value() const { return value_; } vhdl_expr *to_integer(); vhdl_expr *to_std_logic(); vhdl_expr *to_vector(vhdl_type_name_t name, int w); vhdl_expr *resize(int w); private: int64_t bits_to_int() const; char sign_bit() const; bool has_meta_bits() const; std::string value_; bool qualified_, signed_; }; class vhdl_const_bit : public vhdl_expr { public: vhdl_const_bit(char bit) : vhdl_expr(vhdl_type::std_logic(), true), bit_(bit) {} void emit(std::ostream &of, int level) const; vhdl_expr *to_boolean(); vhdl_expr *to_integer(); vhdl_expr *to_vector(vhdl_type_name_t name, int w); vhdl_expr *to_std_ulogic(); private: char bit_; }; enum time_unit_t { TIME_UNIT_PS, TIME_UNIT_NS, TIME_UNIT_US, TIME_UNIT_MS }; class vhdl_const_time : public vhdl_expr { public: vhdl_const_time(uint64_t value, time_unit_t units = TIME_UNIT_NS) : vhdl_expr(vhdl_type::time(), true), value_(value), units_(units) {} void emit(std::ostream &of, int level) const; private: uint64_t value_; time_unit_t units_; }; class vhdl_const_int : public vhdl_expr { public: vhdl_const_int(int64_t value) : vhdl_expr(vhdl_type::integer(), true), value_(value) {} void emit(std::ostream &of, int level) const; vhdl_expr *to_vector(vhdl_type_name_t name, int w); private: int64_t value_; }; class vhdl_const_bool : public vhdl_expr { public: vhdl_const_bool(bool value) : vhdl_expr(vhdl_type::boolean(), true), value_(value) {} void emit(std::ostream &of, int level) const; private: bool value_; }; class vhdl_expr_list : public vhdl_element { public: ~vhdl_expr_list(); void emit(std::ostream &of, int level) const; bool empty() const { return exprs_.empty(); } void add_expr(vhdl_expr *e); void find_vars(vhdl_var_set_t& read); private: std::list exprs_; }; /* * A function call within an expression. */ class vhdl_fcall : public vhdl_expr { public: vhdl_fcall(const string& name, const vhdl_type *rtype) : vhdl_expr(rtype), name_(name) {}; ~vhdl_fcall() {} void add_expr(vhdl_expr *e) { exprs_.add_expr(e); } void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read); private: std::string name_; vhdl_expr_list exprs_; }; /* * A concurrent statement appears in architecture bodies/ */ class vhdl_conc_stmt : public vhdl_element { public: virtual ~vhdl_conc_stmt() {} }; typedef std::list conc_stmt_list_t; /* * A ' when ' clause that appears in several * statement types. */ struct when_part_t { vhdl_expr *value, *cond, *delay; }; typedef std::list when_list_t; /* * A concurrent signal assignment (i.e. not part of a process). * Can have any number of `when' clauses, in which case the original * rhs becomes the `else' part. */ class vhdl_cassign_stmt : public vhdl_conc_stmt { public: vhdl_cassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs) : lhs_(lhs), rhs_(rhs), after_(NULL) {} ~vhdl_cassign_stmt(); void emit(std::ostream &of, int level) const; void add_condition(vhdl_expr *value, vhdl_expr *cond); void set_after(vhdl_expr *a) { after_ = a; } private: vhdl_var_ref *lhs_; vhdl_expr *rhs_; vhdl_expr *after_; when_list_t whens_; }; class vhdl_with_select_stmt : public vhdl_conc_stmt { public: vhdl_with_select_stmt(vhdl_expr *test, vhdl_var_ref *out) : test_(test), out_(out), others_(NULL) {} ~vhdl_with_select_stmt(); void emit(std::ostream &of, int level) const; void add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay=NULL); void add_default(vhdl_expr* value); private: vhdl_expr *test_; vhdl_var_ref *out_; when_list_t whens_; vhdl_expr* others_; }; /* * Any sequential statement in a process. */ class vhdl_seq_stmt : public vhdl_element { public: virtual ~vhdl_seq_stmt() {} // Find all the variables that are read or written in the // expressions within this statement // This is used to clean up the VHDL output virtual void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) = 0; }; /* * A list of sequential statements. For example inside a * process, loop, or if statement. */ class stmt_container { public: ~stmt_container(); void add_stmt(vhdl_seq_stmt *stmt); void move_stmts_from(stmt_container *other); void emit(std::ostream &of, int level, bool newline=true) const; bool empty() const { return stmts_.empty(); } void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); typedef std::list stmt_list_t; stmt_list_t &get_stmts() { return stmts_; } private: stmt_list_t stmts_; }; /* * Shared between blocking and non-blocking assignment. */ class vhdl_abstract_assign_stmt : public vhdl_seq_stmt { public: vhdl_abstract_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs) : lhs_(lhs), rhs_(rhs), after_(NULL) {} virtual ~vhdl_abstract_assign_stmt(); void set_after(vhdl_expr *after) { after_ = after; } void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); protected: vhdl_var_ref *lhs_; vhdl_expr *rhs_, *after_; }; /* * Similar to Verilog non-blocking assignment, except the LHS * must be a signal not a variable. */ class vhdl_nbassign_stmt : public vhdl_abstract_assign_stmt { public: vhdl_nbassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs) : vhdl_abstract_assign_stmt(lhs, rhs) {} void emit(std::ostream &of, int level) const; }; class vhdl_assign_stmt : public vhdl_abstract_assign_stmt { public: vhdl_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs) : vhdl_abstract_assign_stmt(lhs, rhs) {} void emit(std::ostream &of, int level) const; }; enum vhdl_wait_type_t { VHDL_WAIT_INDEF, // Suspend indefinitely VHDL_WAIT_FOR, // Wait for a constant amount of time VHDL_WAIT_FOR0, // Special wait for zero time VHDL_WAIT_UNTIL, // Wait on an expression VHDL_WAIT_ON // Wait on a sensitivity list }; /* * Delay simulation indefinitely, until an event, or for a * specified time. */ class vhdl_wait_stmt : public vhdl_seq_stmt { public: vhdl_wait_stmt(vhdl_wait_type_t type = VHDL_WAIT_INDEF, vhdl_expr *expr = NULL) : type_(type), expr_(expr) {} ~vhdl_wait_stmt(); void emit(std::ostream &of, int level) const; void add_sensitivity(const std::string &s) { sensitivity_.push_back(s); } vhdl_wait_type_t get_type() const { return type_; } void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_wait_type_t type_; vhdl_expr *expr_; string_list_t sensitivity_; }; class vhdl_null_stmt : public vhdl_seq_stmt { public: void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t&, vhdl_var_set_t&) {} }; enum vhdl_severity_t { SEVERITY_NOTE, SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_FAILURE }; class vhdl_report_stmt : public vhdl_seq_stmt { public: vhdl_report_stmt(vhdl_expr *text, vhdl_severity_t severity = SEVERITY_NOTE); virtual ~vhdl_report_stmt() {} virtual void emit(ostream& of, int level) const; void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_severity_t severity_; vhdl_expr *text_; }; class vhdl_assert_stmt : public vhdl_report_stmt { public: vhdl_assert_stmt(const char *reason); void emit(ostream &of, int level) const; }; class vhdl_if_stmt : public vhdl_seq_stmt { public: vhdl_if_stmt(vhdl_expr *test); ~vhdl_if_stmt(); stmt_container *get_then_container() { return &then_part_; } stmt_container *get_else_container() { return &else_part_; } stmt_container *add_elsif(vhdl_expr *test); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: struct elsif { vhdl_expr *test; stmt_container *container; }; vhdl_expr *test_; stmt_container then_part_, else_part_; std::list elsif_parts_; }; /* * A single branch in a case statement consisting of an * expression part and a statement container. */ class vhdl_case_branch : public vhdl_element { friend class vhdl_case_stmt; public: vhdl_case_branch(vhdl_expr *when) : when_(when) {} ~vhdl_case_branch(); stmt_container *get_container() { return &stmts_; } void emit(std::ostream &of, int level) const; private: vhdl_expr *when_; stmt_container stmts_; }; typedef std::list case_branch_list_t; class vhdl_case_stmt : public vhdl_seq_stmt { public: vhdl_case_stmt(vhdl_expr *test) : test_(test) {} ~vhdl_case_stmt(); void add_branch(vhdl_case_branch *b) { branches_.push_back(b); } void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_expr *test_; case_branch_list_t branches_; }; class vhdl_loop_stmt : public vhdl_seq_stmt { public: virtual ~vhdl_loop_stmt() {} stmt_container *get_container() { return &stmts_; } void emit(std::ostream &of, int level) const; virtual void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: stmt_container stmts_; }; class vhdl_while_stmt : public vhdl_loop_stmt { public: vhdl_while_stmt(vhdl_expr *test) : test_(test) {} ~vhdl_while_stmt(); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_expr *test_; }; class vhdl_for_stmt : public vhdl_loop_stmt { public: vhdl_for_stmt(const char *lname, vhdl_expr *from, vhdl_expr *to) : lname_(lname), from_(from), to_(to) {} ~vhdl_for_stmt(); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: const char *lname_; vhdl_expr *from_, *to_; }; /* * A procedure call. Which is a statement, unlike a function * call which is an expression. */ class vhdl_pcall_stmt : public vhdl_seq_stmt { public: vhdl_pcall_stmt(const char *name) : name_(name) {} void emit(std::ostream &of, int level) const; void add_expr(vhdl_expr *e) { exprs_.add_expr(e); } void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: std::string name_; vhdl_expr_list exprs_; }; /* * A declaration of some sort (variable, component, etc.). * Declarations have names, which is the identifier of the variable, * constant, etc. not the type. */ class vhdl_decl : public vhdl_element { public: vhdl_decl(const string& name, const vhdl_type *type = NULL, vhdl_expr *initial = NULL) : name_(name), type_(type), initial_(initial), has_initial_(initial != NULL) {} virtual ~vhdl_decl(); const std::string &get_name() const { return name_; } const vhdl_type *get_type() const; void set_type(vhdl_type *t) { type_ = t; } void set_initial(vhdl_expr *initial); bool has_initial() const { return has_initial_; } // Return a new reference to this declaration vhdl_var_ref* make_ref() const; // The different sorts of assignment statement // ASSIGN_CONST is used to generate a variable to shadow a // constant that cannot be assigned to (e.g. a function parameter) enum assign_type_t { ASSIGN_BLOCK, ASSIGN_NONBLOCK, ASSIGN_CONST }; // Get the sort of assignment statement to generate for // assignments to this declaration // For some sorts of declarations it doesn't make sense // to assign to it so calling assignment_type just raises // an assertion failure virtual assign_type_t assignment_type() const { assert(false); return ASSIGN_BLOCK; } // True if this declaration can be read from virtual bool is_readable() const { return true; } // Modify this declaration so it can be read from // This does nothing for most declaration types virtual void ensure_readable() {} protected: std::string name_; const vhdl_type *type_; vhdl_expr *initial_; bool has_initial_; }; typedef std::list decl_list_t; /* * A forward declaration of a component. At the moment it is assumed * that components declarations will only ever be for entities * generated by this code generator. This is enforced by making the * constructor private (use component_decl_for instead). */ class vhdl_component_decl : public vhdl_decl { public: static vhdl_component_decl *component_decl_for(vhdl_entity *ent); void emit(std::ostream &of, int level) const; private: vhdl_component_decl(const char *name); decl_list_t ports_; }; class vhdl_type_decl : public vhdl_decl { public: vhdl_type_decl(const string& name, const vhdl_type *base) : vhdl_decl(name, base) {} void emit(std::ostream &of, int level) const; }; /* * A variable declaration inside a process (although this isn't * enforced here). */ class vhdl_var_decl : public vhdl_decl { public: vhdl_var_decl(const string& name, const vhdl_type *type) : vhdl_decl(name, type) {} void emit(std::ostream &of, int level) const; assign_type_t assignment_type() const { return ASSIGN_BLOCK; } }; /* * A signal declaration in architecture. */ class vhdl_signal_decl : public vhdl_decl { public: vhdl_signal_decl(const string& name, const vhdl_type* type) : vhdl_decl(name, type) {} virtual void emit(std::ostream &of, int level) const; assign_type_t assignment_type() const { return ASSIGN_NONBLOCK; } }; /* * A parameter to a function. */ class vhdl_param_decl : public vhdl_decl { public: vhdl_param_decl(const char *name, vhdl_type *type) : vhdl_decl(name, type) {} void emit(std::ostream &of, int level) const; assign_type_t assignment_type() const { return ASSIGN_CONST; } }; enum vhdl_port_mode_t { VHDL_PORT_IN, VHDL_PORT_OUT, VHDL_PORT_INOUT, VHDL_PORT_BUFFER }; /* * A port declaration is like a signal declaration except * it has a direction and appears in the entity rather than * the architecture. */ class vhdl_port_decl : public vhdl_decl { public: vhdl_port_decl(const char *name, vhdl_type *type, vhdl_port_mode_t mode) : vhdl_decl(name, type), mode_(mode) {} void emit(std::ostream &of, int level) const; vhdl_port_mode_t get_mode() const { return mode_; } void set_mode(vhdl_port_mode_t m) { mode_ = m; } assign_type_t assignment_type() const { return ASSIGN_NONBLOCK; } void ensure_readable(); bool is_readable() const; private: vhdl_port_mode_t mode_; }; /* * A mapping from port name to an expression. */ struct port_map_t { std::string name; vhdl_expr *expr; }; typedef std::list port_map_list_t; /* * Instantiation of component. This is really only a placeholder * at the moment until the port mappings are worked out. */ class vhdl_comp_inst : public vhdl_conc_stmt { public: vhdl_comp_inst(const char *inst_name, const char *comp_name); ~vhdl_comp_inst(); void emit(std::ostream &of, int level) const; void map_port(const string& name, vhdl_expr *expr); const std::string &get_comp_name() const { return comp_name_; } const std::string &get_inst_name() const { return inst_name_; } private: std::string comp_name_, inst_name_; port_map_list_t mapping_; }; /* * Contains a list of declarations in a hierarchy. * A scope can be `initializing' where assignments automatically * create initial values for declarations. */ class vhdl_scope { public: vhdl_scope(); ~vhdl_scope(); void add_decl(vhdl_decl *decl); void add_forward_decl(vhdl_decl *decl); vhdl_decl *get_decl(const std::string &name) const; bool have_declared(const std::string &name) const; bool name_collides(const string& name) const; bool contained_within(const vhdl_scope *other) const; vhdl_scope *get_parent() const; bool empty() const { return decls_.empty(); } const decl_list_t &get_decls() const { return decls_; } void set_parent(vhdl_scope *p) { parent_ = p; } bool initializing() const { return init_; } void set_initializing(bool i); bool hoisted_initialiser() const; void hoisted_initialiser(bool h); void set_allow_signal_assignment(bool b) { sig_assign_ = b; } bool allow_signal_assignment() const { return sig_assign_; } private: decl_list_t decls_; vhdl_scope *parent_; bool init_, sig_assign_; bool hoisted_init_; }; /* * Any sort of procedural element: process, function, or * procedure. Roughly these map onto Verilog's processes, * functions, and tasks. */ class vhdl_procedural { public: vhdl_procedural() : contains_wait_stmt_(false) {} virtual ~vhdl_procedural() {} virtual stmt_container *get_container() { return &stmts_; } virtual vhdl_scope *get_scope() { return &scope_; } void added_wait_stmt() { contains_wait_stmt_ = true; } bool contains_wait_stmt() const { return contains_wait_stmt_; } // Managing set of blocking assignment targets in this block void add_blocking_target(vhdl_var_ref* ref); bool is_blocking_target(vhdl_var_ref* ref) const; protected: stmt_container stmts_; vhdl_scope scope_; // If this is true then the body contains a `wait' statement // embedded in it somewhere // If this is the case then we can't use a sensitivity list for // the process bool contains_wait_stmt_; // The set of variable we have performed a blocking // assignment to set blocking_targets_; }; class vhdl_function : public vhdl_decl, public vhdl_procedural { friend class vhdl_forward_fdecl; public: vhdl_function(const char *name, vhdl_type *ret_type); virtual void emit(std::ostream &of, int level) const; vhdl_scope *get_scope() { return &variables_; } void add_param(vhdl_param_decl *p) { scope_.add_decl(p); } private: vhdl_scope variables_; }; class vhdl_forward_fdecl : public vhdl_decl { public: vhdl_forward_fdecl(const vhdl_function *f) : vhdl_decl((f->get_name() + "_Forward").c_str()), f_(f) {} void emit(std::ostream &of, int level) const; private: const vhdl_function *f_; }; class vhdl_process : public vhdl_conc_stmt, public vhdl_procedural { public: vhdl_process(const char *name = "") : name_(name) {} void emit(std::ostream &of, int level) const; void add_sensitivity(const std::string &name); private: std::string name_; string_list_t sens_; }; /* * An architecture which implements an entity. */ class vhdl_arch : public vhdl_element { public: vhdl_arch(const string& entity, const string& name) : name_(name), entity_(entity) {} virtual ~vhdl_arch(); void emit(std::ostream &of, int level=0) const; void add_stmt(vhdl_process *proc); void add_stmt(vhdl_conc_stmt *stmt); vhdl_scope *get_scope() { return &scope_; } private: conc_stmt_list_t stmts_; vhdl_scope scope_; std::string name_, entity_; }; /* * An entity defines the ports, parameters, etc. of a module. Each * entity is associated with a single architecture (although * technically this need not be the case). Entities are `derived' * from instantiations of Verilog module scopes in the hierarchy. */ class vhdl_entity : public vhdl_element { public: vhdl_entity(const string& name, vhdl_arch *arch, int depth=0); virtual ~vhdl_entity(); void emit(std::ostream &of, int level=0) const; void add_port(vhdl_port_decl *decl); vhdl_arch *get_arch() const { return arch_; } const std::string &get_name() const { return name_; } vhdl_scope *get_scope() { return &ports_; } void set_time_units(int units, int precision); friend vhdl_const_time* scale_time(const vhdl_entity* ent, uint64_t t); // Each entity has an associated depth which is how deep in // the Verilog module hierarchy it was found // This is used to limit the maximum depth of modules emitted const int depth; private: std::string name_; vhdl_arch *arch_; // Entity may only have a single architecture vhdl_scope ports_; // Entities have an associated VHDL time unit // This is used to implement the Verilog timescale directive time_unit_t time_unit_; }; typedef std::list entity_list_t; #endif iverilog-10_1/tgt-vhdl/vhdl_target.h000066400000000000000000000017651265551621300175600ustar00rootroot00000000000000// -*- mode: c++ -*- #ifndef INC_VHDL_TARGET_H #define INC_VHDL_TARGET_H #include "vhdl_config.h" #include "ivl_target.h" #include "support.hh" #include "vhdl_syntax.hh" #include using namespace std; void error(const char *fmt, ...); void debug_msg(const char *fmt, ...); int draw_scope(ivl_scope_t scope, void *_parent); extern "C" int draw_process(ivl_process_t net, void *cd); int draw_stmt(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last = false); int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm); void draw_logic(vhdl_arch *arch, ivl_net_logic_t log); vhdl_expr *translate_expr(ivl_expr_t e); vhdl_expr *translate_time_expr(ivl_expr_t e); ivl_design_t get_vhdl_design(); vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus); vhdl_var_ref* readable_ref(vhdl_scope* scope, ivl_nexus_t nex); string make_safe_name(ivl_signal_t sig); void require_support_function(support_function_t f); #endif /* #ifndef INC_VHDL_TARGET_H */ iverilog-10_1/tgt-vhdl/vhdl_type.cc000066400000000000000000000110061265551621300173760ustar00rootroot00000000000000/* * VHDL variable and signal types. * * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vhdl_type.hh" #include #include #include vhdl_type *vhdl_type::std_logic() { return new vhdl_type(VHDL_TYPE_STD_LOGIC); } vhdl_type *vhdl_type::std_ulogic() { return new vhdl_type(VHDL_TYPE_STD_ULOGIC); } vhdl_type *vhdl_type::string() { return new vhdl_type(VHDL_TYPE_STRING); } vhdl_type *vhdl_type::line() { return new vhdl_type(VHDL_TYPE_LINE); } vhdl_type *vhdl_type::boolean() { return new vhdl_type(VHDL_TYPE_BOOLEAN); } vhdl_type *vhdl_type::integer() { return new vhdl_type(VHDL_TYPE_INTEGER); } vhdl_type *vhdl_type::nunsigned(int width, int lsb) { return new vhdl_type(VHDL_TYPE_UNSIGNED, width-1+lsb, lsb); } vhdl_type *vhdl_type::nsigned(int width, int lsb) { return new vhdl_type(VHDL_TYPE_SIGNED, width-1+lsb, lsb); } vhdl_type *vhdl_type::time() { return new vhdl_type(VHDL_TYPE_TIME); } vhdl_type *vhdl_type::get_base() const { assert(name_ == VHDL_TYPE_ARRAY); return base_; } /* * This is just the name of the type, without any parameters. */ std::string vhdl_type::get_string() const { switch (name_) { case VHDL_TYPE_STD_LOGIC: return std::string("std_logic"); case VHDL_TYPE_STD_ULOGIC: return std::string("std_ulogic"); case VHDL_TYPE_STD_LOGIC_VECTOR: return std::string("std_logic_vector"); case VHDL_TYPE_STRING: return std::string("String"); case VHDL_TYPE_LINE: return std::string("Line"); case VHDL_TYPE_FILE: return std::string("File"); case VHDL_TYPE_INTEGER: return std::string("Integer"); case VHDL_TYPE_BOOLEAN: return std::string("Boolean"); case VHDL_TYPE_SIGNED: return std::string("signed"); case VHDL_TYPE_UNSIGNED: return std::string("unsigned"); case VHDL_TYPE_ARRAY: // Each array has its own type declaration return array_name_; default: return std::string("BadType"); } } /* * The is the qualified name of the type. */ std::string vhdl_type::get_decl_string() const { switch (name_) { case VHDL_TYPE_STD_LOGIC_VECTOR: case VHDL_TYPE_UNSIGNED: case VHDL_TYPE_SIGNED: { std::ostringstream ss; ss << get_string() << "(" << msb_; ss << " downto " << lsb_ << ")"; return ss.str(); } default: return get_string(); } } /* * Like get_decl_string but completely expands array declarations. */ std::string vhdl_type::get_type_decl_string() const { switch (name_) { case VHDL_TYPE_ARRAY: { std::ostringstream ss; ss << "array (" << msb_ << " downto " << lsb_ << ") of " << base_->get_decl_string(); return ss.str(); } default: return get_decl_string(); } } void vhdl_type::emit(std::ostream &of, int) const { of << get_decl_string(); } vhdl_type::vhdl_type(const vhdl_type &other) : vhdl_element(other), name_(other.name_), msb_(other.msb_), lsb_(other.lsb_), array_name_(other.array_name_) { if (other.base_ != NULL) base_ = new vhdl_type(*other.base_); else base_ = NULL; } vhdl_type::~vhdl_type() { delete base_; } vhdl_type *vhdl_type::std_logic_vector(int msb, int lsb) { return new vhdl_type(VHDL_TYPE_STD_LOGIC_VECTOR, msb, lsb); } vhdl_type *vhdl_type::type_for(int width, bool issigned, int lsb, bool unresolved) { if (width == 1) { if (unresolved) return vhdl_type::std_ulogic(); else return vhdl_type::std_logic(); } else if (issigned) return vhdl_type::nsigned(width, lsb); else return vhdl_type::nunsigned(width, lsb); } vhdl_type *vhdl_type::array_of(vhdl_type *b, std::string &n, int m, int l) { return new vhdl_type(b, n, m, l); } iverilog-10_1/tgt-vhdl/vhdl_type.hh000066400000000000000000000057641265551621300174260ustar00rootroot00000000000000/* * VHDL variable and signal types. * * Copyright (C) 2008-2010 Nick Gasson (nick@nickg.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef INC_VHDL_TYPE_HH #define INC_VHDL_TYPE_HH #include "vhdl_element.hh" enum vhdl_type_name_t { VHDL_TYPE_STD_LOGIC, VHDL_TYPE_STD_ULOGIC, VHDL_TYPE_STD_LOGIC_VECTOR, VHDL_TYPE_STRING, VHDL_TYPE_LINE, VHDL_TYPE_FILE, VHDL_TYPE_INTEGER, VHDL_TYPE_BOOLEAN, VHDL_TYPE_SIGNED, VHDL_TYPE_UNSIGNED, VHDL_TYPE_TIME, VHDL_TYPE_ARRAY }; /* * A type at the moment is just a name. It shouldn't get * too much more complex, as Verilog's type system is much * simpler than VHDL's. */ class vhdl_type : public vhdl_element { public: // Scalar constructor vhdl_type(vhdl_type_name_t name, int msb = 0, int lsb = 0) : name_(name), msb_(msb), lsb_(lsb), base_(NULL) {} // Array constructor vhdl_type(vhdl_type *base, const std::string &array_name, int msb, int lsb) : name_(VHDL_TYPE_ARRAY), msb_(msb), lsb_(lsb), base_(base), array_name_(array_name) {} // Copy constructor vhdl_type(const vhdl_type &other); virtual ~vhdl_type(); void emit(std::ostream &of, int level) const; vhdl_type_name_t get_name() const { return name_; } std::string get_string() const; std::string get_decl_string() const; std::string get_type_decl_string() const; vhdl_type *get_base() const; int get_width() const { return msb_ - lsb_ + 1; } int get_msb() const { return msb_; } int get_lsb() const { return lsb_; } // Common types static vhdl_type *std_logic(); static vhdl_type *std_ulogic(); static vhdl_type *string(); static vhdl_type *line(); static vhdl_type *std_logic_vector(int msb, int lsb); static vhdl_type *nunsigned(int width, int lsb=0); static vhdl_type *nsigned(int width, int lsb=0); static vhdl_type *integer(); static vhdl_type *boolean(); static vhdl_type *time(); static vhdl_type *type_for(int width, bool issigned, int lsb=0, bool unresolved=false); static vhdl_type *array_of(vhdl_type *b, std::string &n, int m, int l); protected: vhdl_type_name_t name_; int msb_, lsb_; vhdl_type *base_; // Array base type for VHDL_TYPE_ARRAY std::string array_name_; // Type name for the array `type array_name_ is ...' }; #endif iverilog-10_1/tgt-vhdl/vhpi/000077500000000000000000000000001265551621300160415ustar00rootroot00000000000000iverilog-10_1/tgt-vhdl/vhpi/finish.c000066400000000000000000000000671265551621300174700ustar00rootroot00000000000000#include void finish(void) { exit(0); } iverilog-10_1/tgt-vlog95/000077500000000000000000000000001265551621300152635ustar00rootroot00000000000000iverilog-10_1/tgt-vlog95/Makefile.in000066400000000000000000000055721265551621300173410ustar00rootroot00000000000000# # Copyright (C) 2011-2012 Cary R. (cygcary@yahoo.com) # # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = vlog95.o event.o expr.o logic_lpm.o misc.o numbers.o scope.o stmt.o udp.o all: dep vlog95.tgt check: all clean: rm -rf *.o dep vlog95.tgt distclean: clean rm -f Makefile config.log cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-vlog95/$@ dep: mkdir dep %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif vlog95.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O -lm $(TGTLDFLAGS) install: all installdirs $(libdir)/ivl$(suffix)/vlog95.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/vlog95.conf $(libdir)/ivl$(suffix)/vlog95-s.conf $(libdir)/ivl$(suffix)/vlog95.tgt: ./vlog95.tgt $(INSTALL_PROGRAM) ./vlog95.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.tgt" $(libdir)/ivl$(suffix)/vlog95.conf: $(srcdir)/vlog95.conf $(INSTALL_DATA) $(srcdir)/vlog95.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.conf" $(libdir)/ivl$(suffix)/vlog95-s.conf: $(srcdir)/vlog95-s.conf $(INSTALL_DATA) $(srcdir)/vlog95-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.tgt" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-vlog95/cppcheck.sup000066400000000000000000000002761265551621300176010ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:vlog95.c:59 // target_query() unusedFunction:vlog95.c:251 iverilog-10_1/tgt-vlog95/event.c000066400000000000000000000045351265551621300165570ustar00rootroot00000000000000/* * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include "config.h" # include "vlog95_priv.h" void emit_event(ivl_scope_t scope, ivl_statement_t stmt) { unsigned eidx, nevents, first = 1; nevents = ivl_stmt_nevent(stmt); for (eidx = 0; eidx < nevents; eidx += 1) { unsigned idx, count, had_edge = 0; ivl_event_t event = ivl_stmt_events(stmt, eidx); /* Check for any edge events. */ count = ivl_event_nany(event); if (count) had_edge = 1; for (idx = 0; idx < count; idx += 1) { if (first) first = 0; else fprintf(vlog_out, " or "); emit_nexus_as_ca(scope, ivl_event_any(event, idx), 0, 0); } /* Check for positive edge events. */ count = ivl_event_npos(event); if (count) had_edge = 1; for (idx = 0; idx < count; idx += 1) { if (first) first = 0; else fprintf(vlog_out, " or "); fprintf(vlog_out, "posedge "); emit_nexus_as_ca(scope, ivl_event_pos(event, idx), 0, 0); } /* Check for negative edge events. */ count = ivl_event_nneg(event); if (count) had_edge = 1; for (idx = 0; idx < count; idx += 1) { if (first) first = 0; else fprintf(vlog_out, " or "); fprintf(vlog_out, "negedge "); emit_nexus_as_ca(scope, ivl_event_neg(event, idx), 0, 0); } /* We have a named event if there were no edge events. */ if (!had_edge) { ivl_scope_t ev_scope = ivl_event_scope(event); if (first) first = 0; else fprintf(vlog_out, " or "); emit_scope_module_path(scope, ev_scope); emit_id(ivl_event_basename(event)); } } } iverilog-10_1/tgt-vlog95/expr.c000066400000000000000000001233461265551621300164160ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include "config.h" # include "vlog95_priv.h" /* The expression code needs to know when a parameter definition is being * emitted so it can print the numeric value instead of the parameter name. */ ivl_parameter_t emitting_param = 0; /* * Data type used to signify if a $signed or $unsigned should be emitted. */ typedef enum expr_sign_e { NO_SIGN = 0, NEED_SIGNED = 1, NEED_UNSIGNED = 2 } expr_sign_t; static expr_sign_t expr_get_binary_sign_type(ivl_expr_t expr) { ivl_expr_t oper1, oper2; int opr_sign = 0; int expr_sign = ivl_expr_signed(expr); expr_sign_t rtn = NO_SIGN; switch (ivl_expr_opcode(expr)) { case 'E': case 'e': case 'N': case 'n': case '<': case 'L': case '>': case 'G': /* The comparison operators always act as if the argument is * unsigned. */ break; case 'l': case 'r': case 'R': /* For the shift operators only the left operand is used to * determine the sign information. */ opr_sign = ivl_expr_signed(ivl_expr_oper1(expr)); break; default: /* For the rest of the opcodes the operator is considered to * be signed if either argument is real or if both arguments * are signed. */ oper1 = ivl_expr_oper1(expr); oper2 = ivl_expr_oper2(expr); if ((ivl_expr_value(oper1) == IVL_VT_REAL) || (ivl_expr_value(oper2) == IVL_VT_REAL)) { opr_sign = 1; } else if (ivl_expr_signed(oper1) && ivl_expr_signed(oper2)) { opr_sign = 1; } break; } /* Check to see if a $signed() or $unsigned() is needed. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED; return rtn; } static expr_sign_t expr_get_select_sign_type(ivl_expr_t expr, unsigned can_skip_unsigned) { int opr_sign = 0; int expr_sign = ivl_expr_signed(expr); expr_sign_t rtn = NO_SIGN; /* If there is no select expression then the sign is determined by * the expression that is being selected (padded). */ if (! ivl_expr_oper2(expr)) { ivl_expr_t oper1 = ivl_expr_oper1(expr); opr_sign = ivl_expr_signed(oper1); /* If the expression being padded is not a signal then don't * skip a soft $unsigned() (from the binary operator). */ if (ivl_expr_type(oper1) != IVL_EX_SIGNAL) can_skip_unsigned -= 1; } /* Check to see if a $signed() or $unsigned() is needed. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; return rtn; } static expr_sign_t expr_get_signal_sign_type(ivl_expr_t expr, unsigned can_skip_unsigned) { int opr_sign = ivl_signal_signed(ivl_expr_signal(expr)); int expr_sign = ivl_expr_signed(expr); expr_sign_t rtn = NO_SIGN; /* Check to see if a $signed() or $unsigned() is needed. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; return rtn; } static expr_sign_t expr_get_unary_sign_type(ivl_expr_t expr) { ivl_expr_t expr1; int opr_sign = 0; int expr_sign = ivl_expr_signed(expr); expr_sign_t rtn = NO_SIGN; switch (ivl_expr_opcode(expr)) { case '&': case '|': case '^': case 'A': case 'N': case 'X': /* The reduction operators always act as if the argument is * unsigned. */ break; case 'r': /* For a cast to real the expression should be signed and no * sign conversion is needed. */ opr_sign = expr_sign; if (! expr_sign) { fprintf(stderr, "%s:%u: vlog95 error: Cast to real " "expression is not signed.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } break; case '2': case 'v': /* If the cast to a vector value is from a real then no sign * conversion is needed. Otherwise use the actual argument. */ expr1 = ivl_expr_oper1(expr); if (ivl_expr_value(expr1) == IVL_VT_REAL) { opr_sign = expr_sign; } else { opr_sign = ivl_expr_signed(expr1); } break; default: /* For the rest of the opcodes the argument sign type depends * on the actual argument. */ opr_sign = ivl_expr_signed(ivl_expr_oper1(expr)); break; } /* Check to see if a $signed() or $unsigned() is needed. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED; return rtn; } /* * This routine is used to determine if the expression is a binary operator * where the width could be different to create a self-determined context. */ static unsigned expr_is_binary_self_det(ivl_expr_t expr) { unsigned rtn = 0; if (ivl_expr_type(expr) == IVL_EX_BINARY) { switch (ivl_expr_opcode(expr)) { case '+': case '-': case '*': case '/': case '%': case '&': case '|': case '^': case 'X': case 'A': case 'O': case 'R': case 'l': case 'r': rtn = 1; break; default: break; } } return rtn; } /* * Determine if a $signed() or $unsigned() system function is needed to get * the expression sign information correct. can_skip_unsigned may be set for * the binary/ternary operators if one of the operands will implicitly cast * the expression to unsigned. See calc_can_skip_unsigned() for the details. */ static expr_sign_t expr_get_sign_type(ivl_expr_t expr, unsigned wid, unsigned can_skip_unsigned, unsigned is_full_prec) { unsigned expr_wid = ivl_expr_width(expr); expr_sign_t rtn = NO_SIGN; ivl_expr_type_t type = ivl_expr_type(expr); switch (type) { case IVL_EX_BINARY: rtn = expr_get_binary_sign_type(expr); break; case IVL_EX_CONCAT: /* A concatenation is always unsigned so add a $signed() when * needed. */ if (ivl_expr_signed(expr)) rtn = NEED_SIGNED; break; case IVL_EX_SELECT: rtn = expr_get_select_sign_type(expr, can_skip_unsigned); /* If there is no select expression then this is padding so use * the actual expressions width if it is a binary operator that * could create a self-determined context. */ if (! ivl_expr_oper2(expr)) { ivl_expr_t oper1 = ivl_expr_oper1(expr); if (expr_is_binary_self_det(oper1)) { expr_wid = ivl_expr_width(oper1); } } break; case IVL_EX_SIGNAL: rtn = expr_get_signal_sign_type(expr, can_skip_unsigned); break; case IVL_EX_UNARY: rtn = expr_get_unary_sign_type(expr); break; /* These do not currently have sign casting information. A select * is used for that purpose. */ case IVL_EX_ARRAY: case IVL_EX_DELAY: case IVL_EX_ENUMTYPE: case IVL_EX_EVENT: case IVL_EX_NEW: case IVL_EX_NULL: case IVL_EX_NUMBER: case IVL_EX_PROPERTY: case IVL_EX_REALNUM: case IVL_EX_SCOPE: case IVL_EX_SFUNC: case IVL_EX_SHALLOWCOPY: case IVL_EX_STRING: case IVL_EX_TERNARY: case IVL_EX_UFUNC: default: break; } /* Check for a self-determined context. A zero width expression * is special and is not considered a self determined context. */ if ((rtn == NO_SIGN) && (wid != expr_wid) && expr_wid && ! (is_full_prec && ((expr_wid < wid) || (type == IVL_EX_SIGNAL)))) { if (ivl_expr_signed(expr)) rtn = NEED_SIGNED; else rtn = NEED_UNSIGNED; } return rtn; } /* * We can convert any 2^n ** expression to * 1 << (n * ). If the variable is signed then we need * to add a check for less than zero and for that case return 0. */ static unsigned emit_power_as_shift(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { int rtype; int64_t value, scale; unsigned is_signed_rval = 0; unsigned is_signed_expr = ivl_expr_signed(expr); unsigned expr_wid; ivl_expr_t lval = ivl_expr_oper1(expr); ivl_expr_t rval = ivl_expr_oper2(expr); (void)wid; /* Parameter is not used. */ /* The L-value must be a number. */ if (ivl_expr_type(lval) != IVL_EX_NUMBER) return 0; /* The L-value must of the form 2^n. */ value = get_int64_from_number(lval, &rtype); if (rtype) return 0; expr_wid = ivl_expr_width(lval); if (value < 2) return 0; if (value % 2) return 0; /* Generate the appropriate conversion. */ if (ivl_expr_signed(rval)) { emit_expr(scope, rval, 0, 0, 0, 0); fprintf(vlog_out, " < 0 ? "); if (is_signed_expr) { if (expr_wid == 32) { fprintf(vlog_out, "0"); } else if (allow_signed) { fprintf(vlog_out, "%u'sh0", expr_wid); } else { fprintf(stderr, "%s:%u: vlog95 error: Sized signed " "power operator l-value is not " "supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; fprintf(vlog_out, "%u'h0", expr_wid); } } else { fprintf(vlog_out, "%u'h0", expr_wid); } fprintf(vlog_out, " : ("); is_signed_rval = 1; } scale = value / 2; if (is_signed_expr) { if (expr_wid == 32) { fprintf(vlog_out, "1"); } else if (allow_signed) { fprintf(vlog_out, "%u'sh1", expr_wid); } else { /* This is an error condition and has already been * reported above. */ fprintf(vlog_out, "%u'h1", expr_wid); } } else { fprintf(vlog_out, "%u'h1", expr_wid); } fprintf(vlog_out, " << "); if (scale != 1) { if (is_signed_rval) { fprintf(vlog_out, "(%"PRId64, scale); } else { fprintf(vlog_out, "(%u'h%"PRIx64, ivl_expr_width(rval), scale); } fprintf(vlog_out, " * "); } emit_expr(scope, rval, 0, 0, 0, 0); if (scale != 1) fprintf(vlog_out, ")"); if (is_signed_rval) fprintf(vlog_out, ")"); return 1; } static void emit_expr_array(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); (void)wid; /* Parameter is not used. */ emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); } /* * Some of the operators can do an implicit cast to real so for them emit * the non-real argument in a self-determined context. */ static unsigned get_cast_width(ivl_expr_t expr, ivl_expr_t oper, unsigned wid) { ivl_variable_type_t expr_type = ivl_expr_value(expr); if ((expr_type == IVL_VT_REAL) && (expr_type != ivl_expr_value(oper))) { wid = 0; } return wid; } static unsigned calc_can_skip_unsigned(ivl_expr_t oper1, ivl_expr_t oper2) { unsigned oper1_signed, oper2_signed; /* Check to see if the operands are signed or softly signed. * The expression is signed (hard). * It is a signed signal cast to unsigned (soft). * It is a padding cast from signed to unsigned (soft). */ oper1_signed = ivl_expr_signed(oper1); oper1_signed |= (ivl_expr_type(oper1) == IVL_EX_SIGNAL) && (ivl_signal_signed(ivl_expr_signal(oper1))); oper1_signed |= (ivl_expr_type(oper1) == IVL_EX_SELECT) && (! ivl_expr_oper2(oper1)) && (ivl_expr_signed(ivl_expr_oper1(oper1))); oper2_signed = ivl_expr_signed(oper2); oper2_signed |= (ivl_expr_type(oper2) == IVL_EX_SIGNAL) && (ivl_signal_signed(ivl_expr_signal(oper2))); oper2_signed |= (ivl_expr_type(oper2) == IVL_EX_SELECT) && (! ivl_expr_oper2(oper2)) && (ivl_expr_signed(ivl_expr_oper1(oper2))); /* If either operand is a hard unsigned skip adding an explicit * $unsigned() since it will be added implicitly. */ return ! oper1_signed || ! oper2_signed; } static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, unsigned is_full_prec) { const char *oper = ""; ivl_expr_t oper1 = ivl_expr_oper1(expr); ivl_expr_t oper2 = ivl_expr_oper2(expr); unsigned can_skip_unsigned = calc_can_skip_unsigned(oper1, oper2); switch (ivl_expr_opcode(expr)) { case '+': oper = "+"; break; case '-': oper = "-"; break; case '*': oper = "*"; break; case '/': oper = "/"; break; case '%': oper = "%"; break; case 'p': oper = "**"; break; case 'E': oper = "==="; break; case 'e': oper = "=="; break; case 'N': oper = "!=="; break; case 'n': oper = "!="; break; case '<': oper = "<"; break; case 'L': oper = "<="; break; case '>': oper = ">"; break; case 'G': oper = ">="; break; case '&': oper = "&"; break; case '|': oper = "|"; break; case '^': oper = "^"; break; case 'A': oper = "&"; break; case 'O': oper = "|"; break; case 'X': oper = "~^"; break; case 'a': oper = "&&"; break; case 'o': oper = "||"; break; case 'l': oper = "<<"; break; case 'r': oper = ">>"; break; case 'R': oper = ">>>"; break; case 'm': oper = "min"; break; case 'M': oper = "max"; break; } fprintf(vlog_out, "("); switch (ivl_expr_opcode(expr)) { case '%': if (ivl_expr_value(expr) == IVL_VT_REAL) { fprintf(stderr, "%s:%u: vlog95 error: Real modulus operator " "is not supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } case '+': case '-': case '*': case '/': emit_expr(scope, oper1, get_cast_width(expr, oper1, wid), 0, can_skip_unsigned, is_full_prec); fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, get_cast_width(expr, oper2, wid), 0, can_skip_unsigned, is_full_prec); break; case '&': case '|': case '^': case 'X': emit_expr(scope, oper1, wid, 0, can_skip_unsigned, is_full_prec); fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, wid, 0, can_skip_unsigned, is_full_prec); break; case 'E': case 'e': case 'N': case 'n': case '<': case 'L': case '>': case 'G': emit_expr(scope, oper1, ivl_expr_width(oper1), 0, can_skip_unsigned, 0); fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, ivl_expr_width(oper2), 0, can_skip_unsigned, 0); break; case 'a': case 'o': emit_expr(scope, oper1, ivl_expr_width(oper1), 0, 1, 0); fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, ivl_expr_width(oper2), 0, 1, 0); break; case 'R': if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not " "supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } case 'l': case 'r': emit_expr(scope, oper1, wid, 0, 0, 0); fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, ivl_expr_width(oper2), 0, 2, 0); break; case 'A': case 'O': fprintf(vlog_out, "~("); emit_expr(scope, oper1, wid, 0, can_skip_unsigned, is_full_prec); fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, wid, 0, can_skip_unsigned, is_full_prec); fprintf(vlog_out, ")"); break; case 'p': if (! emit_power_as_shift(scope, expr, wid)) { emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, " ** "); emit_expr(scope, oper2, ivl_expr_width(oper2), 0, 0, 0); fprintf(stderr, "%s:%u: vlog95 error: Power operator is not " "supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } break; /* Convert the Verilog-A min() or max() functions. */ case 'm': case 'M': if (ivl_expr_value(expr) == IVL_VT_REAL) { /* For a real expression use the $min()/$max() function. */ if (ivl_expr_opcode(expr) == 'm') fprintf(vlog_out, "$min("); else fprintf(vlog_out, "$max("); emit_expr(scope, oper1, wid, 0, 0, 0); fprintf(vlog_out, ","); emit_expr(scope, oper2, wid, 0, 0, 0); fprintf(vlog_out, ")"); } else { /* This only works when the argument has no side effect. */ fprintf(vlog_out, "(("); emit_expr(scope, oper1, wid, 0, 0, 0); fprintf(vlog_out, ") %s (", oper); emit_expr(scope, oper2, wid, 0, 0, 0); fprintf(vlog_out, ") ? ("); emit_expr(scope, oper1, wid, 0, 0, 0); fprintf(vlog_out, ") : ("); emit_expr(scope, oper2, wid, 0, 0, 0); fprintf(vlog_out, "))"); } break; default: emit_expr(scope, oper1, wid, 0, can_skip_unsigned, 0); fprintf(vlog_out, ""); emit_expr(scope, oper2, wid, 0, can_skip_unsigned, 0); fprintf(stderr, "%s:%u: vlog95 error: Unknown expression " "operator (%c).\n", ivl_expr_file(expr), ivl_expr_lineno(expr), ivl_expr_opcode(expr)); vlog_errors += 1; break; } fprintf(vlog_out, ")"); } static void emit_expr_concat(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { unsigned repeat = ivl_expr_repeat(expr); unsigned idx, count = ivl_expr_parms(expr); (void)wid; /* Parameter is not used. */ if (repeat != 1) fprintf(vlog_out, "{%u", repeat); fprintf(vlog_out, "{"); count -= 1; for (idx = 0; idx < count; idx += 1) { emit_expr(scope, ivl_expr_parm(expr, idx), 0, 0, 0, 0); fprintf(vlog_out, ", "); } emit_expr(scope, ivl_expr_parm(expr, count), 0, 0, 0, 0); fprintf(vlog_out, "}"); if (repeat != 1) fprintf(vlog_out, "}"); } static void emit_expr_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { (void)wid; /* Parameter is not used. */ emit_scaled_delay(scope, ivl_expr_delay_val(expr)); } /* * An event in an expression context must be a named event. */ static void emit_expr_event(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_event_t event = ivl_expr_event(expr); ivl_scope_t ev_scope = ivl_event_scope(event); (void)wid; /* Parameter is not used. */ assert(! ivl_event_nany(event)); assert(! ivl_event_npos(event)); assert(! ivl_event_nneg(event)); emit_scope_call_path(scope, ev_scope); emit_id(ivl_event_basename(event)); } /* * A number can also be a parameter reference. If it is a parameter * reference then emit the appropriate parameter name instead of the * numeric value unless this is the actual parameter definition. */ static void emit_expr_number(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_parameter_t param = ivl_expr_parameter(expr); (void)wid; /* Parameter is not used. */ if (param && (param != emitting_param)) { emit_scope_call_path(scope, ivl_parameter_scope(param)); emit_id(ivl_parameter_basename(param)); } else { emit_number(ivl_expr_bits(expr), ivl_expr_width(expr), ivl_expr_signed(expr), ivl_expr_file(expr), ivl_expr_lineno(expr)); } } static void emit_expr_real_number(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_parameter_t param = ivl_expr_parameter(expr); (void)wid; /* Parameter is not used. */ if (param && (param != emitting_param)) { emit_scope_call_path(scope, ivl_parameter_scope(param)); emit_id(ivl_parameter_basename(param)); } else { emit_real_number(ivl_expr_dvalue(expr)); } } /* * Class properties are not supported in vlog95, but they can be translated. */ static void emit_class_property(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); (void)wid; /* Parameter is not used. */ emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); fprintf(vlog_out, ".%s", ivl_expr_name(expr)); } static void emit_expr_scope_piece(ivl_scope_t scope) { ivl_scope_t parent = ivl_scope_parent(scope); /* If this scope has a parent then emit it first. */ if (parent) { emit_expr_scope_piece(parent); fprintf(vlog_out, "."); } emit_id(ivl_scope_basename(scope)); } static void emit_expr_scope(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { (void)scope; /* Parameter is not used. */ (void)wid; /* Parameter is not used. */ emit_expr_scope_piece(ivl_expr_scope(expr)); } static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr) { /* A select of a number is really a parameter select. */ if (ivl_expr_type(expr) == IVL_EX_NUMBER) { ivl_parameter_t param = ivl_expr_parameter(expr); if (param) { emit_scope_call_path(scope, ivl_parameter_scope(param)); emit_id(ivl_parameter_basename(param)); } else { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unable to find " "parameter for select expression \n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } } else { emit_expr(scope, expr, 0, 0, 0, 0); } } /* * Emit a packed array access as a concatenation of bit selects. */ static void emit_expr_packed(ivl_scope_t scope, ivl_expr_t sig_expr, ivl_expr_t sel_expr, unsigned wid) { unsigned idx; assert(wid > 0); fprintf(vlog_out, "{"); for (idx = wid - 1; idx > 0; idx -= 1) { emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_expr(scope, sel_expr, 0, 0, 0, 1); fprintf(vlog_out, " + %u], ", idx); } emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_expr(scope, sel_expr, 0, 0, 0, 1); fprintf(vlog_out, "]}"); } /* * Emit an indexed part select as a concatenation of bit selects. Since a * parameter in 1364-1995 is always zero based the select expression needs * to keep any scaling that was done by the compiler. */ static void emit_expr_ips(ivl_scope_t scope, ivl_expr_t sig_expr, ivl_expr_t sel_expr, ivl_select_type_t sel_type, unsigned wid, int msb, int lsb, unsigned is_param) { unsigned idx; assert(wid > 0); /* If it was not already given, note that a down index part select will * require the -pallowsigned=1 flag to get the index values correct if * the select expression is not signed. */ if ((! allow_signed ) && (sel_type == IVL_SEL_IDX_DOWN) && (! ivl_expr_signed(sel_expr))) { fprintf(stderr, "%s:%u: vlog95 note: Translating a down indexed " "part select requires the -pallowsigned=1 flag.\n", ivl_expr_file(sel_expr), ivl_expr_lineno(sel_expr)); } fprintf(vlog_out, "{"); if (msb >= lsb) { if ((sel_type == IVL_SEL_IDX_DOWN) && ! is_param) { lsb += wid - 1; msb += wid - 1; emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); for (idx = 1; idx < wid; idx += 1) { fprintf(vlog_out, ", "); emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " - %u]", idx); } fprintf(vlog_out, "}"); } else { assert((sel_type == IVL_SEL_IDX_UP) || (is_param && (sel_type == IVL_SEL_IDX_DOWN))); for (idx = wid - 1; idx > 0; idx -= 1) { emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " + %u], ", idx); } emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]}"); } } else { if ((sel_type == IVL_SEL_IDX_UP) && ! is_param) { lsb -= wid - 1; msb -= wid - 1; emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); for (idx = 1; idx < wid; idx += 1) { fprintf(vlog_out, ", "); emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " + %u]", idx); } fprintf(vlog_out, "}"); } else { assert((sel_type == IVL_SEL_IDX_DOWN) || (is_param && (sel_type == IVL_SEL_IDX_UP))); for (idx = wid - 1; idx > 0; idx -= 1) { emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " - %u], ", idx); } emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]}"); } } } static void check_select_signed(ivl_expr_t sig_expr, ivl_expr_t sel_expr, int msb, int lsb) { expr_sign_t sign_type = expr_get_sign_type(sel_expr, ivl_expr_width(sel_expr), 0, 1); char msg[64]; snprintf(msg, sizeof(msg), "%s:%u", ivl_expr_file(sel_expr), ivl_expr_lineno(sel_expr)); msg[sizeof(msg)-1] = 0; // HERE: These first two can be fixed if the compiler is enhanced to pass // the original sign information for the base expression. /* If the element has a MSB that is greater than or equal to the LSB * and the LSB is greater than zero the compiler created a signed * expression to normalize the access. This normalization will be * removed, but we cannot currently determine if the base expression * started out signed or not so any extra $signed() operators will * need to be removed manually. */ if ((msb >= lsb) && (lsb > 0) && (sign_type == NEED_SIGNED)) { fprintf(stderr, "%s: vlog95 sorry: The translation of a select " "with MSB >= LSB > 0 is not smart enough to remove " "the extra $signed() operators.\n", msg); fprintf(stderr, "%*s Any extra $signed() operators " "will need to be removed manually.\n", (int)strlen(msg), " "); vlog_errors += 1; /* If the element is not a parameter and the LSB > MSB then the cast * to signed ($signed()) from the normalization process may need to * be removed. If the select expression is a constant number then * this is not needed. */ } else if ((lsb > msb) && (ivl_expr_type(sig_expr) != IVL_EX_NUMBER) && (ivl_expr_type(sel_expr) != IVL_EX_NUMBER) && (sign_type == NEED_SIGNED)) { fprintf(stderr, "%s: vlog95 sorry: The translation of a select " "with LSB > MSB is not smart enough to remove " "the extra $signed() operators.\n", msg); fprintf(stderr, "%*s Any extra $signed() operators " "will need to be removed manually.\n", (int)strlen(msg), " "); vlog_errors += 1; /* Parameters are translated with normalization so for some of them * the -pallowsigned=1 flag is required to get the selection * expression 100% correct. */ } else if ((! allow_signed) && (ivl_expr_type(sig_expr) == IVL_EX_NUMBER)) { int pmsb, plsb; ivl_parameter_t param = ivl_expr_parameter(sig_expr); assert(param); pmsb = ivl_parameter_msb(param); plsb = ivl_parameter_lsb(param); if ((pmsb >= plsb) && (plsb > 0)) { fprintf(stderr, "%s: vlog95 note: Translating a MSB >= " "LSB > 0 parameter select requires the " "-pallowsigned=1 flag.\n", msg); } else if (plsb > pmsb) { fprintf(stderr, "%s: vlog95 note: Translating a LSB > " "MSB parameter select requires the " "-pallowsigned=1 flag.\n", msg); } } } static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_expr_t sel_expr = ivl_expr_oper2(expr); ivl_expr_t sig_expr = ivl_expr_oper1(expr); ivl_select_type_t sel_type = ivl_expr_sel_type(expr); (void)wid; /* Parameter is not used. */ /* If this is a dynamic array or queue select, translate the * select differently. */ if ((ivl_expr_type(sig_expr) == IVL_EX_SIGNAL) && ((ivl_signal_data_type(ivl_expr_signal(sig_expr)) == IVL_VT_DARRAY) || (ivl_signal_data_type(ivl_expr_signal(sig_expr)) == IVL_VT_QUEUE))) { assert(sel_expr); emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_expr(scope, sel_expr, 0, 0, 0, 1); fprintf(vlog_out, "]"); return; } if (sel_expr) { unsigned width = ivl_expr_width(expr); ivl_expr_type_t type = ivl_expr_type(sig_expr); assert(width > 0); /* The compiler uses selects for some shifts. */ if (type != IVL_EX_NUMBER && type != IVL_EX_SIGNAL) { fprintf(vlog_out, "(" ); emit_select_name(scope, sig_expr); fprintf(vlog_out, " >> " ); emit_scaled_expr(scope, sel_expr, 1, 0); fprintf(vlog_out, ")" ); } else { /* A constant/parameter must be zero based in 1364-1995 * so keep the compiler generated normalization. This * does not always work for selects before the parameter * since 1364-1995 does not support signed math. */ int msb = 1; int lsb = 0; if (type == IVL_EX_SIGNAL) { get_sig_msb_lsb(ivl_expr_signal(sig_expr), &msb, &lsb); } /* A bit select. */ if (width == 1) { check_select_signed(sig_expr, sel_expr, msb, lsb); emit_select_name(scope, sig_expr); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); } else if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { /* A constant part select. */ emit_select_name(scope, sig_expr); emit_scaled_range(scope, sel_expr, width, msb, lsb); } else if (sel_type == IVL_SEL_OTHER) { /* A packed array access. */ assert(lsb == 0); assert(msb >= 0); emit_expr_packed(scope, sig_expr, sel_expr, width); } else { /* An indexed part select. */ check_select_signed(sig_expr, sel_expr, msb, lsb); emit_expr_ips(scope, sig_expr, sel_expr, sel_type, width, msb, lsb, type == IVL_EX_NUMBER); } } } else { // HERE: Should this sign extend if the expression is signed? unsigned width = ivl_expr_width(expr); unsigned sig_wid = ivl_expr_width(sig_expr); emit_expr(scope, sig_expr, sig_wid, 0, 0, 0); /* Select part of a signal when needed. */ if ((ivl_expr_type(sig_expr) == IVL_EX_SIGNAL) && (width < sig_wid)) { int msb, lsb; int64_t value; get_sig_msb_lsb(ivl_expr_signal(sig_expr), &msb, &lsb); value = lsb; if (msb >= lsb) value += width - 1; else value -= width - 1; fprintf(vlog_out, "[%"PRId64":%u]", value, lsb); } } } /* * This routine is used to emit both system and user functions. */ static void emit_expr_func(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { unsigned count = ivl_expr_parms(expr); (void)wid; /* Parameter is not used. */ if (count) { unsigned idx; fprintf(vlog_out, "("); count -= 1; for (idx = 0; idx < count; idx += 1) { // HERE: For a user function should the argument width be used here. emit_expr(scope, ivl_expr_parm(expr, idx), 0, 1, 0, 0); fprintf(vlog_out, ", "); } // HERE: For a user function should the argument width be used here. emit_expr(scope, ivl_expr_parm(expr, count), 0, 1, 0, 0); fprintf(vlog_out, ")"); /* User functions without arguments are not supported so a dummy * argument is added both here and in the definition. */ } else if (ivl_expr_type(expr) == IVL_EX_UFUNC) { fprintf(vlog_out, "(1'bx)"); } } static void emit_expr_signal(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); (void)wid; /* Parameter is not used. */ emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig)) { int lsb = ivl_signal_array_base(sig); int msb = lsb + ivl_signal_array_count(sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb); fprintf(vlog_out, "]"); } } static void emit_expr_ternary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, unsigned is_full_prec) { ivl_expr_t oper2 = ivl_expr_oper2(expr); ivl_expr_t oper3 = ivl_expr_oper3(expr); unsigned can_skip_unsigned = calc_can_skip_unsigned(oper2, oper3); fprintf(vlog_out, "("); emit_expr(scope, ivl_expr_oper1(expr), 0, 0, 0, 0); fprintf(vlog_out, " ? "); // HERE: Do these two emits need to use get_cast_width() like the binary // arithmetic operators? emit_expr(scope, oper2, wid, 0, can_skip_unsigned, is_full_prec); fprintf(vlog_out, " : "); emit_expr(scope, oper3, wid, 0, can_skip_unsigned, is_full_prec); fprintf(vlog_out, ")"); } static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, unsigned is_full_prec) { const char *oper = "invalid"; ivl_expr_t oper1 = ivl_expr_oper1(expr); switch (ivl_expr_opcode(expr)) { case '-': oper = "-"; break; case '~': oper = "~"; break; case '&': oper = "&"; break; case '|': oper = "|"; break; case '^': oper = "^"; break; case 'A': oper = "~&"; break; case 'N': oper = "~|"; break; case 'X': oper = "~^"; break; case '!': oper = "!"; break; } switch (ivl_expr_opcode(expr)) { case '-': case '~': fprintf(vlog_out, "(%s", oper); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, ")"); break; case '&': case '|': case '^': case 'A': case 'N': case 'X': case '!': fprintf(vlog_out, "(%s", oper); emit_expr(scope, oper1, 0, 0, 0, 0); fprintf(vlog_out, ")"); break; case '2': case 'v': case 'r': /* A cast is a noop. */ emit_expr(scope, oper1, 0, 0, 0, 0); break; case 'I': fprintf(vlog_out, "(++"); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, ")"); fprintf(stderr, "%s:%u: vlog95 sorry: Pre-increment " "operator is not currently translated.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case 'i': fprintf(vlog_out, "("); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, "++)"); fprintf(stderr, "%s:%u: vlog95 sorry: Post-increment " "operator is not currently translated.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case 'D': fprintf(vlog_out, "(--"); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, ")"); fprintf(stderr, "%s:%u: vlog95 sorry: Pre-decrement " "operator is not currently translated.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case 'd': fprintf(vlog_out, "("); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, "--)"); fprintf(stderr, "%s:%u: vlog95 sorry: Post-decrement " "operator is not currently translated.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; /* Convert the Verilog-A abs() function. */ case 'm': if (ivl_expr_value(expr) == IVL_VT_REAL) { /* For a real expression use the $abs() function. */ fprintf(vlog_out, "$abs("); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, ")"); } else { /* This only works when the argument has no side effect. */ fprintf(vlog_out, "(("); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, ") > 0 ? ("); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, ") : -("); emit_expr(scope, oper1, wid, 0, 0, is_full_prec); fprintf(vlog_out, "))"); } break; default: fprintf(vlog_out, ""); emit_expr(scope, oper1, wid, 0, 0, 0); fprintf(stderr, "%s:%u: vlog95 error: Unknown unary " "operator (%c).\n", ivl_expr_file(expr), ivl_expr_lineno(expr), ivl_expr_opcode(expr)); vlog_errors += 1; break; } } void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, unsigned is_lval_width, unsigned can_skip_unsigned, unsigned is_full_prec) { expr_sign_t sign_type; /* If the width is from an L-value (assignment) then the actual * expression width can be larger. */ if (is_lval_width) { unsigned expr_wid = ivl_expr_width(expr); if (wid < expr_wid) wid = expr_wid; } /* In a self-determined context the expression set the width. */ if (! wid) wid = ivl_expr_width(expr); sign_type = expr_get_sign_type(expr, wid, can_skip_unsigned, is_full_prec); /* Check to see if a $signed() or $unsigned() needs to be emitted * before the expression. */ if (sign_type == NEED_SIGNED) { fprintf(vlog_out, "$signed("); if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: $signed() is not " "supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } /* A $signed() creates a self-determined context. */ wid = 0; /* It also clears the full precision flag. */ is_full_prec = 0; } if (sign_type == NEED_UNSIGNED) { fprintf(vlog_out, "$unsigned("); if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: $unsigned() is not " "supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } /* A $unsigned() creates a self-determined context. */ wid = 0; /* It also clears the full precision flag. */ is_full_prec = 0; } /* Emit the expression. */ switch (ivl_expr_type(expr)) { case IVL_EX_ARRAY: emit_expr_array(scope, expr, wid); break; case IVL_EX_ARRAY_PATTERN: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Array pattern expressions " "are not supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case IVL_EX_BINARY: emit_expr_binary(scope, expr, wid, is_full_prec); break; case IVL_EX_CONCAT: emit_expr_concat(scope, expr, wid); break; case IVL_EX_DELAY: emit_expr_delay(scope, expr, wid); break; case IVL_EX_ENUMTYPE: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Enum expressions " "are not supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case IVL_EX_EVENT: emit_expr_event(scope, expr, wid); break; case IVL_EX_NEW: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: New operator " "is not supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case IVL_EX_NULL: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Null operator " "is not supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case IVL_EX_NUMBER: emit_expr_number(scope, expr, wid); break; case IVL_EX_PROPERTY: emit_class_property(scope, expr, wid); break; case IVL_EX_REALNUM: emit_expr_real_number(scope, expr, wid); break; case IVL_EX_SCOPE: emit_expr_scope(scope, expr, wid); break; case IVL_EX_SELECT: emit_expr_select(scope, expr, wid); break; case IVL_EX_SFUNC: fprintf(vlog_out, "%s", ivl_expr_name(expr)); emit_expr_func(scope, expr, wid); break; case IVL_EX_SHALLOWCOPY: fprintf(vlog_out, " "); emit_expr(scope, ivl_expr_oper2(expr), wid, 0, 0, 0); fprintf(stderr, "%s:%u: vlog95 error: New operator " "is not supported.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; break; case IVL_EX_SIGNAL: emit_expr_signal(scope, expr, wid); break; case IVL_EX_STRING: emit_string(ivl_expr_string(expr)); break; case IVL_EX_TERNARY: emit_expr_ternary(scope, expr, wid, is_full_prec); break; case IVL_EX_UFUNC: emit_scope_path(scope, ivl_expr_def(expr)); emit_expr_func(scope, expr, wid); break; case IVL_EX_UNARY: emit_expr_unary(scope, expr, wid, is_full_prec); break; default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown expression " "type (%d).\n", ivl_expr_file(expr), ivl_expr_lineno(expr), (int)ivl_expr_type(expr)); vlog_errors += 1; break; } /* Close the $signed() or $unsigned() if need. */ if (sign_type != NO_SIGN) fprintf(vlog_out, ")"); } iverilog-10_1/tgt-vlog95/logic_lpm.c000066400000000000000000002274131265551621300174050ustar00rootroot00000000000000/* * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include "config.h" # include "vlog95_priv.h" /* * Data type used to signify if a $signed or $unsigned should be emitted. */ typedef enum lpm_sign_e { NO_SIGN = 0, NEED_SIGNED = 1, NEED_UNSIGNED = 2 } lpm_sign_t; static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, int*base, unsigned*array_word); /* * Look to see if the nexus driver is signed. */ static int nexus_driver_is_signed(ivl_scope_t scope, ivl_nexus_t nex) { int is_signed = 0; unsigned sign_set = 0; unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); ivl_drive_t cur_drive1 = ivl_nexus_ptr_drive1(nex_ptr); ivl_drive_t cur_drive0 = ivl_nexus_ptr_drive0(nex_ptr); if ((cur_drive1 == IVL_DR_HiZ) && (cur_drive0 == IVL_DR_HiZ)) continue; /* Only one driver can set the sign information. */ assert( ! sign_set); if (t_lpm) { sign_set = 1; is_signed = ivl_lpm_signed(t_lpm); } if (t_net_const) { sign_set = 1; is_signed = ivl_const_signed(t_net_const); } if (t_nlogic) { sign_set = 1; /* A BUFZ is used to drive a local signal so look for the * local signal to get the sign information. */ if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) { unsigned array_word = 0; int base = 0; ivl_signal_t sig; assert(ivl_logic_pins(t_nlogic) == 2); sig = nexus_is_signal(scope, ivl_logic_pin(t_nlogic, 0), &base, &array_word); assert(sig); is_signed = ivl_signal_signed(sig); } /* The rest of the logic type are always unsigned. */ } if (t_sig) { sign_set = 1; is_signed = ivl_signal_signed(t_sig); } } return is_signed; } static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm, unsigned can_skip_unsigned) { lpm_sign_t rtn = NO_SIGN; int opr_sign = 0; int lpm_sign = ivl_lpm_signed(lpm); switch (ivl_lpm_type(lpm)) { case IVL_LPM_SIGN_EXT: opr_sign = nexus_driver_is_signed(ivl_lpm_scope(lpm), ivl_lpm_data(lpm, 0)); break; default: opr_sign = lpm_sign; break; } /* Check to see if a $signed() or $unsigned() is needed. */ if (lpm_sign && ! opr_sign) rtn = NEED_SIGNED; if (! lpm_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; return rtn; } static unsigned emit_drive(ivl_drive_t drive) { switch (drive) { case IVL_DR_HiZ: fprintf(vlog_out, "highz"); break; case IVL_DR_WEAK: fprintf(vlog_out, "weak"); break; case IVL_DR_PULL: fprintf(vlog_out, "pull"); break; case IVL_DR_STRONG: fprintf(vlog_out, "strong"); break; case IVL_DR_SUPPLY: fprintf(vlog_out, "supply"); break; default: return 1; break; } return 0; } /* * If the strength type is 2 then emit both strengths. If it is 1 then only * emit the 1 strength (pullup) and if it is 0 only emit the 0 strength * (pulldown). */ static void emit_strength(ivl_drive_t drive1, ivl_drive_t drive0, unsigned strength_type, const char *type, const char *file, unsigned lineno) { assert(strength_type <= 2); if ((strength_type == 2) && ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG))) { fprintf(vlog_out, " ("); if (emit_drive(drive1)) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " "1 drive (%d)\n", file, lineno, type, (int)drive1); vlog_errors += 1; } fprintf(vlog_out, "1, "); if (emit_drive(drive0)) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " "0 drive (%d)\n", file, lineno, type, (int)drive0); vlog_errors += 1; } fprintf(vlog_out, "0)"); } else if ((strength_type == 1) && (drive1 != IVL_DR_PULL)) { fprintf(vlog_out, " ("); if (emit_drive(drive1)) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " "1 drive (%d)\n", file, lineno, type, (int)drive1); vlog_errors += 1; } fprintf(vlog_out, "1)"); } else if ((strength_type == 0) && (drive0 != IVL_DR_PULL)) { fprintf(vlog_out, " ("); if (emit_drive(drive0)) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " "0 drive (%d)\n", file, lineno, type, (int)drive0); vlog_errors += 1; } fprintf(vlog_out, "0)"); } } static void emit_gate_strength(ivl_net_logic_t nlogic, unsigned strength_type) { emit_strength(ivl_logic_drive1(nlogic), ivl_logic_drive0(nlogic), strength_type, "gate", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); } /* * Look for a single driver behind an LPM that passes strength information * and get the real drive information from it. */ static void get_unique_lpm_drive(ivl_lpm_t lpm, ivl_drive_t *drive1, ivl_drive_t *drive0) { ivl_nexus_t nex = ivl_lpm_data(lpm, 0); unsigned idx, count = ivl_nexus_ptrs(nex); unsigned have_driver = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_drive_t cur_drive1 = ivl_nexus_ptr_drive1(nex_ptr); ivl_drive_t cur_drive0 = ivl_nexus_ptr_drive0(nex_ptr); if ((cur_drive1 == IVL_DR_HiZ) && (cur_drive0 == IVL_DR_HiZ)) continue; assert(! have_driver); *drive1 = cur_drive1; *drive0 = cur_drive0; have_driver = 1; } /* This should never happen. */ if (! have_driver) { fprintf(stderr, "%s:%u: vlog95 error: Unable to find drive " "information for strength transparent LPM.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } } static void emit_lpm_strength(ivl_lpm_t lpm) { ivl_lpm_type_t type = ivl_lpm_type(lpm); ivl_drive_t drive1 = IVL_DR_STRONG; ivl_drive_t drive0 = IVL_DR_STRONG; /* This LPM object passes strength information so we need to look * for the strength information at the real driver. */ if (type == IVL_LPM_PART_PV) { get_unique_lpm_drive(lpm, &drive1, &drive0); } else { drive1 = ivl_lpm_drive1(lpm); drive0 = ivl_lpm_drive0(lpm); } emit_strength(drive1, drive0, 2, "LPM", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); } static void emit_delay(ivl_scope_t scope, ivl_expr_t rise, ivl_expr_t fall, ivl_expr_t decay, unsigned dly_count) { assert((dly_count >= 2) && (dly_count <= 3)); /* No delays. */ if (! rise) { assert(! fall); assert(! decay); return; } /* If all three delays match then we only have a single delay. */ if ((rise == fall) && (rise == decay)) { fprintf(vlog_out, " #("); emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ")"); return; } /* If we have a gate that only supports two delays then print them. */ if (dly_count == 2) { fprintf(vlog_out, " #("); emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ", "); emit_scaled_delayx(scope, fall, 0); fprintf(vlog_out, ")"); return; } /* What's left is a gate that supports three delays. */ fprintf(vlog_out, " #("); emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ", "); emit_scaled_delayx(scope, fall, 0); if (decay) { fprintf(vlog_out, ", "); emit_scaled_delayx(scope, decay, 0); } fprintf(vlog_out, ")"); } static unsigned is_local_nexus(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count = ivl_nexus_ptrs(nex); unsigned is_local = 1; unsigned has_output_driver = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); if (! sig) continue; /* Check to see if there is an output port driving into * the local scope. */ if ((scope == ivl_scope_parent(ivl_signal_scope(sig))) && (ivl_signal_port(sig) == IVL_SIP_OUTPUT)) { has_output_driver = 1; continue; } if (scope != ivl_signal_scope(sig)) continue; if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) continue; if (ivl_signal_local(sig)) { is_local = 1; } else { is_local = 0; break; } } /* We return is_local=true only if there is not an output driving * into this scope. This is needed since some module outputs are * combined with a concatenation. */ return is_local && !has_output_driver; } /* * This returns the nexus of the LPM if it is not a local signal. */ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm) { ivl_nexus_t output = 0; switch (ivl_lpm_type(lpm)) { case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_DIVIDE: case IVL_LPM_FF: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_MUX: case IVL_LPM_PART_PV: case IVL_LPM_PART_VP: case IVL_LPM_POW: case IVL_LPM_RE_AND: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_XNOR: case IVL_LPM_REPEAT: case IVL_LPM_SFUNC: case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: case IVL_LPM_SIGN_EXT: case IVL_LPM_SUB: case IVL_LPM_SUBSTITUTE: case IVL_LPM_UFUNC: /* If the output of this LPM is a local signal then something * else will request that this be emitted. */ output = ivl_lpm_q(lpm); if (is_local_nexus(scope, output)) return 0; break; default: // HERE: Can this be a simple assert at some point in time? fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown LPM type (%d).\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm), (int)ivl_lpm_type(lpm)); vlog_errors += 1; return 0; } return output; } static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic); static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend); /* For an undriven port look for the local signal to get the nexus name. */ static void emit_nexus_port_signal(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count = ivl_nexus_ptrs(nex); ivl_signal_t sig = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) assert(0); if (t_sig) { if (scope != ivl_signal_scope(t_sig)) continue; assert(! sig); sig = t_sig; } } /* There will not be a signal for an empty port. */ if (sig) emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0), 0, 0); else fprintf(vlog_out, "/* Empty */"); } static ivl_signal_t find_local_signal(ivl_scope_t scope, ivl_nexus_t nex, unsigned *word) { unsigned idx, count = ivl_nexus_ptrs(nex); ivl_signal_t sig = 0; *word = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (!t_sig) continue; if (ivl_signal_local(t_sig) && (ivl_signal_port(t_sig) != IVL_SIP_INPUT)) continue; if (ivl_signal_scope(t_sig) != scope) continue; assert(! sig); sig = t_sig; *word = ivl_nexus_ptr_pin(nex_ptr); } return sig; } /* * Emit the input port driving expression. */ void emit_nexus_port_driver_as_ca(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count = ivl_nexus_ptrs(nex); ivl_lpm_t lpm = 0; ivl_net_const_t net_const = 0; ivl_net_logic_t nlogic = 0; ivl_signal_t sig = 0; unsigned word = 0; /* Look for the nexus driver. */ for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) continue; if (t_lpm) { assert(! lpm); lpm = t_lpm; } if (t_net_const) { assert(! net_const); net_const = t_net_const; } if (t_nlogic) { assert(! nlogic); nlogic = t_nlogic; } if (t_sig) { assert(! sig); sig = t_sig; word = ivl_nexus_ptr_pin(nex_ptr); } } /* An LPM is driving the nexus. */ if (lpm) { assert(! net_const); assert(! nlogic); assert(! sig); /* If there is a signal in this scope that is also driven by * the LPM then use the signal instead. */ sig = find_local_signal(scope, ivl_lpm_q(lpm), &word); if (sig) emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0, 0); else emit_lpm_as_ca(scope, lpm, 0); /* A constant is driving the nexus. */ } else if (net_const) { assert( !nlogic); assert(! sig); /* If there is a signal in this scope that is also driven by * the constant then use the signal instead. */ sig = find_local_signal(scope, ivl_const_nex(net_const), &word); if (sig) emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0, 0); else emit_const_nexus(scope, net_const); /* A logic gate is driving the nexus. */ } else if (nlogic) { assert(! sig); /* If there is a signal in this scope that is also driven by * the logic then use the signal instead. */ sig = find_local_signal(scope, ivl_logic_pin(nlogic, 0), &word); if (sig) emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0, 0); else emit_logic_as_ca(scope, nlogic); /* A signal is driving the nexus. */ } else if (sig) { emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0, 0); /* If there is no driver then look for a single signal that is * driven by this nexus that has the correct scope. This is needed * to translate top level ports. */ } else { emit_nexus_port_signal(scope, nex); } } void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD, unsigned sign_extend) { /* If there is no nexus then there is nothing to print. */ if (! nex) return; /* A local nexus only has a single driver. */ if (is_local_nexus(scope, nex)) { unsigned idx, count = ivl_nexus_ptrs(nex); unsigned must_be_sig = 0; unsigned out_of_scope_drive = 0; ivl_lpm_t lpm = 0; ivl_net_const_t net_const = 0; ivl_net_logic_t nlogic = 0; ivl_signal_t sig = 0; unsigned word = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) { /* If we only have a single input then we want * the nexus this signal is driven by. */ if (count == 1) { must_be_sig = 1; } else continue; } if (t_lpm) { assert(! lpm); lpm = t_lpm; } if (t_net_const) { if (scope != ivl_const_scope(t_net_const)) { // HERE: Need to verify that this is not a parameter out_of_scope_drive = 1; } assert(! net_const); net_const = t_net_const; } if (t_nlogic) { assert(! nlogic); nlogic = t_nlogic; } if (t_sig) { assert(! sig); sig = t_sig; word = ivl_nexus_ptr_pin(nex_ptr); } } if (lpm) { assert(! net_const); assert(! nlogic); assert(! sig); assert(! must_be_sig); // HERE: I think we need special input code like the following. #if 0 /* If there is a signal in this scope that is also driven by * the LPM then use the signal instead. */ sig = find_local_signal(scope, ivl_lpm_q(lpm), &word); if (sig) emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0, 0); else emit_lpm_as_ca(scope, lpm, sign_extend); #endif emit_lpm_as_ca(scope, lpm, sign_extend); } else if (net_const) { assert( !nlogic); assert(! sig); assert(! must_be_sig); if (out_of_scope_drive) { // HERE: An out of scope const drive that is not a parameter is really a // port so look for and emit the local signal name (nexus_is_signal // may work). The is_local_nexus code also needs to be changed to // not emit the port expressions as a CA. Make sure this works // correctly if the parameter is passed as a port argument. // For now report this as missing. fprintf(vlog_out, ""); } else emit_const_nexus(scope, net_const); } else if (nlogic) { assert(! sig); assert(! must_be_sig); emit_logic_as_ca(scope, nlogic); } else if (sig) { // HERE: should these be allow_UD? if (must_be_sig) { emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0, 0); } else emit_name_of_nexus(scope, nex, 0); // HERE: The assert causes pr1703959 to fail. // } else assert(0); } else { fprintf(stderr, "?:?: vlog95 error: Could not emit " "nexus as a CA.\n"); vlog_errors += 1; fprintf(vlog_out, ""); } } else { emit_name_of_nexus(scope, nex, allow_UD); } } static void emit_pull_const(char value, unsigned width) { unsigned idx; fprintf(vlog_out, "%u'b", width); for (idx = 0; idx < width; idx += 1) { fprintf(vlog_out, "%c", value); } } static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic) { unsigned inputs = ivl_logic_pins(nlogic) - 1; switch (ivl_logic_type(nlogic)) { case IVL_LO_AND: assert(inputs == 2); fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, " & "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LO_BUF: case IVL_LO_BUFT: case IVL_LO_BUFZ: assert(inputs == 1); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); break; case IVL_LO_NAND: assert(inputs == 2); fprintf(vlog_out, "~("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, " & "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LO_NOR: assert(inputs == 2); fprintf(vlog_out, "~("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LO_NOT: assert(inputs == 1); fprintf(vlog_out, "(~ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LO_OR: assert(inputs == 2); fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LO_XNOR: assert(inputs == 2); fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, " ~^ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LO_XOR: assert(inputs == 2); fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, " ^ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); fprintf(vlog_out, ")"); break; /* A pull up/down at this point has been turned into an assignment * with strength so just emit the appropriate constant. */ case IVL_LO_PULLDOWN: emit_pull_const('0', ivl_logic_width(nlogic)); break; case IVL_LO_PULLUP: emit_pull_const('1', ivl_logic_width(nlogic)); break; default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown CA logic type " "(%d).\n", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic), (int)ivl_logic_type(nlogic)); vlog_errors += 1; } } static void emit_lpm_array(ivl_scope_t scope, ivl_lpm_t lpm) { ivl_signal_t sig = ivl_lpm_array(lpm); emit_scope_module_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); fprintf(vlog_out, "["); // HERE: Need to remove the scale to match array base instead of adding it back. emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); fprintf(vlog_out, " + %d]", ivl_signal_array_base(sig)); } static void emit_lpm_concat(ivl_scope_t scope, ivl_lpm_t lpm) { unsigned idx, count= ivl_lpm_size(lpm); ivl_nexus_t nex; fprintf(vlog_out, "{"); // HERE: Need to check for a zero repeat that was dropped from the concat. /* Check to see if this is a repeat. */ nex = ivl_lpm_data(lpm, 0); for (idx = 1; idx < count; idx += 1) { if (nex != ivl_lpm_data(lpm, idx)) break; } /* If all the nexus match then we have a repeat. */ if ((idx == count) && (count > 1)) { fprintf(vlog_out, "%u{", count); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, "}"); /* Icarus uses a concat to combine the output from multiple devices * into a single vector, because of this we need to also look for * the nexus driver outside the scope. emit_nexus_as_ca( , , 1, ) */ } else { for (idx = count-1; idx > 0; idx -= 1) { emit_nexus_as_ca(scope, ivl_lpm_data(lpm, idx), 1, 0); fprintf(vlog_out, ", "); } emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); } fprintf(vlog_out, "}"); } /* * Look for an output signal in the nexus that is driving into this scope. */ static ivl_signal_t find_output_signal(ivl_scope_t scope, ivl_nexus_t nex, unsigned*array_word) { unsigned idx, count = ivl_nexus_ptrs(nex); (void)array_word; /* Parameter is not used. */ for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (! t_sig) continue; /* The signal must not be a driver. */ if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) continue; /* The signal must be an output. */ if (ivl_signal_port(t_sig) != IVL_SIP_OUTPUT) continue; /* The signal must be driving into this scope. */ if (ivl_scope_parent(ivl_signal_scope(t_sig)) == scope) { return t_sig; } } return 0; } static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, int*base, unsigned*array_word) { unsigned idx, count = ivl_nexus_ptrs(nex); ivl_lpm_t lpm = 0; ivl_net_const_t net_const = 0; ivl_net_logic_t nlogic = 0; ivl_signal_t sig; /* Look for a signal in the local scope first. */ sig = find_local_signal(scope, nex, array_word); if (sig) return sig; /* Now look for an output signal driving into the local scope. */ sig = find_output_signal(scope, nex, array_word); if (sig) return sig; /* Now scan the nexus looking for a driver. */ for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) continue; if (t_lpm) { assert(! lpm); /* The real signal could be hidden behind a select. */ if (ivl_lpm_type(t_lpm) == IVL_LPM_PART_VP) { t_sig = nexus_is_signal(scope, ivl_lpm_data(t_lpm, 0), base, array_word); } if (t_sig) *base += ivl_lpm_base(t_lpm); else lpm = t_lpm; } if (t_net_const) { assert(! net_const); net_const = t_net_const; } if (t_nlogic) { assert(! nlogic); /* The real signal could be hidden behind a BUFZ gate. */ if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) { assert(ivl_logic_pins(t_nlogic) == 2); t_sig = nexus_is_signal(scope, ivl_logic_pin(t_nlogic, 1), base, array_word); } else nlogic = t_nlogic; } if (t_sig) { assert(! sig); sig = t_sig; /* This could be an array word so save the word. */ *array_word = ivl_nexus_ptr_pin(nex_ptr); } } return sig; } static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend) { unsigned width = ivl_lpm_width(lpm); unsigned array_word = 0; int base = ivl_lpm_base(lpm); int msb, lsb; ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0), &base, &array_word); if (sign_extend && !allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not " "supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } // HERE: variable parameter select needs to be rebuilt. if (! sig) { /* Check if the compiler used a select for a shift. */ assert(base >= 0); if (base) fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); if (base) { fprintf(vlog_out, " "); if (sign_extend) fprintf(vlog_out, ">"); fprintf(vlog_out, ">> %d)", base); } return; } if (sign_extend) fprintf(vlog_out, "("); emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig)) { int array_idx = (int) array_word + ivl_signal_array_base(sig); fprintf(vlog_out, "[%d]", array_idx); } get_sig_msb_lsb(sig, &msb, &lsb); if (sign_extend) { assert(base != lsb); if (msb >= lsb) base += lsb; else base = lsb - base; fprintf(vlog_out, " >>> %d)", base); return; } if (width == 1) { ivl_nexus_t sel = ivl_lpm_data(lpm, 1); if (sel) { fprintf(vlog_out, "["); // HERE: Need to scale the select nexus. if ((msb >= lsb) && (lsb == 0)) { emit_nexus_as_ca(scope, sel, 0, 0); } else { fprintf(stderr, "%s:%u: vlog95 sorry: Non-zero based " "variable part selects are not " "supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; fprintf(vlog_out, ""); } } else { /* Skip a select of the entire bit. */ if ((msb == lsb) && (lsb == base)) return; fprintf(vlog_out, "["); if (msb >= lsb) base += lsb; else base = lsb - base; fprintf(vlog_out, "%d", base); } } else { fprintf(vlog_out, "["); // HERE: No support for an indexed part select. ivl_nexus_t sel = ivl_lpm_data(lpm, 1); if (sel) { fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " "selects are not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; fprintf(vlog_out, ":"); } else { if (msb >= lsb) { base += lsb; fprintf(vlog_out, "%d:%d", base+(int)width-1, base); } else { base = lsb - base; fprintf(vlog_out, "%d:%d", base-(int)width+1, base); } } } fprintf(vlog_out, "]"); } // HERE: No support for trigger. Is this actually needed? static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm) { unsigned count = ivl_lpm_size(lpm); if (count) { unsigned idx; count -= 1; fprintf(vlog_out, "("); for (idx = 0; idx < count; idx += 1) { emit_nexus_as_ca(scope, ivl_lpm_data(lpm, idx), 0, 0); fprintf(vlog_out, ", "); } emit_nexus_as_ca(scope, ivl_lpm_data(lpm, count), 0, 0); fprintf(vlog_out, ")"); } } static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend) { lpm_sign_t sign_type; /* Check to see if a $signed() or $unsigned() needs to be emitted * before the expression. */ sign_type = lpm_get_sign_type(lpm, 0); if (sign_type == NEED_SIGNED) { fprintf(vlog_out, "$signed("); if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: $signed() is not " "supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } } if (sign_type == NEED_UNSIGNED) { fprintf(vlog_out, "$unsigned("); if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: $unsigned() is not " "supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } } switch (ivl_lpm_type(lpm)) { /* Convert the Verilog-A abs() function. This only works when the * argument has no side effect. */ case IVL_LPM_ABS: // HERE: If this is a real net then use the $abs() function to get NaN to // work correctly. See the expr code. fprintf(vlog_out, "(("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ") > "); fprintf(vlog_out, "0 ? ("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ") : -("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, "))"); break; case IVL_LPM_ADD: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " + "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_ARRAY: emit_lpm_array(scope, lpm); break; case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); break; case IVL_LPM_CMP_EEQ: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " === "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_EQ: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " == "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_EQX: // HERE: Need to heck that this is not a real nexus. fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " ==? "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); fprintf(stderr, "%s:%u: vlog95 error: Compare wildcard equal " "operator is not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; break; case IVL_LPM_CMP_EQZ: // HERE: Need to heck that this is not a real nexus. fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " == "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); fprintf(stderr, "%s:%u: vlog95 error: Compare equal Z (caseZ) " "operator is not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; break; case IVL_LPM_CMP_GE: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " >= "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_GT: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " > "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_NE: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " != "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_NEE: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " !== "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; /* A concat-Z should never be generated, but report it as an * error if one is generated. */ case IVL_LPM_CONCATZ: fprintf(stderr, "%s:%u: vlog95 error: Transparent concatenations " "should not be generated.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; case IVL_LPM_CONCAT: emit_lpm_concat(scope, lpm); break; case IVL_LPM_DIVIDE: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " / "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_MOD: // HERE: Need to check if this LPM is IVL_VT_REAL. if (0) { fprintf(stderr, "%s:%u: vlog95 error: Real modulus operator " "is not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " %% "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_MULT: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " * "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_MUX: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); fprintf(vlog_out, " ? "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, " : "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_PART_PV: emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); break; case IVL_LPM_PART_VP: emit_lpm_part_select(scope, lpm, sign_extend); break; case IVL_LPM_POW: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " ** "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); fprintf(stderr, "%s:%u: vlog95 error: Power operator is not " "supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; break; case IVL_LPM_RE_AND: fprintf(vlog_out, "(&"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_RE_NAND: fprintf(vlog_out, "(~&"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_RE_NOR: fprintf(vlog_out, "(~|"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_RE_OR: fprintf(vlog_out, "(|"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_RE_XOR: fprintf(vlog_out, "(^"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_RE_XNOR: fprintf(vlog_out, "(~^"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_REPEAT: fprintf(vlog_out, "{%u{", ivl_lpm_size(lpm)); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, "}}"); break; case IVL_LPM_SFUNC: fprintf(vlog_out, "%s", ivl_lpm_string(lpm)); emit_lpm_func(scope, lpm); break; case IVL_LPM_SHIFTL: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " << "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_SHIFTR: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " "); if (ivl_lpm_signed(lpm)) { if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: >>> operator " "is not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } fprintf(vlog_out, ">"); } fprintf(vlog_out, ">> "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_SIGN_EXT: assert(! sign_extend); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 1); break; case IVL_LPM_SUB: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " - "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; case IVL_LPM_SUBSTITUTE: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 sorry: Substitute LPMs are " "not currently translated.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; break; case IVL_LPM_UFUNC: emit_scope_path(scope, ivl_lpm_define(lpm)); emit_lpm_func(scope, lpm); break; default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown LPM type (%d).\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm), (int)ivl_lpm_type(lpm)); vlog_errors += 1; } /* Close the $signed() or $unsigned() if needed. */ if (sign_type != NO_SIGN) fprintf(vlog_out, ")"); } static void emit_posedge_dff_prim(void) { fprintf(vlog_out, "\n"); fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " "positive edge D-FF. */\n"); fprintf(vlog_out, "primitive IVL_posedge_DFF " "(q, clk, en, d, clr, set);\n"); fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' '); fprintf(vlog_out, "%*cinput clk, en, d, clr, set;\n", indent_incr, ' '); fprintf(vlog_out, "%*creg q;\n", indent_incr, ' '); fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); fprintf(vlog_out, "%*cr 1 0 0 0 : ? : 0 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cr 1 1 0 0 : ? : 1 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cp 1 0 0 0 : 0 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cp 1 1 0 0 : 1 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cp x 0 0 0 : 0 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cp x 1 0 0 : 1 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cn ? ? 0 0 : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c* 0 ? 0 0 : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? * ? ? ? : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? * ? ? : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? * ? : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? ? * : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 0 1 : ? : 1 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 0 x : 1 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 0 x : 0 : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 1 ? : ? : 0 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x 0 : 0 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x 0 : 1 : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x x : ? : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x 1 : ? : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cendtable\n", indent_incr, ' '); fprintf(vlog_out, "endprimitive\n"); } static void emit_negedge_dff_prim(void) { fprintf(vlog_out, "\n"); fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " "negative edge D-FF. */\n"); fprintf(vlog_out, "primitive IVL_negedge_DFF " "(q, clk, en, d, clr, set);\n"); fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' '); fprintf(vlog_out, "%*cinput clk, en, d, clr, set;\n", indent_incr, ' '); fprintf(vlog_out, "%*creg q;\n", indent_incr, ' '); fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); fprintf(vlog_out, "%*cf 1 0 0 0 : ? : 0 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cf 1 1 0 0 : ? : 1 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cn 1 0 0 0 : 0 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cn 1 1 0 0 : 1 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cn x 0 0 0 : 0 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cn x 1 0 0 : 1 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cp ? ? 0 0 : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c* 0 ? 0 0 : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? * ? ? ? : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? * ? ? : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? * ? : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? ? * : ? : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 0 1 : ? : 1 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 0 x : 1 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 0 x : 0 : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? 1 ? : ? : 0 ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x 0 : 0 : - ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x 0 : 1 : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x x : ? : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*c? ? ? x 1 : ? : x ;\n", 2*indent_incr, ' '); fprintf(vlog_out, "%*cendtable\n", indent_incr, ' '); fprintf(vlog_out, "endprimitive\n"); } static unsigned need_posedge_dff_prim = 0; static unsigned need_negedge_dff_prim = 0; /* * Synthesis creates a D-FF LPM object. To allow this to be simulated as * Verilog we need to generate a D-FF UDP that is used to represent this * LPM. Since this must be included with user derived code it must be * licensed using the lesser GPL to avoid the requirement that their code * also be licensed under the GPL. We print a note that LGPL code is * being included in the output so the user can remove it if desired. * * The general idea with all this is that we want the user to be able to * simulate a synthesized D-FF, etc., but we don't want them to take the * ideas behind the primitive(s) and claim them as their own. */ void emit_icarus_generated_udps() { /* Emit the copyright information and LGPL note and then emit any * needed primitives. */ if (need_posedge_dff_prim || need_negedge_dff_prim) { fprintf(vlog_out, "\n" "/*\n" " * This is the copyright information for the following primitive(s)\n" " * (library elements).\n" " *\n" " * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com)\n" " *\n" " * This library is free software; you can redistribute it and/or\n" " * modify it under the terms of the GNU Lesser General Public\n" " * License as published by the Free Software Foundation; either\n" " * version 2.1 of the License, or (at your option) any later version.\n" " *\n" " * This library is distributed in the hope that it will be useful,\n" " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" " * Lesser General Public License for more details.\n" " *\n" " * You should have received a copy of the GNU Lesser General Public\n" " * License along with this library; if not, write to the Free Software\n" " * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" " */\n"); fprintf(stderr, "NOTE: vlog95: Adding LGPL 2.1 primitive(s) at the end of the output file.\n"); } if (need_posedge_dff_prim) emit_posedge_dff_prim(); if (need_negedge_dff_prim) emit_negedge_dff_prim(); } static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) { unsigned negedge = ivl_lpm_negedge(lpm); ivl_expr_t aset_expr = ivl_lpm_aset_value(lpm); ivl_expr_t sset_expr = ivl_lpm_sset_value(lpm); ivl_nexus_t nex; unsigned emitted, have_data, have_sset; const char *aset_bits = 0; const char *sset_bits = 0; /* For now we only support a width of 1 for these bits. */ if (aset_expr) { if (ivl_expr_width(aset_expr) != 1) { fprintf(stderr, "%s:%u: vlog95 sorry: FF LPMs with " "multi-bit asynchronous set values are not " "currently translated.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } aset_bits = ivl_expr_bits(aset_expr); } if (sset_expr) { assert(ivl_expr_width(sset_expr) == 1); sset_bits = ivl_expr_bits(sset_expr); } fprintf(vlog_out, "%*c", indent, ' '); if (negedge) { fprintf(vlog_out, "IVL_negedge_DFF"); } else { fprintf(vlog_out, "IVL_posedge_DFF"); } emit_lpm_strength(lpm); /* The lpm FF does not support any delays. */ /* The FF name is a temporary so we don't bother to print it unless * we have a range. Then we need to use a made up name. */ if (ivl_lpm_width(lpm) > 1) { fprintf(vlog_out, " synth_%p [%u:0]", lpm, ivl_lpm_width(lpm)-1U); } fprintf(vlog_out, " ("); /* Emit the q pin. */ emit_name_of_nexus(scope, ivl_lpm_q(lpm), 0); fprintf(vlog_out, ", "); /* Emit the clock pin. */ emit_nexus_as_ca(scope, ivl_lpm_clk(lpm), 0, 0); fprintf(vlog_out, ", "); /* Emit the enable pin expression(s) if needed. */ emitted = 0; nex = ivl_lpm_enable(lpm); if (nex) { emit_nexus_as_ca(scope, nex, 0, 0); emitted = 1; } nex = ivl_lpm_sync_clr(lpm); if (nex) { if (emitted) fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, nex, 0, 0); emitted = 1; } have_sset = 0; nex = ivl_lpm_sync_set(lpm); if (nex) { if (emitted) fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, nex, 0, 0); emitted = 1; have_sset = 1; } if (!emitted) fprintf(vlog_out, "1'b1"); fprintf(vlog_out, ", "); /* Emit the data pin expression(s). */ have_data = ivl_lpm_data(lpm, 0) != 0; nex = ivl_lpm_sync_clr(lpm); if (nex) { emit_nexus_as_ca(scope, nex, 0, 0); if (have_data | have_sset) fprintf(vlog_out, " & "); if (have_data & have_sset) fprintf(vlog_out, "("); } nex = ivl_lpm_sync_set(lpm); if (nex) { if (! sset_bits || (sset_bits[0] == '1')) { emit_nexus_as_ca(scope, nex, 0, 0); if (have_data) fprintf(vlog_out, " | "); } else { fprintf(vlog_out, "~"); emit_nexus_as_ca(scope, nex, 0, 0); if (have_data) fprintf(vlog_out, " & "); } } nex = ivl_lpm_data(lpm, 0); if (nex) emit_nexus_as_ca(scope, nex, 0, 0); if (have_data & have_sset) fprintf(vlog_out, ")"); fprintf(vlog_out, ", "); /* Emit the clear pin expression(s) if needed. */ emitted = 0; nex = ivl_lpm_async_clr(lpm); if (nex) { emit_nexus_as_ca(scope, nex, 0, 0); emitted = 1; } nex = ivl_lpm_async_set(lpm); if (aset_bits && (aset_bits[0] != '0')) nex = 0; if (nex) { if (emitted) fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, nex, 0, 0); emitted = 1; } if (!emitted) fprintf(vlog_out, "1'b0"); fprintf(vlog_out, ", "); /* Emit the set pin expression(s) if needed. */ nex = ivl_lpm_async_set(lpm); if (aset_bits && (aset_bits[0] != '1')) nex = 0; if (nex) emit_nexus_as_ca(scope, nex, 0, 0); else fprintf(vlog_out, "1'b0"); fprintf(vlog_out, ");\n"); /* We need to emit a primitive for this instance. */ if (negedge) need_negedge_dff_prim = 1; else need_posedge_dff_prim = 1; } static ivl_signal_t get_output_from_nexus(ivl_scope_t scope, ivl_nexus_t nex, int64_t*array_idx) { ivl_signal_t use_sig = 0; unsigned is_array = 0; unsigned idx, count; count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); if (! sig) continue; if (ivl_signal_local(sig)) { /* If the local signal is another receiver skip it. */ if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) { continue; } assert(0); } /* The signal must be in the correct scope. */ if (scope != ivl_signal_scope(sig)) continue; /* Since we are looking for the output signal it is a receiver. */ if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) { continue; } if (use_sig) { // HERE: Which one should we use? For now it's the first one found. // I believe this needs to be solved (see the inout.v test). fprintf(stderr, "%s:%u: vlog95 warning: Duplicate name (%s", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig) > 0) { int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr); tmp_idx += ivl_signal_array_base(sig); fprintf(stderr, "[%"PRId64"]", tmp_idx); } fprintf(stderr, ") found for nexus (%s", ivl_signal_basename(use_sig)); if (is_array) fprintf(stderr, "[%"PRId64"]", *array_idx); fprintf(stderr, ")\n"); } else { /* We have a signal that can be used to find the name. */ use_sig = sig; if (ivl_signal_dimensions(sig) > 0) { is_array = 1; *array_idx = ivl_nexus_ptr_pin(nex_ptr); *array_idx += ivl_signal_array_base(sig); } } } return use_sig; } static void emit_lpm_part_pv(ivl_scope_t scope, ivl_lpm_t lpm) { unsigned width = ivl_lpm_width(lpm); int64_t array_word = 0; int base = ivl_lpm_base(lpm); int msb, lsb; ivl_signal_t sig = get_output_from_nexus(scope, ivl_lpm_q(lpm), &array_word); assert(sig); assert(ivl_lpm_data(lpm, 1) == 0); emit_id(ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig)) { fprintf(vlog_out, "[%"PRId64"]", array_word); } get_sig_msb_lsb(sig, &msb, &lsb); fprintf(vlog_out, "["); if (width == 1) { if (msb >= lsb) base += lsb; else base = lsb - base; fprintf(vlog_out, "%d", base); } else { if (msb >= lsb) { base += lsb; fprintf(vlog_out, "%d:%d", base+(int)width-1, base); } else { base = lsb - base; fprintf(vlog_out, "%d:%d", base-(int)width+1, base); } } fprintf(vlog_out, "]"); } static unsigned output_is_module_instantiation_input(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count = ivl_nexus_ptrs(nex); unsigned rtn = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); /* Skip drivers. */ if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) continue; ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); /* If the nexus is driving other things or signals that are * not a module instantiation input then return false. */ // HERE: debug this to see if the output can drive other things local to the // module that is being called. // if (! t_sig) return 0; if (! t_sig) continue; if (ivl_signal_port(t_sig) != IVL_SIP_INPUT) return 0; if (ivl_scope_parent(ivl_signal_scope(t_sig)) != scope) return 0; if (rtn) return 0; rtn = 1; } return rtn; } void emit_lpm(ivl_scope_t scope, ivl_lpm_t lpm) { ivl_nexus_t output = get_lpm_output(scope, lpm); ivl_lpm_type_t type = ivl_lpm_type(lpm); /* If the output is local then someone else will output this lpm. */ if (! output) return; /* If the LPM is a D-FF then we need to emit it as a UDP. */ if (type == IVL_LPM_FF) { emit_lpm_ff(scope, lpm); return; } // HERE: Look for a select passed to a pull device (pr2019553). /* Skip assignments to a module instantiation input. */ if (output_is_module_instantiation_input(scope, output)) return; fprintf(vlog_out, "%*cassign", indent, ' '); emit_lpm_strength(lpm); emit_delay(scope, ivl_lpm_delay(lpm, 0), ivl_lpm_delay(lpm, 1), ivl_lpm_delay(lpm, 2), 3); fprintf(vlog_out, " "); if (type == IVL_LPM_PART_PV) emit_lpm_part_pv(scope, lpm); else emit_name_of_nexus(scope, output, 0); fprintf(vlog_out, " = "); emit_lpm_as_ca(scope, lpm, 0); fprintf(vlog_out, ";"); if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); } fprintf(vlog_out, "\n"); } static void emit_logic_file_line(ivl_net_logic_t nlogic) { if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); } } /* * A BUFZ is a simple variable assignment possibly with strength and/or delay. */ static void emit_bufz(ivl_scope_t scope, ivl_net_logic_t nlogic) { assert(ivl_logic_pins(nlogic) == 2); fprintf(vlog_out, "assign"); emit_gate_strength(nlogic, 2); emit_delay(scope, ivl_logic_delay(nlogic, 0), ivl_logic_delay(nlogic, 1), ivl_logic_delay(nlogic, 2), 3); fprintf(vlog_out, " "); emit_name_of_nexus(scope, ivl_logic_pin(nlogic, 0), 0); fprintf(vlog_out, " = "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); fprintf(vlog_out, ";"); emit_logic_file_line(nlogic); fprintf(vlog_out, "\n"); } static void emit_and_save_udp_name(ivl_net_logic_t nlogic){ ivl_udp_t udp = ivl_logic_udp(nlogic); assert(udp); emit_id(ivl_udp_name(udp)); add_udp_to_list(udp); } static void emit_name_of_logic_nexus(ivl_scope_t scope, ivl_net_logic_t nlogic, ivl_nexus_t nex) { if (nex) { emit_name_of_nexus(scope, nex, 0); } else { if (ivl_logic_type(nlogic) != IVL_LO_UDP) { fprintf(stderr, "%s:%u: vlog95 warning: Missing logic pin " "for (%d) named: %s.\n", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic), ivl_logic_type(nlogic), ivl_logic_basename(nlogic)); } fprintf(vlog_out, "1'bz"); } } void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) { // HERE: We need to investigate if this is really the base of a CA. A real // gate only allows a signal or a signal bit select for the output(s) // and a scalar expression for the input. We also need to modify the // compiler to support logical 'and' and logical 'or' since they // short circuit. Verify input count. unsigned idx, count, dly_count, strength_type = 2; unsigned outputs = 1; const char *name; /* Skip gates that have a local nexus as the output since they are * part of a continuous assignment. */ if (is_local_nexus(scope, ivl_logic_pin(nlogic, 0))) return; fprintf(vlog_out, "%*c", indent, ' '); /* Check to see if this logical should really be emitted as/was * generated from a continuous assignment. */ if (ivl_logic_is_cassign(nlogic)) { unsigned pin_count = 2; if (ivl_logic_type(nlogic) != IVL_LO_NOT) pin_count += 1; assert(ivl_logic_pins(nlogic) == pin_count); fprintf(vlog_out, "assign"); emit_gate_strength(nlogic, strength_type); emit_delay(scope, ivl_logic_delay(nlogic, 0), ivl_logic_delay(nlogic, 1), ivl_logic_delay(nlogic, 2), 3); fprintf(vlog_out, " "); emit_name_of_nexus(scope, ivl_logic_pin(nlogic, 0), 0); fprintf(vlog_out, " = "); emit_logic_as_ca(scope, nlogic); fprintf(vlog_out, ";"); emit_logic_file_line(nlogic); fprintf(vlog_out, "\n"); return; } switch (ivl_logic_type(nlogic)) { case IVL_LO_AND: fprintf(vlog_out, "and"); dly_count = 2; break; case IVL_LO_BUF: fprintf(vlog_out, "buf"); dly_count = 2; outputs = 0; break; case IVL_LO_BUFIF0: fprintf(vlog_out, "bufif0"); dly_count = 3; break; case IVL_LO_BUFIF1: fprintf(vlog_out, "bufif1"); dly_count = 3; break; // case IVL_LO_BUFT: case IVL_LO_BUFZ: emit_bufz(scope, nlogic); return; case IVL_LO_CMOS: fprintf(vlog_out, "cmos"); dly_count = 3; break; case IVL_LO_NAND: fprintf(vlog_out, "nand"); dly_count = 2; break; case IVL_LO_NMOS: fprintf(vlog_out, "nmos"); dly_count = 3; break; case IVL_LO_NOR: fprintf(vlog_out, "nor"); dly_count = 2; break; case IVL_LO_NOT: fprintf(vlog_out, "not"); dly_count = 2; outputs = 0; break; case IVL_LO_NOTIF0: fprintf(vlog_out, "notif0"); dly_count = 3; break; case IVL_LO_NOTIF1: fprintf(vlog_out, "notif1"); dly_count = 3; break; case IVL_LO_OR: fprintf(vlog_out, "or"); dly_count = 2; break; case IVL_LO_PMOS: fprintf(vlog_out, "pmos"); dly_count = 3; break; case IVL_LO_PULLDOWN: fprintf(vlog_out, "pulldown"); dly_count = 0; outputs = 0; strength_type = 0; break; case IVL_LO_PULLUP: fprintf(vlog_out, "pullup"); dly_count = 0; outputs = 0; strength_type = 1; break; case IVL_LO_RCMOS: fprintf(vlog_out, "rcmos"); dly_count = 3; break; case IVL_LO_RNMOS: fprintf(vlog_out, "rnmos"); dly_count = 3; break; case IVL_LO_RPMOS: fprintf(vlog_out, "rpmos"); dly_count = 3; break; case IVL_LO_UDP: emit_and_save_udp_name(nlogic); dly_count = 2; break; case IVL_LO_XNOR: fprintf(vlog_out, "xnor"); dly_count = 2; break; case IVL_LO_XOR: fprintf(vlog_out, "xor"); dly_count = 2; break; default: fprintf(vlog_out, "("); fprintf(stderr, "%s:%u: vlog95 error: Unsupported logic type " "(%d) named: %s.\n", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic), ivl_logic_type(nlogic), ivl_logic_basename(nlogic)); vlog_errors += 1; dly_count = 0; break; } emit_gate_strength(nlogic, strength_type); if (dly_count) emit_delay(scope, ivl_logic_delay(nlogic, 0), ivl_logic_delay(nlogic, 1), ivl_logic_delay(nlogic, 2), dly_count); // HERE: The name has the location information encoded in it. We need to // remove this and rebuild the instance array. For now we just strip // this encoding and create an zero based range. Need to skip the // local names _s. // This can also be an escaped id. name = ivl_logic_basename(nlogic); if (name && *name) { char *fixed_name = strdup(name); unsigned lp = strlen(name) - 1; unsigned width = ivl_logic_width(nlogic); if (fixed_name[lp] == '>') { while (fixed_name[lp] != '<') { assert(lp > 0); lp -= 1; } fixed_name[lp] = 0; } fprintf(vlog_out, " "); emit_id(fixed_name); free(fixed_name); if (width > 1) { fprintf(vlog_out, " [%u:0]", width-1); } } fprintf(vlog_out, " ("); count = ivl_logic_pins(nlogic); count -= 1; if (outputs == 0) outputs = count; for (idx = 0; idx < outputs; idx += 1) { emit_name_of_logic_nexus(scope, nlogic, ivl_logic_pin(nlogic, idx)); fprintf(vlog_out, ", "); } for (/* None */; idx < count; idx += 1) { emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx), 0, 0); fprintf(vlog_out, ", "); } if (strength_type == 2) { emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx), 0, 0); } else { /* A pull gate only has a single output connection. */ assert(count == 0); emit_name_of_logic_nexus(scope, nlogic, ivl_logic_pin(nlogic, idx)); } fprintf(vlog_out, ");"); emit_logic_file_line(nlogic); fprintf(vlog_out, "\n"); } void emit_tran(ivl_scope_t scope, ivl_switch_t tran) { unsigned dly_count, pins; const char *name; fprintf(vlog_out, "%*c", indent, ' '); switch (ivl_switch_type(tran)) { case IVL_SW_RTRAN: fprintf(vlog_out, "rtran"); dly_count = 0; pins = 2; break; case IVL_SW_RTRANIF0: fprintf(vlog_out, "rtranif0"); dly_count = 3; pins = 3; break; case IVL_SW_RTRANIF1: fprintf(vlog_out, "rtranif1"); dly_count = 3; pins = 3; break; case IVL_SW_TRAN: fprintf(vlog_out, "tran"); dly_count = 0; pins = 2; break; case IVL_SW_TRANIF0: fprintf(vlog_out, "tranif0"); dly_count = 3; pins = 3; break; case IVL_SW_TRANIF1: fprintf(vlog_out, "tranif1"); dly_count = 3; pins = 3; break; case IVL_SW_TRAN_VP: default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: No supported for a TRAN_VP " "named: %s.\n", ivl_switch_file(tran), ivl_switch_lineno(tran), ivl_switch_basename(tran)); dly_count = 0; pins = 2; vlog_errors += 1; break; } if (dly_count) emit_delay(scope, ivl_switch_delay(tran, 0), ivl_switch_delay(tran, 1), ivl_switch_delay(tran, 2), dly_count); assert(pins == 2 || pins == 3); // The same problem here as for the gates above. name = ivl_switch_basename(tran); if (name && *name) { char *fixed_name = strdup(name); unsigned lp = strlen(name) - 1; unsigned width = ivl_switch_width(tran); if (fixed_name[lp] == '>') { while (fixed_name[lp] != '<') { assert(lp > 0); lp -= 1; } fixed_name[lp] = 0; } fprintf(vlog_out, " "); emit_id(fixed_name); free(fixed_name); if (width > 1) { fprintf(vlog_out, " [%u:0]", width-1); } } fprintf(vlog_out, " ("); emit_name_of_nexus(scope, ivl_switch_a(tran), 0); fprintf(vlog_out, ", "); emit_name_of_nexus(scope, ivl_switch_b(tran), 0); if (pins == 3) { fprintf(vlog_out, ", "); emit_nexus_as_ca(scope, ivl_switch_enable(tran), 0, 0); } fprintf(vlog_out, ");"); if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_switch_file(tran), ivl_switch_lineno(tran)); } fprintf(vlog_out, "\n"); } void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig) { ivl_nexus_t nex = ivl_signal_nex(sig, 0); unsigned idx, count = ivl_nexus_ptrs(nex); unsigned long emitted = (uintptr_t) ivl_nexus_get_private(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_net_const_t net_const = ivl_nexus_ptr_con(nex_ptr); if (! net_const) continue; if (scope != ivl_const_scope(net_const)) continue; /* Found the constant so emit it if it has not been emitted. */ if (emitted) { --emitted; continue; } fprintf(vlog_out, "%*cassign", indent, ' '); emit_strength(ivl_nexus_ptr_drive1(nex_ptr), ivl_nexus_ptr_drive0(nex_ptr), 2, "assign", ivl_signal_file(sig), ivl_signal_lineno(sig)); emit_delay(scope, ivl_const_delay(net_const, 0), ivl_const_delay(net_const, 1), ivl_const_delay(net_const, 2), 3); fprintf(vlog_out, " "); emit_id(ivl_signal_basename(sig)); fprintf(vlog_out, " = "); emit_const_nexus(scope, net_const); fprintf(vlog_out, ";"); emit_sig_file_line(sig); fprintf(vlog_out, "\n"); /* Increment the emitted constant count by one. */ ivl_nexus_set_private(nex, (void *) ((uintptr_t) ivl_nexus_get_private(nex) + 1U)); return; } /* We must find the constant in the nexus. */ assert(0); } static void dump_drive(ivl_drive_t drive) { switch (drive) { case IVL_DR_HiZ: fprintf(stderr, "highz"); break; case IVL_DR_SMALL: fprintf(stderr, "small"); break; case IVL_DR_MEDIUM: fprintf(stderr, "medium"); break; case IVL_DR_WEAK: fprintf(stderr, "weak"); break; case IVL_DR_LARGE: fprintf(stderr, "large"); break; case IVL_DR_PULL: fprintf(stderr, "pull"); break; case IVL_DR_STRONG: fprintf(stderr, "strong"); break; case IVL_DR_SUPPLY: fprintf(stderr, "supply"); break; } } /* * Routine to dump the nexus information. */ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count; if ((scope == 0) || (nex == 0)) return; count = ivl_nexus_ptrs(nex); fprintf(stderr, "Dumping nexus %p from scope: %s\n", nex, ivl_scope_name(scope)); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_lpm_t lpm = ivl_nexus_ptr_lpm(nex_ptr); ivl_net_const_t net_const = ivl_nexus_ptr_con(nex_ptr); ivl_net_logic_t nlogic = ivl_nexus_ptr_log(nex_ptr); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); fprintf(stderr, " %u (", idx); dump_drive(ivl_nexus_ptr_drive1(nex_ptr)); fprintf(stderr, "1 ,"); dump_drive(ivl_nexus_ptr_drive0(nex_ptr)); fprintf(stderr, "0) "); if (lpm) { ivl_scope_t lpm_scope = ivl_lpm_scope(lpm); assert(! net_const); assert(! nlogic); assert(! sig); fprintf(stderr, "LPM: "); fprintf(stderr, "{%s:%u} ", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); if (scope != lpm_scope) fprintf(stderr, "(%s) ", ivl_scope_name(lpm_scope)); switch (ivl_lpm_type(lpm)) { case IVL_LPM_ABS: fprintf(stderr, "abs"); break; case IVL_LPM_ADD: fprintf(stderr, "add"); break; case IVL_LPM_ARRAY: fprintf(stderr, "array"); break; case IVL_LPM_CAST_INT: fprintf(stderr, ""); break; case IVL_LPM_CAST_INT2: fprintf(stderr, ""); break; case IVL_LPM_CAST_REAL: fprintf(stderr, ""); break; case IVL_LPM_CONCAT: fprintf(stderr, "concat"); break; case IVL_LPM_CONCATZ: fprintf(stderr, "concatz"); break; case IVL_LPM_CMP_EEQ: fprintf(stderr, "eeq"); break; case IVL_LPM_CMP_EQ: fprintf(stderr, "eq"); break; case IVL_LPM_CMP_GE: fprintf(stderr, "ge"); break; case IVL_LPM_CMP_GT: fprintf(stderr, "gt"); break; case IVL_LPM_CMP_NE: fprintf(stderr, "ne"); break; case IVL_LPM_CMP_NEE: fprintf(stderr, "nee"); break; case IVL_LPM_DIVIDE: fprintf(stderr, "divide"); break; case IVL_LPM_FF: fprintf(stderr, "dff"); break; case IVL_LPM_MOD: fprintf(stderr, "mod"); break; case IVL_LPM_MULT: fprintf(stderr, "mult"); break; case IVL_LPM_MUX: fprintf(stderr, "mux"); break; case IVL_LPM_PART_VP: fprintf(stderr, "part-VP"); fprintf(stderr, "(%u+%u)", ivl_lpm_base(lpm), ivl_lpm_width(lpm)); break; case IVL_LPM_PART_PV: fprintf(stderr, "part-PV"); break; case IVL_LPM_POW: fprintf(stderr, "pow"); break; case IVL_LPM_RE_AND: fprintf(stderr, "R-AND"); break; case IVL_LPM_RE_NAND: fprintf(stderr, "R-NAND"); break; case IVL_LPM_RE_OR: fprintf(stderr, "R-OR"); break; case IVL_LPM_RE_NOR: fprintf(stderr, "R-NOR"); break; case IVL_LPM_RE_XNOR: fprintf(stderr, "R-XNOR"); break; case IVL_LPM_RE_XOR: fprintf(stderr, "R-XOR"); break; case IVL_LPM_REPEAT: fprintf(stderr, "repeat"); break; case IVL_LPM_SFUNC: fprintf(stderr, "S-func"); break; case IVL_LPM_SHIFTL: fprintf(stderr, "shiftl"); break; case IVL_LPM_SHIFTR: fprintf(stderr, "shiftr"); break; case IVL_LPM_SIGN_EXT: fprintf(stderr, "sign"); break; case IVL_LPM_SUB: fprintf(stderr, "sub"); break; case IVL_LPM_UFUNC: fprintf(stderr, "U-func"); break; default: fprintf(stderr, "<%d>", ivl_lpm_type(lpm)); } if (ivl_lpm_signed(lpm)) fprintf(stderr, " "); } else if (net_const) { ivl_scope_t const_scope = ivl_const_scope(net_const); assert(! nlogic); assert(! sig); fprintf(stderr, "Const:"); if (scope != const_scope) { fprintf(stderr, " (%s)", ivl_scope_name(const_scope)); } if (ivl_const_signed(net_const)) fprintf(stderr, " "); } else if (nlogic) { ivl_scope_t logic_scope = ivl_logic_scope(nlogic); ivl_logic_t logic_type = ivl_logic_type(nlogic); assert(! sig); fprintf(stderr, "Logic: "); fprintf(stderr, "{%s:%u} ", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); if (scope != logic_scope) { fprintf(stderr, "(%s) ", ivl_scope_name(logic_scope)); } switch (logic_type) { case IVL_LO_AND: fprintf(stderr, "and"); break; case IVL_LO_BUF: fprintf(stderr, "buf"); break; case IVL_LO_BUFIF0: fprintf(stderr, "bufif0"); break; case IVL_LO_BUFIF1: fprintf(stderr, "bufif1"); break; case IVL_LO_BUFT: fprintf(stderr, "buft"); break; case IVL_LO_BUFZ: fprintf(stderr, "bufz"); break; case IVL_LO_CMOS: fprintf(stderr, "cmos"); break; case IVL_LO_NAND: fprintf(stderr, "nand"); break; case IVL_LO_NMOS: fprintf(stderr, "nmos"); break; case IVL_LO_NOR: fprintf(stderr, "nor"); break; case IVL_LO_NOT: fprintf(stderr, "not"); break; case IVL_LO_NOTIF0: fprintf(stderr, "notif0"); break; case IVL_LO_NOTIF1: fprintf(stderr, "notif1"); break; case IVL_LO_OR: fprintf(stderr, "or"); break; case IVL_LO_PMOS: fprintf(stderr, "pmos"); break; case IVL_LO_PULLDOWN: fprintf(stderr, "pulldown"); break; case IVL_LO_PULLUP: fprintf(stderr, "pullup"); break; case IVL_LO_RCMOS: fprintf(stderr, "rcmos"); break; case IVL_LO_RNMOS: fprintf(stderr, "rnmos"); break; case IVL_LO_RPMOS: fprintf(stderr, "rpmos"); break; case IVL_LO_UDP: { ivl_udp_t udp = ivl_logic_udp(nlogic); assert(udp); fprintf(stderr, "UDP %s", ivl_udp_name(udp)); break; } case IVL_LO_XNOR: fprintf(stderr, "xnor"); break; case IVL_LO_XOR: fprintf(stderr, "xor"); break; default: fprintf(stderr, "<%d>", ivl_logic_type(nlogic)); } /* The BUF and NOT gates can have multiple outputs and a * single input. . */ if ((logic_type == IVL_LO_BUF) || (logic_type == IVL_LO_NOT)) { unsigned outputs = ivl_logic_pins(nlogic) - 1; if (outputs == 1) fprintf(stderr, "(1 output)"); else fprintf(stderr, "(%u outputs)", outputs); /* The rest of the gates have a single output and can * have zero or more inputs. */ } else { unsigned inputs = ivl_logic_pins(nlogic) - 1; if (inputs == 1) fprintf(stderr, "(1 input)"); else fprintf(stderr, "(%u inputs)", inputs); } } else if (sig) { ivl_scope_t sig_scope = ivl_signal_scope(sig); fprintf(stderr, "Signal: \""); if (scope != sig_scope) fprintf(stderr, "%s.", ivl_scope_name(sig_scope)); fprintf(stderr, "%s", ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig) > 0) { fprintf(stderr, "[]"); } fprintf(stderr, "\""); // HERE: Do we need to add support for an array word or is that an LPM. if (ivl_signal_local(sig)) fprintf(stderr, " {local}"); else fprintf(stderr, " {%s:%u}", ivl_signal_file(sig), ivl_signal_lineno(sig)); switch (ivl_signal_port(sig)) { case IVL_SIP_INPUT: fprintf(stderr, " input"); break; case IVL_SIP_OUTPUT: fprintf(stderr, " output"); break; case IVL_SIP_INOUT: fprintf(stderr, " inout"); break; case IVL_SIP_NONE: break; } switch (ivl_signal_type(sig)) { case IVL_SIT_NONE: fprintf(stderr, " "); break; case IVL_SIT_REG: fprintf(stderr, " reg"); break; case IVL_SIT_TRI: fprintf(stderr, " tri"); break; case IVL_SIT_TRI0: fprintf(stderr, " tri0"); break; case IVL_SIT_TRI1: fprintf(stderr, " tri1"); break; case IVL_SIT_TRIAND: fprintf(stderr, " triand"); break; case IVL_SIT_TRIOR: fprintf(stderr, " trior"); break; case IVL_SIT_UWIRE: fprintf(stderr, " uwire"); break; } switch (ivl_signal_data_type(sig)) { case IVL_VT_VOID: fprintf(stderr, " "); break; case IVL_VT_NO_TYPE: fprintf(stderr, " "); break; case IVL_VT_REAL: fprintf(stderr, " real"); break; case IVL_VT_BOOL: fprintf(stderr, " bool"); break; case IVL_VT_LOGIC: fprintf(stderr, " logic"); break; case IVL_VT_STRING: fprintf(stderr, " string"); break; case IVL_VT_DARRAY: fprintf(stderr, " dynamic array"); case IVL_VT_CLASS: fprintf(stderr, " class"); case IVL_VT_QUEUE: fprintf(stderr, " queue"); break; } if (ivl_signal_signed(sig)) fprintf(stderr, " "); } else fprintf(stderr, "Error: No/missing information!"); fprintf(stderr, " (%u)\n", ivl_nexus_ptr_pin(nex_ptr)); } } iverilog-10_1/tgt-vlog95/misc.c000066400000000000000000000766201265551621300163750ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include # include "config.h" # include "vlog95_priv.h" # include "ivl_alloc.h" /* * Emit a constant delay that has been rescaled to the given scopes timescale. */ void emit_scaled_delay(ivl_scope_t scope, uint64_t delay) { int scale = ivl_scope_time_units(scope) - sim_precision; int pre = ivl_scope_time_units(scope) - ivl_scope_time_precision(scope); char *frac; unsigned real_dly = 0; assert(scale >= 0); assert(pre >= 0); assert(scale >= pre); frac = (char *)malloc(pre+1); frac[pre] = 0; for (/* none */; scale > 0; scale -= 1) { if (scale > pre) { assert((delay % 10) == 0); } else { frac[scale-1] = (delay % 10) + '0'; if (frac[scale-1] != '0') { real_dly = 1; } else if (!real_dly) { frac[scale-1] = 0; } } delay /= 10; } if (real_dly) { fprintf(vlog_out, "%"PRIu64".%s", delay, frac); } else { if (delay & 0xffffffff80000000) { fprintf(vlog_out, "(64'd%"PRIu64")", delay); } else { fprintf(vlog_out, "%"PRIu64, delay); } } free(frac); } static void emit_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) { /* A delay in a continuous assignment can also be a continuous * assignment expression. */ if (ivl_expr_type(expr) == IVL_EX_SIGNAL) { ivl_signal_t sig = ivl_expr_signal(expr); if (ivl_signal_local(sig)) { assert(! is_stmt); emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0), 0, 0); return; } } emit_expr(scope, expr, 0, 0, 0, 1); } /* * Check to see if the bit based expression is of the form (expr) * */ static unsigned check_scaled_expr(ivl_expr_t expr, uint64_t scale, const char *msg, unsigned must_match) { uint64_t scale_val; int rtype; if ((ivl_expr_type(expr) != IVL_EX_BINARY) || (ivl_expr_opcode(expr) != '*') || (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) { fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " "cannot be scaled.\n ", ivl_expr_file(expr), ivl_expr_lineno(expr), msg); vlog_errors += 1; return 0; } scale_val = get_uint64_from_number(ivl_expr_oper2(expr), &rtype); if (rtype > 0) { fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " "scale coefficient was greater than 64 bits " "(%d).\n", ivl_expr_file(expr), ivl_expr_lineno(expr), msg, rtype); vlog_errors += 1; return 0; } if (rtype < 0) { fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " "scale coefficient has an undefined bit.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), msg); vlog_errors += 1; return 0; } if (scale != scale_val) { if (must_match) { fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " "scale coefficient did not match expected " "value (%"PRIu64" != %"PRIu64").\n", ivl_expr_file(expr), ivl_expr_lineno(expr), msg, scale, scale_val); vlog_errors += 1; return 0; } return 2; } /* Yes, this expression is of the correct form. */ return 1; } /* * Check to see if the real expression is of the form (expr) * */ static unsigned check_scaled_real_expr(ivl_expr_t expr, double scale) { double scale_val; if ((ivl_expr_type(expr) != IVL_EX_BINARY) || (ivl_expr_opcode(expr) != '*') || (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_REALNUM)) { fprintf(stderr, "%s:%u: vlog95 error: Variable real time unit " " expression/value cannot be scaled.\n ", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return 0; } scale_val = ivl_expr_dvalue(ivl_expr_oper2(expr)); if (scale != scale_val) { fprintf(stderr, "%s:%u: vlog95 error: Variable real time unit " "expression/value scale coefficient did not " "match expected value (%g != %g).\n", ivl_expr_file(expr), ivl_expr_lineno(expr), scale, scale_val); vlog_errors += 1; return 0; } /* Yes, this expression is of the correct form. */ return 1; } /* * Emit a constant or variable delay that has been rescaled to the given * scopes timescale. */ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) { ivl_expr_type_t type = ivl_expr_type(expr); if (type == IVL_EX_DELAY) { emit_scaled_delay(scope, ivl_expr_delay_val(expr)); } else if (type == IVL_EX_NUMBER) { assert(! ivl_expr_signed(expr)); int rtype; uint64_t value = get_uint64_from_number(expr, &rtype); if (rtype > 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Time value is " "greater than 64 bits (%d) and cannot be " "safely represented.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), rtype); vlog_errors += 1; return; } if (rtype < 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Time value has an " "undefined bit and cannot be represented.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return; } emit_scaled_delay(scope, value); } else { int exponent = ivl_scope_time_units(scope) - sim_precision; assert(exponent >= 0); if ((exponent == 0) && (type == IVL_EX_SIGNAL)) { emit_delay(scope, expr, is_stmt); /* A real delay variable is not scaled by the compiler. */ } else if (type == IVL_EX_SIGNAL) { if (is_stmt) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Only continuous " "assignment delay variables are scaled " "at run time.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return; } emit_delay(scope, expr, is_stmt); } else { uint64_t iscale = 1; unsigned rtn; assert(! ivl_expr_signed(expr)); /* Calculate the integer time scaling coefficient. */ while (exponent > 0) { iscale *= 10; exponent -= 1; } /* Check to see if this is an integer time value. */ rtn = check_scaled_expr(expr, iscale, "Variable time", 0); /* This may be a scaled real value. */ if (rtn == 2){ ivl_expr_t tmp_expr; uint64_t rprec = 1; /* This could be a scaled real time so calculate * the real time scaling coefficients and check * that the expression matches (statements only). */ exponent = ivl_scope_time_precision(scope) - sim_precision; assert(exponent >= 0); while (exponent > 0) { rprec *= 10; exponent -= 1; } /* Verify that the precision scaling is correct. */ if (! check_scaled_expr(expr, rprec, "Variable real time prec.", 1)) { fprintf(vlog_out, ""); return; } /* Verify that the left operator is a real to * integer cast. */ tmp_expr = ivl_expr_oper1(expr); if ((ivl_expr_type(tmp_expr) != IVL_EX_UNARY) || (ivl_expr_opcode(tmp_expr) != 'v')) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Real time " "value does not have a cast to " "integer.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return; } /* Check that the cast value is scaled correctly. */ assert(iscale >= rprec); tmp_expr = ivl_expr_oper1(tmp_expr); assert(ivl_expr_value(tmp_expr) == IVL_VT_REAL); if (! check_scaled_real_expr(tmp_expr, iscale/rprec)) { fprintf(vlog_out, ""); return; } assert(is_stmt); emit_delay(scope, ivl_expr_oper1(tmp_expr), is_stmt); return; } else if (rtn == 1) { emit_delay(scope, ivl_expr_oper1(expr), is_stmt); return; } fprintf(vlog_out, ""); } } } static int64_t get_valid_int64_from_number(ivl_expr_t expr, int *rtype, const char *msg) { int64_t value = get_int64_from_number(expr, rtype); if (*rtype > 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Scaled %s is greater than " "64 bits (%d) and cannot be safely represented.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), msg, *rtype); vlog_errors += 1; } else if (*rtype < 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Scaled %s has an undefined " "bit and cannot be represented.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), msg); vlog_errors += 1; } return value; } // HERE: Probably need to pass in a msg string to make this work with // indexed part selects. static unsigned is_scaled_expr(ivl_expr_t expr, int msb, int lsb) { int64_t scale_val; int rtype; /* This is as easy as removing the addition/subtraction that was * added to scale the value to be zero based, but we need to verify * that the scaling value is correct first. */ if (msb > lsb) { if ((ivl_expr_type(expr) != IVL_EX_BINARY) || ((ivl_expr_opcode(expr) != '+') && (ivl_expr_opcode(expr) != '-')) || (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Scaled " "expression value cannot be scaled.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return 0; } scale_val = get_valid_int64_from_number( ivl_expr_oper2(expr), &rtype, "expression value scale coefficient"); } else { if ((ivl_expr_type(expr) != IVL_EX_BINARY) || ((ivl_expr_opcode(expr) != '+') && (ivl_expr_opcode(expr) != '-')) || (ivl_expr_type(ivl_expr_oper1(expr)) != IVL_EX_NUMBER)) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Scaled " "expression value cannot be scaled.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return 0; } scale_val = get_valid_int64_from_number( ivl_expr_oper1(expr), &rtype, "expression value scale coefficient"); } if (rtype) return 0; if (ivl_expr_opcode(expr) == '+') scale_val *= -1; if (lsb != scale_val) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Scaled expression value " "scaling coefficient did not match expected " "value (%d != %"PRIu64").\n", ivl_expr_file(expr), ivl_expr_lineno(expr), lsb, scale_val); vlog_errors += 1; return 0; } return 1; } static int64_t get_in_range_int64_from_number(ivl_expr_t expr, int *rtype, const char *msg) { int64_t value = get_int64_from_number(expr, rtype); if (*rtype > 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Scaled %s is greater than " "64 bits (%d) and cannot be safely represented.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), msg, *rtype); vlog_errors += 1; } return value; } void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, unsigned width, int msb, int lsb) { int rtype; int64_t value = get_in_range_int64_from_number(expr, &rtype, "range value"); (void)scope; /* Parameter is not used. */ if (rtype < 0) fprintf(vlog_out, "[1'bx:1'bx]"); if (rtype) return; if (msb >= lsb) { value += lsb; fprintf(vlog_out, "[%"PRId64":%"PRId64"]", value + (int64_t)(width - 1), value); } else { value = (int64_t)lsb - value; fprintf(vlog_out, "[%"PRId64":%"PRId64"]", value - (int64_t)(width - 1), value); } } void emit_scaled_expr(ivl_scope_t scope, ivl_expr_t expr, int msb, int lsb) { if (msb >= lsb) { if (ivl_expr_type(expr) == IVL_EX_NUMBER) { int rtype; int64_t value = get_in_range_int64_from_number(expr, &rtype, "value"); if (rtype < 0) fprintf(vlog_out, "1'bx"); if (rtype) return; value += lsb; fprintf(vlog_out, "%"PRId64, value); } else if (lsb == 0) { /* If the LSB is zero then there is no scale. */ emit_expr(scope, expr, 0, 0, 0, 1); } else { if (is_scaled_expr(expr, msb, lsb)) { emit_expr(scope, ivl_expr_oper1(expr), 0, 0, 0, 1); } } } else { if (ivl_expr_type(expr) == IVL_EX_NUMBER) { int rtype; int64_t value = get_in_range_int64_from_number(expr, &rtype, "value"); if (rtype < 0) fprintf(vlog_out, "1'bx"); if (rtype) return; value = (int64_t)lsb - value; fprintf(vlog_out, "%"PRId64, value); } else { if (is_scaled_expr(expr, msb, lsb)) { emit_expr(scope, ivl_expr_oper2(expr), 0, 0, 0, 1); } } } } static unsigned find_signal_in_nexus(ivl_scope_t scope, ivl_nexus_t nex) { ivl_signal_t use_sig = 0; unsigned is_driver = 0; unsigned is_array = 0; int64_t array_idx = 0; unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); if (! sig) continue; if (ivl_signal_local(sig)) { /* If the local signal is another receiver skip it. */ if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) { continue; } assert(0); } /* We have a signal that can be used to find the name. */ if (scope == ivl_signal_scope(sig)) { if (use_sig) { /* Swap a receiver for a driver. */ if (is_driver && (ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) { use_sig = sig; is_driver = 0; if (ivl_signal_dimensions(sig) > 0) { is_array = 1; array_idx = ivl_nexus_ptr_pin(nex_ptr); array_idx += ivl_signal_array_base(sig); } continue; } // HERE: Which one should we use? For now it's the first one found. // I believe this needs to be solved (see the inout.v test). fprintf(stderr, "%s:%u: vlog95 warning: Duplicate " "name (%s", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig) > 0) { int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr); tmp_idx += ivl_signal_array_base(sig); fprintf(stderr, "[%"PRId64"]", tmp_idx); } fprintf(stderr, ") found for nexus (%s", ivl_signal_basename(use_sig)); if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx); fprintf(stderr, ")\n"); } else { use_sig = sig; /* This signal is a driver. */ if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) { is_driver = 1; } if (ivl_signal_dimensions(sig) > 0) { is_array = 1; array_idx = ivl_nexus_ptr_pin(nex_ptr); array_idx += ivl_signal_array_base(sig); } } } } if (use_sig) { emit_id(ivl_signal_basename(use_sig)); if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx); return 1; } return 0; } static void emit_number_as_string(ivl_net_const_t net_const) { const char *bits = ivl_const_bits(net_const); unsigned count = ivl_const_width(net_const); int idx; assert((count % 8) == 0); fprintf(vlog_out, "\""); for (idx = (int)count-1; idx >= 0; idx -= 8) { unsigned bit; char val = 0; for (bit = 0; bit < 8; bit += 1) { val |= (bits[idx-bit] == '1') ? 1 << (7-bit) : 0x00; } /* Skip any NULL bytes. */ if (val == 0) continue; /* Print some values that can be escaped. */ if (val == '"') fprintf(vlog_out, "\\\""); else if (val == '\\') fprintf(vlog_out, "\\\\"); else if (val == '\n') fprintf(vlog_out, "\\n"); else if (val == '\t') fprintf(vlog_out, "\\t"); /* Print the printable characters. */ else if (isprint((int)val)) fprintf(vlog_out, "%c", val); /* Print the non-printable characters as an octal escape. */ else fprintf(vlog_out, "\\%03o", val); } fprintf(vlog_out, "\""); } static unsigned emit_as_input(ivl_scope_t scope, ivl_net_const_t net_const) { ivl_scope_t const_scope = ivl_const_scope(net_const); ivl_scope_t parent = ivl_scope_parent(scope); /* Look to see if the constant scope is a parent of this scope. */ while (parent) { if (parent == const_scope) break; parent = ivl_scope_parent(parent); } /* If the constant scope is a parent then look for an input in * this scope and use that for the name. */ if (parent) { ivl_nexus_t nex = ivl_const_nex(net_const); unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); if (sig && (ivl_signal_port(sig) == IVL_SIP_INPUT)) { emit_id(ivl_signal_basename(sig)); return 1; } } } return 0; } void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t net_const) { ivl_scope_t const_scope = ivl_const_scope(net_const); unsigned idx, count, lineno; const char *file; count = ivl_scope_params(const_scope); file = ivl_const_file(net_const); lineno = ivl_const_lineno(net_const); /* Look to see if the constant matches a parameter in its scope. */ for (idx = 0; idx < count; idx += 1) { ivl_parameter_t par = ivl_scope_param(const_scope, idx); if (lineno != ivl_parameter_lineno(par)) continue; if (strcmp(file, ivl_parameter_file(par)) == 0) { /* Check that the appropriate expression bits match the * original parameter bits. */ // HERE: Verify that the values match and then print the name. // Does this work with out of scope references? Check real parameters. emit_id(ivl_parameter_basename(par)); return; } } /* If the scopes don't match then we assume this is an empty port. */ if (const_scope != scope) { /* This constant could really be from an input port. */ if (emit_as_input(scope, net_const)) return; fprintf(vlog_out, "/* Empty */"); return; } switch (ivl_const_type(net_const)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: emit_number(ivl_const_bits(net_const), ivl_const_width(net_const), ivl_const_signed(net_const), ivl_const_file(net_const), ivl_const_lineno(net_const)); break; case IVL_VT_STRING: emit_number_as_string(net_const); break; case IVL_VT_REAL: emit_real_number(ivl_const_real(net_const)); break; default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown constant type " "(%d).\n", ivl_const_file(net_const), ivl_const_lineno(net_const), (int)ivl_const_type(net_const)); vlog_errors += 1; break; } } static unsigned find_const_nexus(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count; count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_net_const_t net_const = ivl_nexus_ptr_con(nex_ptr); // HERE: Do we need to check for duplicates? if (net_const) { assert(! ivl_nexus_ptr_pin(nex_ptr)); emit_const_nexus(scope, net_const); return 1; } } return 0; } static unsigned find_driving_signal(ivl_scope_t scope, ivl_nexus_t nex) { ivl_signal_t sig = 0; unsigned is_array = 0; int64_t array_idx = 0; unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (! t_sig) continue; if (ivl_signal_local(t_sig)) continue; /* An output can be used if it is driven by this nexus. */ if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ) && (ivl_signal_port(t_sig) != IVL_SIP_OUTPUT)) { continue; } /* We have a signal that can be used to find the name. */ if (sig) { // HERE: Which one should we use? For now it's the first one found. // I believe this needs to be solved (see above). fprintf(stderr, "%s:%u: vlog95 warning: Duplicate name (%s", ivl_signal_file(t_sig), ivl_signal_lineno(t_sig), ivl_signal_basename(t_sig)); if (ivl_signal_dimensions(t_sig) > 0) { int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr); tmp_idx += ivl_signal_array_base(t_sig); fprintf(stderr, "[%"PRId64"]", tmp_idx); } fprintf(stderr, ") found for nexus (%s", ivl_signal_basename(sig)); if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx); fprintf(stderr, ")\n"); } else { sig = t_sig; if (ivl_signal_dimensions(sig) > 0) { is_array = 1; array_idx = ivl_nexus_ptr_pin(nex_ptr); array_idx += ivl_signal_array_base(sig); } } } if (sig) { emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx); return 1; } return 0; } static unsigned is_local_input(ivl_scope_t scope, ivl_nexus_t nex) { ivl_signal_t sig = 0; unsigned idx, count = ivl_nexus_ptrs(nex); (void)scope; /* Parameter is not used. */ for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (! t_sig) continue; if (! ivl_signal_local(t_sig)) continue; if (ivl_signal_port(t_sig) != IVL_SIP_INPUT) continue; assert(! sig); assert(ivl_signal_dimensions(t_sig) == 0); sig = t_sig; } if (sig) { fprintf(vlog_out, "ivlog%s", ivl_signal_basename(sig)); return 1; } return 0; } // HERE: Does this work correctly with an array reference created from @*? void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD) { ivl_scope_t mod_scope; /* First look in the local scope for the nexus name. */ if (find_signal_in_nexus(scope, nex)) return; /* If the signal was not found in the passed scope then look in * the module scope if the passed scope was not the module scope. */ mod_scope = get_module_scope(scope); if (mod_scope != scope) { if (find_signal_in_nexus(mod_scope, nex)) return; } /* Look to see if this is a up/down reference. */ if (allow_UD && find_driving_signal(scope, nex)) return; /* If there is no signals driving this then look for a constant. */ if (find_const_nexus(scope, nex)) return; /* Module inputs that are split (arg[7:4], arg[3:0]) need to use * the local signal names. */ if (is_local_input(scope, nex)) return; // HERE: Need to check arr[var]? Can this be rebuilt? // Then look for down scopes and then any scope. For all this warn if // multiples are found in a given scope. This all needs to be before // the constant code. /* It is possible that the nexus does not have a name. For this * case do not print an actual name. */ fprintf(vlog_out, "/* Empty */"); // dump_nexus_information(scope, nex); } /* * This function traverses the scope tree looking for the enclosing module * scope. When it is found the module scope is returned. As far as this * translation is concerned a package is a special form of a module * definition and a class is also a top level scope. */ ivl_scope_t get_module_scope(ivl_scope_t scope) { while ((ivl_scope_type(scope) != IVL_SCT_MODULE) && (ivl_scope_type(scope) != IVL_SCT_PACKAGE) && (ivl_scope_type(scope) != IVL_SCT_CLASS)) { ivl_scope_t pscope = ivl_scope_parent(scope); assert(pscope); scope = pscope; } return scope; } /* * A package is emitted as a module with a special name. This routine * calculates the name for the package. The returned string must be freed * by the calling routine. */ char * get_package_name(ivl_scope_t scope) { char *package_name; const char *name = ivl_scope_basename(scope); package_name = (char *)malloc(strlen(name)+13); strcpy(package_name, "ivl_package_"); strcat(package_name, name); return package_name; } static void emit_scope_piece(ivl_scope_t scope, ivl_scope_t call_scope) { ivl_scope_t parent = ivl_scope_parent(call_scope); /* If we are not at the top of the scope (parent != 0) and the two * scopes do not match then print the parent scope. */ if ((parent != 0) && (scope != parent)) { emit_scope_piece(scope, parent); } /* If the scope is a package then add the special part of the name. */ if (ivl_scope_type(call_scope) == IVL_SCT_PACKAGE) { char *package_name = get_package_name(call_scope); emit_id(package_name); free(package_name); /* Print the base scope. */ } else emit_id(ivl_scope_basename(call_scope)); fprintf(vlog_out, "."); } /* * This routine emits the appropriate string to call the call_scope from the * given scope. If the module scopes for the two match then do nothing. If * the module scopes are different, but the call_scope begins with the * entire module scope of scope then we can trim the top off the call_scope * (it is a sub-scope of the module that contains scope). Otherwise we need * to print the entire path of call_scope. */ void emit_scope_module_path(ivl_scope_t scope, ivl_scope_t call_scope) { ivl_scope_t mod_scope = get_module_scope(scope); ivl_scope_t call_mod_scope = get_module_scope(call_scope); if (mod_scope == call_mod_scope) return; emit_scope_piece(mod_scope, call_mod_scope); } /* This is the same as emit_scope_module_path() except we need to add down * references for variables, etc. */ void emit_scope_call_path(ivl_scope_t scope, ivl_scope_t call_scope) { ivl_scope_t mod_scope, call_mod_scope; if (scope == call_scope) return; mod_scope = get_module_scope(scope); call_mod_scope = get_module_scope(call_scope); if (mod_scope != call_mod_scope) { emit_scope_piece(mod_scope, call_mod_scope); } else if (scope != call_scope) { ivl_scope_t parent; /* We only emit a scope path if the scope is a parent of the * call scope. */ for (parent = ivl_scope_parent(call_scope); parent != 0; parent = ivl_scope_parent(parent)) { if (parent == scope) { emit_scope_piece(scope, call_scope); return; } } } } static void emit_scope_path_piece(ivl_scope_t scope, ivl_scope_t call_scope) { ivl_scope_t parent = ivl_scope_parent(call_scope); /* If we are not at the top of the scope (parent != 0) and the two * scopes do not match then print the parent scope. */ if ((parent != 0) && (scope != parent)) { emit_scope_path_piece(scope, parent); fprintf(vlog_out, "."); } /* If the scope is a package then add the special part of the name. */ if (ivl_scope_type(call_scope) == IVL_SCT_PACKAGE) { char *package_name = get_package_name(call_scope); emit_id(package_name); free(package_name); /* Print the base scope. */ } else emit_id(ivl_scope_basename(call_scope)); } /* * This routine emits the appropriate string to call the call_scope from the * given scope. If the module scopes for the two match then just return the * base name of the call_scope. If the module scopes are different, but the * call_scope begins with the entire module scope of scope then we can trim * the top off the call_scope (it is a sub-scope of the module that contains * scope). Otherwise we need to print the entire path of call_scope. */ void emit_scope_path(ivl_scope_t scope, ivl_scope_t call_scope) { ivl_scope_t mod_scope, call_mod_scope; /* Check to see if this is a root scope task or function. */ if (ivl_scope_parent(call_scope) == 0) { fprintf(vlog_out, "ivl_root_scope."); mod_scope = 0; call_mod_scope = 0; } else { mod_scope = get_module_scope(scope); call_mod_scope = get_module_scope(call_scope); } if (mod_scope == call_mod_scope) { emit_id(ivl_scope_basename(call_scope)); } else { emit_scope_path_piece(mod_scope, call_scope); } } static unsigned is_escaped(const char *id) { assert(id); /* The first digit must be alpha or '_' to be a normal id. */ if (isalpha((int)id[0]) || id[0] == '_') { unsigned idx; for (idx = 1; id[idx] != '\0'; idx += 1) { if (! (isalnum((int)id[idx]) || id[idx] == '_' || id[idx] == '$')) { return 1; } } /* Any Verilog keyword should also be escaped. */ // HERE: Create a keyword.gperf file to do this check. if ((strcmp(id, "input") == 0) || (strcmp(id, "output") == 0) ) return 1; /* We looked at all the digits, so this is a normal id. */ return 0; } return 1; } void emit_id(const char *id) { if (is_escaped(id)) fprintf(vlog_out, "\\%s ", id); else fprintf(vlog_out, "%s", id); } /* * Get the correct MSB and LSB for a signal. */ void get_sig_msb_lsb(ivl_signal_t sig, int *msb, int *lsb) { switch (ivl_signal_packed_dimensions(sig)) { /* For a scalar we use zero for both the MSB and LSB. */ case 0: *msb = 0; *lsb = 0; break; case 1: /* For a vector we use the real MSB and LSB. */ *msb = ivl_signal_packed_msb(sig, 0); *lsb = ivl_signal_packed_lsb(sig, 0); break; /* For a packed vector we use the normalized MSB and LSB. */ default: *msb = ivl_signal_width(sig) - 1; *lsb = 0; break; } } iverilog-10_1/tgt-vlog95/numbers.c000066400000000000000000000306571265551621300171150ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include # include "config.h" # include "vlog95_priv.h" /* * Extract an int32_t value from the given bit information. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. -2 is all bits z and -3 is all bits x. */ static int32_t get_int32_from_bits(const char *bits, unsigned nbits, unsigned is_signed, int *result_type) { unsigned trim_wid = nbits - 1; const char msb = is_signed ? bits[trim_wid] : '0'; unsigned idx; int32_t value = 0; /* Trim any duplicate bits from the MSB. */ for (/* none */; trim_wid > 0; trim_wid -= 1) { if (msb != bits[trim_wid]) { trim_wid += 1; break; } } if (trim_wid < nbits) trim_wid += 1; /* Check to see if the value is too large. */ if (trim_wid > 32U) { *result_type = trim_wid; return 0; } /* Now build the value from the bits. */ for (idx = 0; idx < trim_wid; idx += 1) { if (bits[idx] == '1') value |= (int32_t)1 << idx; else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } return 0; } } /* Sign extend as needed. */ // HERE: Need to emit 1 instead of -1 for some of the constants. // if (is_signed && (nbits > 1) && (msb == '1') && (trim_wid < 32U)) { if (is_signed && (msb == '1') && (trim_wid < 32U)) { value |= ~(((int32_t)1 << trim_wid) - (int32_t)1); } *result_type = 0; return value; } /* Emit the given bits as either a signed or unsigned constant. If the * bits contain an undefined value then emit them as a binary constant * otherwise emit them as a hex constant. */ static void emit_bits(const char *bits, unsigned nbits, unsigned is_signed) { unsigned has_undef = 0; assert(nbits > 0); /* Check for an undefined bit. */ for (int idx = (int)nbits-1; idx >= 0; idx -= 1) { if ((bits[idx] != '0') && (bits[idx] != '1')) { has_undef = 1; break; } } fprintf(vlog_out, "%u'", nbits); if (is_signed) fprintf(vlog_out, "s"); /* Emit as a binary constant. */ if (has_undef || (nbits < 2)) { int start = nbits - 1; char sbit = bits[start]; /* Trim extra leading bits. */ if (! is_signed && (sbit == '1')) sbit = ' '; while (start && (sbit == bits[start-1])) start -= 1; /* Print the trimmed value. */ fprintf(vlog_out, "b"); for (int idx = start; idx >= 0; idx -= 1) { fprintf(vlog_out, "%c", bits[idx]); } /* Emit as a hex constant. */ } else { unsigned start = 4*(nbits/4); unsigned result = 0; fprintf(vlog_out, "h"); /* The first digit may not be a full hex digit. */ if (start < nbits) { for (unsigned idx = start; idx < nbits; idx += 1) { if (bits[idx] == '1') result |= 1U << (idx%4); } fprintf(vlog_out, "%1x", result); } /* Now print the full hex digits. */ for (int idx = start-1; idx >= 0; idx -= 4) { result = 0; if (bits[idx] == '1') result |= 0x8; if (bits[idx-1] == '1') result |= 0x4; if (bits[idx-2] == '1') result |= 0x2; if (bits[idx-3] == '1') result |= 0x1; fprintf(vlog_out, "%1x", result); } } } void emit_number(const char *bits, unsigned nbits, unsigned is_signed, const char *file, unsigned lineno) { /* If the user is allowing signed constructs then we can emit a * signed number as a normal integer or with the 's syntax if * an integer is not appropriate. */ if (is_signed && allow_signed) { int rtype; int32_t value = get_int32_from_bits(bits, nbits, 1, &rtype); if (rtype != 0) emit_bits(bits, nbits, is_signed); else fprintf(vlog_out, "%"PRId32, value); /* Otherwise a signed value can only be 32 bits long since it can * only be represented as an integer. We can trim any matching MSB * bits to make it fit. We cannot support individual undefined * bits in the constant. */ } else if (is_signed) { int rtype; int32_t value = get_int32_from_bits(bits, nbits, 1, &rtype); if (rtype > 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Signed number is " "greater than 32 bits (%d) and cannot be " "safely represented.\n", file, lineno, rtype); vlog_errors += 1; } else if (rtype == -1) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Signed number has " "an undefined bit and cannot be " "represented.\n", file, lineno); vlog_errors += 1; return; } else if (rtype == -2) { fprintf(vlog_out, "%u'bz", nbits); } else if (rtype == -3) { /* If this is a 32-bit wide constant then generate the * undefined with integers to get a signed value. */ if (nbits == 32) fprintf(vlog_out, "1/0"); else fprintf(vlog_out, "%u'bx", nbits); } else { fprintf(vlog_out, "%"PRId32, value); } /* An unsigned number is represented in hex if all the bits are * defined and it is more than a single bit otherwise it is * represented in binary form to preserve all the information. */ } else { emit_bits(bits, nbits, is_signed); } } void emit_real_number(double value) { /* Check for NaN. */ if (isnan(value)) { fprintf(vlog_out, "(0.0/0.0)"); return; } /* Check for the infinities. */ if (isinf(value)) { if (signbit(value)) fprintf(vlog_out, "(-1.0/0.0)"); else fprintf(vlog_out, "(1.0/0.0)"); return; } /* Check for +/- zero. */ if (value == 0.0) { if (signbit(value)) fprintf(vlog_out, "-0.0"); else fprintf(vlog_out, "0.0"); } else { char buffer[32]; char *cptr; unsigned len; /* Print the double to a temporary string using an extra digit. */ buffer[sizeof(buffer)-1] = 0; snprintf(buffer, sizeof(buffer), "%#.17g", value); assert(buffer[sizeof(buffer)-1] == 0); /* Check to see if there is a digit after the decimal point and * add a digit if it is missing. */ len = strlen(buffer); if (buffer[len-1] == '.') { assert((len + 1) < sizeof(buffer)); buffer[len] = '0'; len += 1; buffer[len] = 0; } /* Now trim any extra trailing zero digits. */ cptr = buffer + len - 1; while ((*cptr == '0') && (*(cptr-1) != '.')) cptr -= 1; *(cptr+1) = 0; /* Now print the processed output. */ fprintf(vlog_out, "%s", buffer); } } /* * Extract an uint64_t value from the given number expression. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. */ uint64_t get_uint64_from_number(ivl_expr_t expr, int *result_type) { unsigned nbits = ivl_expr_width(expr); unsigned trim_wid = nbits - 1; const char *bits = ivl_expr_bits(expr); unsigned idx; uint64_t value = 0; assert(ivl_expr_type(expr) == IVL_EX_NUMBER); assert(! ivl_expr_signed(expr)); /* Trim any '0' bits from the MSB. */ for (/* none */; trim_wid > 0; trim_wid -= 1) { if ('0' != bits[trim_wid]) { trim_wid += 1; break; } } if (trim_wid < nbits) trim_wid += 1; /* Check to see if the value is too large. */ if (trim_wid > 64U) { *result_type = trim_wid; return 0; } /* Now build the value from the bits. */ for (idx = 0; idx < trim_wid; idx += 1) { if (bits[idx] == '1') value |= (uint64_t)1 << idx; else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } return 0; } } *result_type = 0; return value; } /* * Extract an int64_t value from the given number expression. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. -2 is all bits z and -3 is all bits x. */ int64_t get_int64_from_number(ivl_expr_t expr, int *result_type) { unsigned is_signed = ivl_expr_signed(expr); unsigned nbits = ivl_expr_width(expr); unsigned trim_wid = nbits - 1; const char *bits = ivl_expr_bits(expr); const char msb = is_signed ? bits[trim_wid] : '0'; unsigned idx; int64_t value = 0; assert(ivl_expr_type(expr) == IVL_EX_NUMBER); /* Trim any duplicate bits from the MSB. */ for (/* none */; trim_wid > 0; trim_wid -= 1) { if (msb != bits[trim_wid]) { trim_wid += 1; break; } } if (trim_wid < nbits) trim_wid += 1; /* Check to see if the value is too large. */ if (trim_wid > 64U) { *result_type = trim_wid; return 0; } /* Now build the value from the bits. */ for (idx = 0; idx < trim_wid; idx += 1) { if (bits[idx] == '1') value |= (int64_t)1 << idx; else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } return 0; } } /* Sign extend as needed. */ if (is_signed && (msb == '1') && (trim_wid < 64U)) { value |= ~(((int64_t)1 << trim_wid) - (int64_t)1); } *result_type = 0; return value; } /* * Extract an int32_t value from the given number expression. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. -2 is all bits z and -3 is all bits x. */ int32_t get_int32_from_number(ivl_expr_t expr, int *result_type) { assert(ivl_expr_type(expr) == IVL_EX_NUMBER); return get_int32_from_bits(ivl_expr_bits(expr), ivl_expr_width(expr), ivl_expr_signed(expr), result_type); } /* * Routine to remove two characters starting at the given address. */ static void remove_two_chars(char* str) { for (; str[2]; str += 1) { str[0] = str[2]; } str[0] = 0; } /* * Routine to print a string value as a string after removing any leading * escaped NULL bytes. */ void emit_string(const char* string) { char *buffer = strdup(string); char *bptr = buffer; char *cptr; fprintf(vlog_out, "\""); /* Prune any leading escaped NULL bytes. */ while ((bptr[0] == '\\') && (bptr[1] == '0') && (bptr[2] == '0') && (bptr[3] == '0')) bptr += 4; for (cptr = bptr; *cptr; cptr += 1) { if (*cptr == '\\') { /* Replace any \011 with \t */ if ((cptr[1] == '0') && (cptr[2] == '1') && (cptr[3] == '1')) { cptr[1] = 't'; remove_two_chars(cptr+2); cptr += 1; /* Replace any \012 with \n */ } else if ((cptr[1] == '0') && (cptr[2] == '1') && (cptr[3] == '2')) { cptr[1] = 'n'; remove_two_chars(cptr+2); cptr += 1; /* Replace any \042 with \" */ } else if ((cptr[1] == '0') && (cptr[2] == '4') && (cptr[3] == '2')) { cptr[1] = '"'; remove_two_chars(cptr+2); cptr += 1; /* Replace any \134 with \\ */ } else if ((cptr[1] == '1') && (cptr[2] == '3') && (cptr[3] == '4')) { cptr[1] = '\\'; remove_two_chars(cptr+2); cptr += 1; } else cptr += 3; } } if (*bptr) fprintf(vlog_out, "%s", bptr); free(buffer); fprintf(vlog_out, "\""); } iverilog-10_1/tgt-vlog95/scope.c000066400000000000000000001265701265551621300165530ustar00rootroot00000000000000/* * Copyright (C) 2010-2015 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include "config.h" # include "vlog95_priv.h" # include "ivl_alloc.h" const char *func_rtn_name = 0; static const char*get_time_const(int time_value) { switch (time_value) { case 2: return "100s"; case 1: return "10s"; case 0: return "1s"; case -1: return "100ms"; case -2: return "10ms"; case -3: return "1ms"; case -4: return "100us"; case -5: return "10us"; case -6: return "1us"; case -7: return "100ns"; case -8: return "10ns"; case -9: return "1ns"; case -10: return "100ps"; case -11: return "10ps"; case -12: return "1ps"; case -13: return "100fs"; case -14: return "10fs"; case -15: return "1fs"; default: fprintf(stderr, "Invalid time constant value %d.\n", time_value); return "N/A"; } } static void emit_func_return(ivl_signal_t sig) { if (ivl_signal_dimensions(sig) > 0) { fprintf(stderr, "%s:%u: vlog95 error: A function cannot return " "an array.\n", ivl_signal_file(sig), ivl_signal_lineno(sig)); vlog_errors += 1; } else if (ivl_signal_integer(sig)) { fprintf(vlog_out, " integer"); } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { fprintf(vlog_out, " real"); } else { int msb, lsb; get_sig_msb_lsb(sig, &msb, &lsb); if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); } } void emit_sig_file_line(ivl_signal_t sig) { if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_signal_file(sig), ivl_signal_lineno(sig)); } } static void emit_sig_id(ivl_signal_t sig) { emit_id(ivl_signal_basename(sig)); fprintf(vlog_out, ";"); emit_sig_file_line(sig); fprintf(vlog_out, "\n"); } static void emit_var_def(ivl_signal_t sig) { if (ivl_signal_local(sig)) return; fprintf(vlog_out, "%*c", indent, ' '); if (ivl_signal_integer(sig)) { fprintf(vlog_out, "integer "); emit_sig_id(sig); if (ivl_signal_dimensions(sig) > 0) { fprintf(stderr, "%s:%u: vlog95 error: Integer arrays (%s) " "are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { fprintf(vlog_out, "real "); emit_sig_id(sig); if (ivl_signal_dimensions(sig) > 0) { fprintf(stderr, "%s:%u: vlog95 error: Real arrays (%s) " "are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } } else if (ivl_signal_data_type(sig) == IVL_VT_STRING) { fprintf(vlog_out, "string "); emit_sig_id(sig); fprintf(stderr, "%s:%u: vlog95 error: SystemVerilog strings (%s) " "are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } else if (ivl_signal_data_type(sig) == IVL_VT_DARRAY) { fprintf(vlog_out, " "); emit_sig_id(sig); fprintf(stderr, "%s:%u: vlog95 error: SystemVerilog dynamic " "arrays (%s) are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } else if (ivl_signal_data_type(sig) == IVL_VT_QUEUE) { fprintf(vlog_out, " "); emit_sig_id(sig); fprintf(stderr, "%s:%u: vlog95 error: SystemVerilog queues " "(%s) are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } else { int msb, lsb; get_sig_msb_lsb(sig, &msb, &lsb); fprintf(vlog_out, "reg "); if (ivl_signal_signed(sig)) { if (allow_signed) { fprintf(vlog_out, "signed "); } else { fprintf(stderr, "%s:%u: vlog95 error: Signed registers " "(%s) are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } } if (msb != 0 || lsb != 0) fprintf(vlog_out, "[%d:%d] ", msb, lsb); emit_id(ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig) > 0) { unsigned wd_count = ivl_signal_array_count(sig); int first = ivl_signal_array_base(sig); int last = first + wd_count - 1; if (ivl_signal_array_addr_swapped(sig)) { fprintf(vlog_out, " [%d:%d]", last, first); } else { fprintf(vlog_out, " [%d:%d]", first, last); } } fprintf(vlog_out, ";"); emit_sig_file_line(sig); fprintf(vlog_out, "\n"); } } /* * Keep a list of constants that drive nets and need to be emitted as * a continuous assignment. */ static ivl_signal_t *net_consts = 0; static unsigned num_net_consts = 0; static void add_net_const_to_list(ivl_signal_t net_const) { num_net_consts += 1; net_consts = realloc(net_consts, num_net_consts * sizeof(ivl_signal_t)); net_consts[num_net_consts-1] = net_const; } static unsigned emit_and_free_net_const_list(ivl_scope_t scope) { unsigned idx; for (idx = 0; idx < num_net_consts; idx += 1) { emit_signal_net_const_as_ca(scope, net_consts[idx]); } free(net_consts); net_consts = 0; idx = num_net_consts != 0; num_net_consts = 0; return idx; } static void save_net_constants(ivl_scope_t scope, ivl_signal_t sig) { ivl_nexus_t nex = ivl_signal_nex(sig, 0); unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_net_const_t net_const = ivl_nexus_ptr_con(nex_ptr); if (! net_const) continue; if (scope != ivl_const_scope(net_const)) continue; add_net_const_to_list(sig); } } static void emit_net_def(ivl_scope_t scope, ivl_signal_t sig) { int msb, lsb; get_sig_msb_lsb(sig, &msb, &lsb); if (ivl_signal_local(sig)) return; fprintf(vlog_out, "%*c", indent, ' '); if (ivl_signal_data_type(sig) == IVL_VT_REAL){ fprintf(vlog_out, "wire "); emit_sig_id(sig); fprintf(stderr, "%s:%u: vlog95 error: Real nets (%s) are " "not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } else if (ivl_signal_dimensions(sig) > 0) { fprintf(vlog_out, "wire "); if (msb != 0 || lsb != 0) fprintf(vlog_out, "[%d:%d] ", msb, lsb); emit_sig_id(sig); fprintf(stderr, "%s:%u: vlog95 error: Array nets (%s) are " "not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } else { switch (ivl_signal_type(sig)) { case IVL_SIT_TRI: case IVL_SIT_UWIRE: // HERE: Need to add support for supply nets. Probably supply strength // with a constant 0/1 driver for all the bits. fprintf(vlog_out, "wire "); break; case IVL_SIT_TRI0: fprintf(vlog_out, "tri0 "); break; case IVL_SIT_TRI1: fprintf(vlog_out, "tri1 "); break; case IVL_SIT_TRIAND: fprintf(vlog_out, "wand "); break; case IVL_SIT_TRIOR: fprintf(vlog_out, "wor "); break; default: fprintf(vlog_out, " "); fprintf(stderr, "%s:%u: vlog95 error: Unknown net type " "(%d).\n", ivl_signal_file(sig), ivl_signal_lineno(sig), (int)ivl_signal_type(sig)); vlog_errors += 1; break; } if (ivl_signal_signed(sig)) { if (allow_signed) { fprintf(vlog_out, "signed "); } else { fprintf(stderr, "%s:%u: vlog95 error: Signed nets (%s) " "are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } } if (msb != 0 || lsb != 0) fprintf(vlog_out, "[%d:%d] ", msb, lsb); emit_sig_id(sig); /* A constant driving a net does not create an lpm or logic * element in the design so save them from the definition. */ save_net_constants(scope, sig); } } static void emit_mangled_name(ivl_scope_t scope, unsigned root) { /* If the module has parameters and it's not a root module then it * may not be unique so we create a mangled name version instead. * The mangled name is of the form: * []. */ if (ivl_scope_params(scope) && ! root) { char *name; size_t len = strlen(ivl_scope_name(scope)) + strlen(ivl_scope_tname(scope)) + 3; name = (char *)malloc(len); (void) strcpy(name, ivl_scope_tname(scope)); (void) strcat(name, "["); (void) strcat(name, ivl_scope_name(scope)); (void) strcat(name, "]"); assert(name[len-1] == 0); /* Emit the mangled name as an escaped identifier. */ fprintf(vlog_out, "\\%s ", name); free(name); } else { emit_id(ivl_scope_tname(scope)); } } /* * This function is called for each process in the design so that we * can extract the processes for the given scope. */ static int find_process(ivl_process_t proc, ivl_scope_t scope) { if (scope == ivl_process_scope(proc)) emit_process(scope, proc); return 0; } void emit_scope_variables(ivl_scope_t scope) { unsigned idx, count; assert(! num_net_consts); /* Output the parameters for this scope. */ count = ivl_scope_params(scope); for (idx = 0; idx < count; idx += 1) { ivl_parameter_t par = ivl_scope_param(scope, idx); ivl_expr_t pex = ivl_parameter_expr(par); fprintf(vlog_out, "%*cparameter ", indent, ' '); emit_id(ivl_parameter_basename(par)); fprintf(vlog_out, " = "); /* Need to emit the parameters value not its name. */ emitting_param = par; emit_expr(scope, pex, ivl_parameter_width(par), 1, 0, 0); emitting_param = 0; fprintf(vlog_out, ";"); if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_parameter_file(par), ivl_parameter_lineno(par)); } fprintf(vlog_out, "\n"); } if (count) fprintf(vlog_out, "\n"); /* Output the signals for this scope. */ count = ivl_scope_sigs(scope); for (idx = 0; idx < count; idx += 1) { ivl_signal_t sig = ivl_scope_sig(scope, idx); if (ivl_signal_type(sig) == IVL_SIT_REG) { /* Do not output the implicit function return register. */ if (ivl_scope_type(scope) == IVL_SCT_FUNCTION && strcmp(ivl_signal_basename(sig), ivl_scope_tname(scope)) == 0) continue; emit_var_def(sig); } else { emit_net_def(scope, sig); } } if (count) fprintf(vlog_out, "\n"); /* Output the named events for this scope. */ count = ivl_scope_events(scope); for (idx = 0; idx < count; idx += 1) { ivl_event_t event = ivl_scope_event(scope, idx); /* If this event has any type of edge sensitivity then it is * not a named event. */ if (ivl_event_nany(event)) continue; if (ivl_event_npos(event)) continue; if (ivl_event_nneg(event)) continue; fprintf(vlog_out, "%*cevent ", indent, ' '); emit_id(ivl_event_basename(event)); fprintf(vlog_out, ";"); if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_event_file(event), ivl_event_lineno(event)); } fprintf(vlog_out, "\n"); } if (count) fprintf(vlog_out, "\n"); if (emit_and_free_net_const_list(scope)) fprintf(vlog_out, "\n"); } static void emit_scope_file_line(ivl_scope_t scope) { if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_scope_file(scope), ivl_scope_lineno(scope)); } } static void emit_module_ports(ivl_scope_t scope) { unsigned idx, count = ivl_scope_ports(scope); if (count == 0) return; fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, 0), 0, 0); for (idx = 1; idx < count; idx += 1) { fprintf(vlog_out, ", "); emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, idx), 0, 0); } fprintf(vlog_out, ")"); } static ivl_signal_t get_port_from_nexus(ivl_scope_t scope, ivl_nexus_t nex, unsigned *word) { assert(nex); unsigned idx, count = ivl_nexus_ptrs(nex); ivl_signal_t sig = 0; *word = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (t_sig) { if (ivl_signal_scope(t_sig) != scope) continue; assert(! sig); sig = t_sig; *word = ivl_nexus_ptr_pin(nex_ptr); } } return sig; } static void emit_sig_type(ivl_signal_t sig) { ivl_signal_type_t type = ivl_signal_type(sig); if (ivl_signal_dimensions(sig) != 0) { fprintf(stderr, "%s:%u: vlog95 error: Array ports (%s) are not " "supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } /* Check to see if we have a variable (reg) or a net. */ if (type == IVL_SIT_REG) { /* The variable data type will be declared later, so here we just want to declare the range and whether or not it is signed. */ if (ivl_signal_integer(sig)) { /* nothing to do */ } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { /* nothing to do */ } else { int msb, lsb; get_sig_msb_lsb(sig, &msb, &lsb); if (ivl_signal_signed(sig)) { if (allow_signed) { fprintf(vlog_out, " signed"); } else { fprintf(stderr, "%s:%u: vlog95 error: Signed " "ports (%s) are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } } if (msb != 0 || lsb != 0) { fprintf(vlog_out, " [%d:%d]", msb, lsb); } } } else { assert((type == IVL_SIT_TRI) || (type == IVL_SIT_TRI0) || (type == IVL_SIT_TRI1) || (type == IVL_SIT_UWIRE)); if (ivl_signal_data_type(sig) == IVL_VT_REAL) { fprintf(stderr, "%s:%u: vlog95 error: Real net ports (%s) " "are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } else { int msb, lsb; get_sig_msb_lsb(sig, &msb, &lsb); if (ivl_signal_signed(sig)) { if (allow_signed) { fprintf(vlog_out, " signed"); } else { fprintf(stderr, "%s:%u: vlog95 error: Signed net " "ports (%s) are not supported.\n", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); vlog_errors += 1; } } if (msb != 0 || lsb != 0) { fprintf(vlog_out, " [%d:%d]", msb, lsb); } } } } static void emit_port(ivl_signal_t port) { assert(port); fprintf(vlog_out, "%*c", indent, ' '); switch (ivl_signal_port(port)) { case IVL_SIP_INPUT: fprintf(vlog_out, "input"); break; case IVL_SIP_OUTPUT: fprintf(vlog_out, "output"); break; case IVL_SIP_INOUT: fprintf(vlog_out, "inout"); break; default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown port direction (%d) " "for signal %s.\n", ivl_signal_file(port), ivl_signal_lineno(port), (int)ivl_signal_port(port), ivl_signal_basename(port)); vlog_errors += 1; break; } emit_sig_type(port); fprintf(vlog_out, " "); /* Split port (arg[7:4],arg[3:0]) are generated using local signals. */ if (ivl_signal_local(port)) { fprintf(vlog_out, "ivlog%s", ivl_signal_basename(port)); } else { emit_id(ivl_signal_basename(port)); } fprintf(vlog_out, ";"); emit_sig_file_line(port); fprintf(vlog_out, "\n"); } static void emit_module_port_defs(ivl_scope_t scope) { unsigned word, idx, count = ivl_scope_ports(scope); for (idx = 0; idx < count; idx += 1) { ivl_nexus_t nex = ivl_scope_mod_port(scope, idx); ivl_signal_t port = get_port_from_nexus(scope, nex, &word); // HERE: Do we need to use word? if (port) emit_port(port); else { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Could not find signal " "definition for port (%u) of module %s.\n", ivl_scope_file(scope), ivl_scope_lineno(scope), idx + 1, ivl_scope_basename(scope)); vlog_errors += 1; } } if (count) fprintf(vlog_out, "\n"); } static void emit_module_call_expr(ivl_scope_t scope, unsigned idx) { unsigned word; ivl_nexus_t nex = ivl_scope_mod_port(scope, idx); ivl_signal_t port = get_port_from_nexus(scope, nex, &word); /* For an input port we need to emit the driving expression. */ if (ivl_signal_port(port) == IVL_SIP_INPUT) { emit_nexus_port_driver_as_ca(ivl_scope_parent(scope), ivl_signal_nex(port, word)); /* For an output we need to emit the signal the output is driving. */ } else { emit_nexus_as_ca(ivl_scope_parent(scope), ivl_signal_nex(port, word), 0, 0); } } static void emit_module_call_expressions(ivl_scope_t scope) { unsigned idx, count = ivl_scope_ports(scope); if (count == 0) return; emit_module_call_expr(scope, 0); for (idx = 1; idx < count; idx += 1) { fprintf(vlog_out, ", "); emit_module_call_expr(scope, idx); } } static void emit_task_func_port_defs(ivl_scope_t scope) { unsigned idx, count = ivl_scope_ports(scope); unsigned start = ivl_scope_type(scope) == IVL_SCT_FUNCTION; for (idx = start; idx < count; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); emit_port(port); } /* If the start and count are both 1 then this is a SystemVerilog * function that does not have an argument so add a dummy one. */ if ((start == 1) && (count == 1)) { fprintf(vlog_out, "%*cinput _vlog95_dummy;", indent, ' '); if (emit_file_line) fprintf(vlog_out, " /* no file/line */"); fprintf(vlog_out, "\n"); } if (count) fprintf(vlog_out, "\n"); } /* * Recursively look for the named block in the given statement. */ static int has_named_block(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, count; int rtn = 0; if (! stmt) return 0; switch (ivl_statement_type(stmt)) { /* Block or fork items can contain a named block. */ case IVL_ST_BLOCK: case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: if (ivl_stmt_block_scope(stmt) == scope) return 1; count = ivl_stmt_block_count(stmt); for (idx = 0; (idx < count) && ! rtn ; idx += 1) { rtn |= has_named_block(scope, ivl_stmt_block_stmt(stmt, idx)); } break; /* Case items can contain a named block. */ case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: count = ivl_stmt_case_count(stmt); for (idx = 0; (idx < count) && ! rtn; idx += 1) { rtn |= has_named_block(scope, ivl_stmt_case_stmt(stmt, idx)); } break; /* Either the true or false clause may have a named block. */ case IVL_ST_CONDIT: rtn = has_named_block(scope, ivl_stmt_cond_true(stmt)); if (! rtn) { rtn = has_named_block(scope, ivl_stmt_cond_false(stmt)); } break; /* The looping statements may have a named block. */ case IVL_ST_DO_WHILE: case IVL_ST_FOREVER: case IVL_ST_REPEAT: case IVL_ST_WHILE: /* The delay and wait statements may have a named block. */ case IVL_ST_DELAY: case IVL_ST_DELAYX: case IVL_ST_WAIT: rtn = has_named_block(scope, ivl_stmt_sub_stmt(stmt)); break; default: /* The rest cannot have a named block. */ ; } return rtn; } /* * Look at all the processes to see if we can find one with the expected * scope. If we don't find one then we can assume the block only has * variable definitions and needs to be emitted here in the scope code. */ static int no_stmts_in_process(ivl_process_t proc, ivl_scope_t scope) { return has_named_block(scope, ivl_process_stmt(proc)); } /* * If a named block has no statements then we may need to emit it here if * there are variable definitions in the scope. We translate all this to * an initial and named begin since that is enough to hold the variables. */ static void emit_named_block_scope(ivl_scope_t scope) { unsigned idx, count = ivl_scope_events(scope); unsigned named_ev = 0; /* If there are no parameters, signals or named events then skip * this block. */ for (idx = 0; idx < count; idx += 1) { ivl_event_t event = ivl_scope_event(scope, idx); /* If this event has any type of edge sensitivity then it is * not a named event. */ if (ivl_event_nany(event)) continue; if (ivl_event_npos(event)) continue; if (ivl_event_nneg(event)) continue; named_ev = 1; break; } if ((ivl_scope_params(scope) == 0) && (ivl_scope_sigs(scope) == 0) && (named_ev == 0)) return; /* Currently we only need to emit a named block for the variables * if the parent scope is a module. This gets much more complicated * if this is not true. */ if (ivl_scope_type(ivl_scope_parent(scope)) != IVL_SCT_MODULE) return; /* Scan all the processes looking for one that matches this scope. * If a match is found then this named block was already emitted by * the process code. */ if (ivl_design_process(design, (ivl_process_f)no_stmts_in_process, scope)) return; /* A match was not found so emit the named block here to get the * variable definitions. */ fprintf(vlog_out, "\n%*cinitial begin: ", indent, ' '); emit_id(ivl_scope_tname(scope)); emit_scope_file_line(scope); fprintf(vlog_out, "\n"); indent += indent_incr; emit_scope_variables(scope); indent -= indent_incr; fprintf(vlog_out, "%*cend /* ", indent, ' '); emit_id(ivl_scope_tname(scope)); fprintf(vlog_out, " */\n"); } /* * In SystemVerilog a task or function can have a process to initialize * variables. In reality SystemVerilog requires this to be before the * initial/always blocks are processed, but that's not how it is currently * implemented in Icarus! */ static int find_tf_process(ivl_process_t proc, ivl_scope_t scope) { if (scope == ivl_process_scope(proc)) { ivl_scope_t mod_scope = scope; /* A task or function can only have initial processes that * are used to set local variables. */ assert(ivl_process_type(proc) == IVL_PR_INITIAL); /* Find the module scope for this task/function. */ while (ivl_scope_type(mod_scope) != IVL_SCT_MODULE) { mod_scope = ivl_scope_parent(mod_scope); assert(mod_scope); } /* Emit the process in the module scope since that is where * this all started. */ emit_process(mod_scope, proc); } return 0; } /* * Emit any initial blocks for the tasks or functions in a module. */ static int emit_tf_process(ivl_scope_t scope, ivl_scope_t parent) { ivl_scope_type_t sc_type = ivl_scope_type(scope); (void)parent; /* Parameter is not used. */ if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK)) { /* Output the initial/always blocks for this module. */ ivl_design_process(design, (ivl_process_f)find_tf_process, scope); } return 0; } static void emit_path_delay(ivl_scope_t scope, ivl_delaypath_t dpath) { unsigned idx, count = 6; uint64_t pdlys [12]; pdlys[0] = ivl_path_delay(dpath, IVL_PE_01); pdlys[1] = ivl_path_delay(dpath, IVL_PE_10); pdlys[2] = ivl_path_delay(dpath, IVL_PE_0z); pdlys[3] = ivl_path_delay(dpath, IVL_PE_z1); pdlys[4] = ivl_path_delay(dpath, IVL_PE_1z); pdlys[5] = ivl_path_delay(dpath, IVL_PE_z0); pdlys[6] = ivl_path_delay(dpath, IVL_PE_0x); pdlys[7] = ivl_path_delay(dpath, IVL_PE_x1); pdlys[8] = ivl_path_delay(dpath, IVL_PE_1x); pdlys[9] = ivl_path_delay(dpath, IVL_PE_x0); pdlys[10] = ivl_path_delay(dpath, IVL_PE_xz); pdlys[11] = ivl_path_delay(dpath, IVL_PE_zx); /* If the first six pdlys match then this may be a 1 delay form. */ if ((pdlys[0] == pdlys[1]) && (pdlys[0] == pdlys[2]) && (pdlys[0] == pdlys[3]) && (pdlys[0] == pdlys[4]) && (pdlys[0] == pdlys[5])) count = 1; /* Check to see if only a rise and fall value are given for the first * six pdlys. */ else if ((pdlys[0] == pdlys[2]) && (pdlys[0] == pdlys[3]) && (pdlys[1] == pdlys[4]) && (pdlys[1] == pdlys[5])) count = 2; /* Check to see if a rise, fall and high-Z value are given for the * first six pdlys. */ else if ((pdlys[0] == pdlys[3]) && (pdlys[1] == pdlys[5]) && (pdlys[2] == pdlys[4])) count = 3; /* Now check to see if the 'bx related pdlys match the reduced * delay form. If not then this is a twelve delay value. */ if ((pdlys[6] != ((pdlys[0] < pdlys[2]) ? pdlys[0] : pdlys[2])) || (pdlys[8] != ((pdlys[1] < pdlys[4]) ? pdlys[1] : pdlys[4])) || (pdlys[11] != ((pdlys[3] < pdlys[5]) ? pdlys[3] : pdlys[5])) || (pdlys[7] != ((pdlys[0] > pdlys[3]) ? pdlys[0] : pdlys[3])) || (pdlys[9] != ((pdlys[1] > pdlys[5]) ? pdlys[1] : pdlys[5])) || (pdlys[10] != ((pdlys[2] > pdlys[4]) ? pdlys[2] : pdlys[4]))) { count = 12; } emit_scaled_delay(scope, pdlys[0]); for(idx = 1; idx < count; idx += 1) { fprintf(vlog_out, ", "); emit_scaled_delay(scope, pdlys[idx]); } } static void emit_specify_paths(ivl_scope_t scope, ivl_signal_t sig) { unsigned idx, count = ivl_signal_npath(sig); for(idx = 0; idx < count; idx += 1) { ivl_delaypath_t dpath = ivl_signal_path(sig, idx); ivl_nexus_t cond = ivl_path_condit(dpath); ivl_nexus_t source = ivl_path_source(dpath); unsigned has_edge = 0; fprintf(vlog_out, "%*c", indent, ' '); if (cond) { fprintf(vlog_out, "if ("); emit_nexus_as_ca(scope, cond, 0, 0); fprintf(vlog_out, ") "); } else if (ivl_path_is_condit(dpath)) { fprintf(vlog_out, "ifnone "); } fprintf(vlog_out, "("); if (ivl_path_source_posedge(dpath)) { fprintf(vlog_out, "posedge "); has_edge = 1; } if (ivl_path_source_negedge(dpath)) { fprintf(vlog_out, "negedge "); has_edge = 1; } emit_nexus_as_ca(scope, source, 0, 0); fprintf(vlog_out, " =>"); /* The compiler does not keep the source expression for an edge * sensitive path so add a constant to get the syntax right. */ if (has_edge) { fprintf(vlog_out, "(%s : 1'bx /* Missing */)", ivl_signal_basename(sig)); } else { fprintf(vlog_out, "%s", ivl_signal_basename(sig)); } fprintf(vlog_out, ") = ("); emit_path_delay(scope, dpath); fprintf(vlog_out, ");\n"); } } /* * The path delay information from the specify block is attached to the * output ports. */ static void emit_specify(ivl_scope_t scope) { unsigned word, idx, count = ivl_scope_ports(scope); unsigned need_specify = 0; for (idx = 0; idx < count; idx += 1) { ivl_nexus_t nex = ivl_scope_mod_port(scope, idx); ivl_signal_t port = get_port_from_nexus(scope, nex, &word); // HERE: Do we need to use word? See emit_module_port_def(). assert(port); if (ivl_signal_npath(port)) { if (! need_specify) { fprintf(vlog_out, "\n%*cspecify\n", indent, ' '); need_specify = 1; indent += indent_incr; } emit_specify_paths(scope, port); } } if (need_specify) { indent -= indent_incr; fprintf(vlog_out, "%*cendspecify\n", indent, ' '); } } /* * Look for a disable in the statement (function body) for this scope. */ static unsigned has_func_disable(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, count, rtn = 0; /* If there is a statement then look to see if it is or has a * disable for this function scope. */ if (! stmt) return 0; assert(ivl_scope_type(scope) == IVL_SCT_FUNCTION); switch (ivl_statement_type(stmt)) { /* These are not allowed in a function. */ case IVL_ST_ASSIGN_NB: case IVL_ST_DELAY: case IVL_ST_DELAYX: case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: case IVL_ST_UTASK: case IVL_ST_WAIT: assert(0); break; /* These are allowed in a function and cannot have a disable. */ case IVL_ST_NOOP: case IVL_ST_ALLOC: case IVL_ST_ASSIGN: case IVL_ST_CASSIGN: case IVL_ST_DEASSIGN: case IVL_ST_FORCE: case IVL_ST_FREE: case IVL_ST_RELEASE: case IVL_ST_STASK: case IVL_ST_TRIGGER: break; /* Look for a disable in each block statement. */ case IVL_ST_BLOCK: count = ivl_stmt_block_count(stmt); for (idx = 0; (idx < count) && ! rtn; idx += 1) { rtn |= has_func_disable(scope, ivl_stmt_block_stmt(stmt, idx)); } break; /* Look for a disable in each case branch. */ case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: count = ivl_stmt_case_count(stmt); for (idx = 0; (idx < count) && ! rtn; idx += 1) { rtn |= has_func_disable(scope, ivl_stmt_case_stmt(stmt, idx)); } break; /* Either the true or false clause may have a disable. */ case IVL_ST_CONDIT: rtn = has_func_disable(scope, ivl_stmt_cond_true(stmt)); if (! rtn) { rtn = has_func_disable(scope, ivl_stmt_cond_false(stmt)); } break; /* These have a single sub-statement so look for a disable there. */ case IVL_ST_DO_WHILE: case IVL_ST_FOREVER: case IVL_ST_REPEAT: case IVL_ST_WHILE: rtn = has_func_disable(scope, ivl_stmt_sub_stmt(stmt)); break; /* The function has a disable if the disable scope matches the * function scope. */ case IVL_ST_DISABLE: rtn = scope == ivl_stmt_call(stmt); break; default: fprintf(stderr, "%s:%u: vlog95 error: Unknown statement type (%d) " "in function disable check.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt), (int)ivl_statement_type(stmt)); vlog_errors += 1; break; } return rtn; } /* * This is the block name used when a SystemVerilog return is used in a * function and the body does not already have an enclosing named block. * This is needed since the actual function cannot be disabled. */ static char *get_func_return_name(ivl_scope_t scope) { const char *name_func = ivl_scope_basename(scope); const char *name_head = "_ivl_"; const char *name_tail = "_return"; char *name_return; name_return = (char *)malloc(strlen(name_head) + strlen(name_func) + strlen(name_tail) + 1); name_return[0] = 0; (void) strcpy(name_return, name_head); (void) strcat(name_return, name_func); (void) strcat(name_return, name_tail); return name_return; } /* * This search method may be slow for a large structural design with a * large number of gate types. That's not what this converter was built * for so this is probably OK. If this becomes an issue then we need a * better method/data structure. */ static const char **scopes_emitted = 0; static unsigned num_scopes_emitted = 0; static unsigned scope_has_been_emitted(ivl_scope_t scope) { unsigned idx; for (idx = 0; idx < num_scopes_emitted; idx += 1) { if (! strcmp(ivl_scope_tname(scope), scopes_emitted[idx])) return 1; } return 0; } static void add_scope_to_list(ivl_scope_t scope) { num_scopes_emitted += 1; scopes_emitted = realloc(scopes_emitted, num_scopes_emitted * sizeof(char *)); scopes_emitted[num_scopes_emitted-1] = ivl_scope_tname(scope); } void free_emitted_scope_list() { free(scopes_emitted); scopes_emitted = 0; num_scopes_emitted = 0; } /* * A list of module scopes that need to have their definition emitted when * the current root scope (module) is finished is kept here. */ static ivl_scope_t *scopes_to_emit = 0; static unsigned num_scopes_to_emit = 0; static unsigned emitting_scopes = 0; int emit_scope(ivl_scope_t scope, ivl_scope_t parent) { char *package_name = 0; ivl_scope_type_t sc_type = ivl_scope_type(scope); unsigned is_auto = ivl_scope_is_auto(scope); unsigned idx; /* Output the scope definition. */ switch (sc_type) { case IVL_SCT_MODULE: assert(!is_auto); /* This is an instantiation. */ if (parent) { assert(indent != 0); /* If the module has parameters then it may not be unique * so we create a mangled name version instead. */ fprintf(vlog_out, "\n%*c", indent, ' '); emit_mangled_name(scope, !parent && !emitting_scopes); fprintf(vlog_out, " "); emit_id(ivl_scope_basename(scope)); fprintf(vlog_out, "("); emit_module_call_expressions(scope); fprintf(vlog_out, ");"); emit_scope_file_line(scope); fprintf(vlog_out, "\n"); num_scopes_to_emit += 1; scopes_to_emit = realloc(scopes_to_emit, num_scopes_to_emit * sizeof(ivl_scope_t)); scopes_to_emit[num_scopes_to_emit-1] = scope; return 0; } assert(indent == 0); /* Set the time scale for this scope. */ fprintf(vlog_out, "\n`timescale %s/%s\n", get_time_const(ivl_scope_time_units(scope)), get_time_const(ivl_scope_time_precision(scope))); if (ivl_scope_is_cell(scope)) { fprintf(vlog_out, "`celldefine\n"); } fprintf(vlog_out, "/* This module was originally defined in " "file %s at line %u. */\n", ivl_scope_def_file(scope), ivl_scope_def_lineno(scope)); fprintf(vlog_out, "module "); emit_mangled_name(scope, !parent && !emitting_scopes); emit_module_ports(scope); break; case IVL_SCT_FUNCTION: /* Root scope functions have already been emitted. */ if (! parent) return 0; assert(indent != 0); fprintf(vlog_out, "\n%*cfunction", indent, ' '); if (ivl_scope_ports(scope) < 1) { fprintf(stderr, "%s:%u: vlog95 error: Function (%s) has " "no return value.\n", ivl_scope_file(scope), ivl_scope_lineno(scope), ivl_scope_tname(scope)); vlog_errors += 1; } /* The function return information is the zero port. */ emit_func_return(ivl_scope_port(scope, 0)); fprintf(vlog_out, " "); emit_id(ivl_scope_tname(scope)); if (is_auto) { fprintf(stderr, "%s:%u: vlog95 error: Automatic functions " "(%s) are not supported.\n", ivl_scope_file(scope), ivl_scope_lineno(scope), ivl_scope_tname(scope)); vlog_errors += 1; } break; case IVL_SCT_TASK: /* Root scope tasks have already been emitted. */ if (! parent) return 0; assert(indent != 0); fprintf(vlog_out, "\n%*ctask ", indent, ' '); emit_id(ivl_scope_tname(scope)); if (is_auto) { fprintf(stderr, "%s:%u: vlog95 error: Automatic tasks " "(%s) are not supported.\n", ivl_scope_file(scope), ivl_scope_lineno(scope), ivl_scope_tname(scope)); vlog_errors += 1; } break; case IVL_SCT_BEGIN: case IVL_SCT_FORK: assert(indent != 0); emit_named_block_scope(scope); return 0; /* A named begin/fork is handled in line. */ case IVL_SCT_GENERATE: fprintf(stderr, "%s:%u: vlog95 sorry: generate scopes are not " "currently translated \"%s\".\n", ivl_scope_file(scope), ivl_scope_lineno(scope), ivl_scope_tname(scope)); vlog_errors += 1; return 0; case IVL_SCT_PACKAGE: assert(indent == 0); assert(! parent); /* Set the time scale for this scope. */ fprintf(vlog_out, "\n`timescale %s/%s\n", get_time_const(ivl_scope_time_units(scope)), get_time_const(ivl_scope_time_precision(scope))); /* Emit a package as a module with a special name. */ fprintf(vlog_out, "/* This package (module) was originally " "defined in file %s at line %u. */\n", ivl_scope_def_file(scope), ivl_scope_def_lineno(scope)); fprintf(vlog_out, "module "); package_name = get_package_name(scope); emit_id(package_name); break; case IVL_SCT_CLASS: fprintf(stderr, "%s:%u: vlog95 sorry: class scopes are not " "currently translated \"%s\".\n", ivl_scope_file(scope), ivl_scope_lineno(scope), ivl_scope_tname(scope)); vlog_errors += 1; return 0; default: fprintf(stderr, "%s:%u: vlog95 error: Unsupported scope type " "(%d) named: %s.\n", ivl_scope_file(scope), ivl_scope_lineno(scope), sc_type, ivl_scope_tname(scope)); vlog_errors += 1; return 0; } fprintf(vlog_out, ";"); emit_scope_file_line(scope); fprintf(vlog_out, "\n"); indent += indent_incr; /* Output the scope ports for this scope. */ if (sc_type == IVL_SCT_MODULE) { emit_module_port_defs(scope); } else { emit_task_func_port_defs(scope); } emit_scope_variables(scope); if (sc_type == IVL_SCT_MODULE) { unsigned count = ivl_scope_lpms(scope); /* Output the LPM devices. */ for (idx = 0; idx < count; idx += 1) { emit_lpm(scope, ivl_scope_lpm(scope, idx)); } /* Output any logic devices. */ count = ivl_scope_logs(scope); for (idx = 0; idx < count; idx += 1) { emit_logic(scope, ivl_scope_log(scope, idx)); } /* Output any switch (logic) devices. */ count = ivl_scope_switches(scope); for (idx = 0; idx < count; idx += 1) { emit_tran(scope, ivl_scope_switch(scope, idx)); } /* Output any initial blocks for tasks or functions defined * in this module. Used to initialize local variables. */ ivl_scope_children(scope, (ivl_scope_f*) emit_tf_process, scope); /* Output the initial/always blocks for this module. */ ivl_design_process(design, (ivl_process_f)find_process, scope); } /* Output the function body. */ if (sc_type == IVL_SCT_FUNCTION) { ivl_statement_t body = ivl_scope_def(scope); assert(func_rtn_name == 0); /* If the function disables itself then that is really a * SystemVerilog return statement in disguise. A toplevel * named begin is needed to make this work in standard Verilog * so add one if it is needed. */ if (ivl_statement_type(body) == IVL_ST_BLOCK) { ivl_scope_t blk_scope = ivl_stmt_block_scope(body); if (blk_scope) { func_rtn_name = ivl_scope_basename(blk_scope); emit_stmt(scope, body); func_rtn_name = 0; } else if (has_func_disable(scope, body)) { char *name_return = get_func_return_name(scope); unsigned count = ivl_stmt_block_count(body); fprintf(vlog_out, "%*cbegin: %s\n", indent, ' ', name_return); indent += indent_incr; func_rtn_name = name_return; for (idx = 0; idx < count; idx += 1) { emit_stmt(scope, ivl_stmt_block_stmt(body, idx)); } func_rtn_name = 0; indent -= indent_incr; fprintf(vlog_out, "%*cend /* %s */\n", indent, ' ', name_return); free(name_return); } else emit_stmt(scope, body); /* A non-block statement may need a named block for a return. */ } else if (has_func_disable(scope, body)) { char *name_return = get_func_return_name(scope); fprintf(vlog_out, "%*cbegin: %s\n", indent, ' ', name_return); indent += indent_incr; func_rtn_name = name_return; emit_stmt(scope, body); func_rtn_name = 0; indent -= indent_incr; fprintf(vlog_out, "%*cend /* %s */\n", indent, ' ', name_return); free(name_return); } else emit_stmt(scope, body); } /* Output the task body. */ if (sc_type == IVL_SCT_TASK) emit_stmt(scope, ivl_scope_def(scope)); /* Print any sub-scopes. */ ivl_scope_children(scope, (ivl_scope_f*) emit_scope, scope); /* And finally print a specify block when needed. */ if (sc_type == IVL_SCT_MODULE) emit_specify(scope); /* Output the scope ending. */ assert(indent >= indent_incr); indent -= indent_incr; switch (sc_type) { case IVL_SCT_MODULE: assert(indent == 0); fprintf(vlog_out, "endmodule /* "); emit_mangled_name(scope, !parent && !emitting_scopes); fprintf(vlog_out, " */\n"); if (ivl_scope_is_cell(scope)) { fprintf(vlog_out, "`endcelldefine\n"); } /* If this is a root scope then emit any saved instance scopes. * Save any scope that does not have parameters/a mangled name * to a list so we don't print duplicate module definitions. */ if (!emitting_scopes) { emitting_scopes = 1; for (idx =0; idx < num_scopes_to_emit; idx += 1) { ivl_scope_t scope_to_emit = scopes_to_emit[idx]; if (scope_has_been_emitted(scope_to_emit)) continue; (void) emit_scope(scope_to_emit, 0); /* If we used a mangled name then the instance is * unique so don't add it to the list. */ if (ivl_scope_params(scope_to_emit)) continue; add_scope_to_list(scope_to_emit); } free(scopes_to_emit); scopes_to_emit = 0; num_scopes_to_emit = 0; emitting_scopes = 0; } break; case IVL_SCT_FUNCTION: fprintf(vlog_out, "%*cendfunction /* %s */\n", indent, ' ', ivl_scope_tname(scope)); break; case IVL_SCT_TASK: fprintf(vlog_out, "%*cendtask /* %s */\n", indent, ' ', ivl_scope_tname(scope)); break; case IVL_SCT_PACKAGE: fprintf(vlog_out, "endmodule /* "); emit_id(package_name); free(package_name); fprintf(vlog_out, " */\n"); break; default: assert(0); break; } return 0; } iverilog-10_1/tgt-vlog95/stmt.c000066400000000000000000001537161265551621300164330ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include "config.h" # include "vlog95_priv.h" static unsigned single_indent = 0; static unsigned get_indent(void) { if (single_indent) { single_indent = 0; return single_indent; } return indent; } static void emit_stmt_file_line(ivl_statement_t stmt) { if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); } } static void emit_stmt_block_body(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, count = ivl_stmt_block_count(stmt); ivl_scope_t my_scope = ivl_stmt_block_scope(stmt); indent += indent_incr; if (my_scope) emit_scope_variables(my_scope); else my_scope = scope; for (idx = 0; idx < count; idx += 1) { emit_stmt(my_scope, ivl_stmt_block_stmt(stmt, idx)); } assert(indent >= indent_incr); indent -= indent_incr; } static void emit_stmt_inter_delay(ivl_scope_t scope, ivl_statement_t stmt) { ivl_expr_t delay = ivl_stmt_delay_expr(stmt); unsigned nevents = ivl_stmt_nevent(stmt); if (nevents) { ivl_expr_t count = ivl_stmt_cond_expr(stmt); if (count) { if (ivl_expr_type(count) == IVL_EX_ULONG) { unsigned long repeat = ivl_expr_uvalue(count); if (repeat != 1) { fprintf(vlog_out, "repeat(%lu) ", repeat); } } else { fprintf(vlog_out, "repeat("); emit_expr(scope, count, 0, 0, 0, 0); fprintf(vlog_out, ") "); } } assert(delay == 0); fprintf(vlog_out, "@("); emit_event(scope, stmt); fprintf(vlog_out, ") "); } if (delay) { assert(nevents == 0); fprintf(vlog_out, "#("); emit_scaled_delayx(scope, delay, 1); fprintf(vlog_out, ") "); } } static void emit_stmt_lval_name(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig) { ivl_expr_t array_idx = ivl_lval_idx(lval); emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); if (array_idx) { int msb, lsb; assert(ivl_signal_dimensions(sig)); fprintf(vlog_out, "["); /* For an array the LSB/MSB order is not important. They are * always accessed from base counting up. */ lsb = ivl_signal_array_base(sig); msb = lsb + ivl_signal_array_count(sig) - 1; emit_scaled_expr(scope, array_idx, msb, lsb); fprintf(vlog_out, "]"); } } static void emit_stmt_lval_packed(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig, ivl_expr_t sel_expr, unsigned wid) { unsigned idx; assert(wid > 0); fprintf(vlog_out, "{"); for (idx = wid - 1; idx > 0; idx -= 1) { emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_expr(scope, sel_expr, 0, 0, 0, 1); fprintf(vlog_out, " + %u], ", idx); } emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_expr(scope, sel_expr, 0, 0, 0, 1); fprintf(vlog_out, "]}"); } static void emit_stmt_lval_ips(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig, ivl_expr_t sel_expr, ivl_select_type_t sel_type, unsigned wid, int msb, int lsb) { unsigned idx; assert(wid > 0); fprintf(vlog_out, "{"); if (msb >= lsb) { if (sel_type == IVL_SEL_IDX_DOWN) { lsb += wid - 1; msb += wid - 1; emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); for (idx = 1; idx < wid; idx += 1) { fprintf(vlog_out, ", "); emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " - %u]", idx); } fprintf(vlog_out, "}"); } else { assert(sel_type == IVL_SEL_IDX_UP); for (idx = wid - 1; idx > 0; idx -= 1) { emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " + %u], ", idx); } emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]}"); } } else { if (sel_type == IVL_SEL_IDX_UP) { lsb -= wid - 1; msb -= wid - 1; emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); for (idx = 1; idx < wid; idx += 1) { fprintf(vlog_out, ", "); emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " + %u]", idx); } fprintf(vlog_out, "}"); } else { assert(sel_type == IVL_SEL_IDX_DOWN); for (idx = wid - 1; idx > 0; idx -= 1) { emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, " - %u], ", idx); } emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]}"); } } } /* * Dynamic arrays are not supported in vlog95, but this assignment can be * translated correctly. */ static void emit_stmt_lval_darray(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig) { ivl_expr_t idx = ivl_lval_idx(lval); emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); if (idx) { fprintf(vlog_out, "["); emit_expr(scope, idx, 0, 0, 0, 1); fprintf(vlog_out, "]"); } } /* * Class or class properties are not supported in vlog95, but this assignment * can be translated correctly. */ static ivl_type_t emit_stmt_lval_class(ivl_scope_t scope, ivl_lval_t lval) { ivl_lval_t nest = ivl_lval_nest(lval); ivl_signal_t sig = ivl_lval_sig(lval); ivl_type_t type; int idx = ivl_lval_property_idx(lval); if (nest) { type = emit_stmt_lval_class(scope, nest); assert(type); } else { assert(sig); emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); type = ivl_signal_net_type(sig); } if (idx >= 0) { fprintf(vlog_out, ".%s", ivl_type_prop_name(type, idx)); return ivl_type_prop_type(type, idx); } else return 0; } static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t sel_expr; ivl_select_type_t sel_type; unsigned width = ivl_lval_width(lval); int msb, lsb; assert(width > 0); /* A class supports a nested L-value so it may not have a signal * at this level. */ if (! sig) { (void) emit_stmt_lval_class(scope, lval); return; } switch (ivl_signal_data_type(sig)) { case IVL_VT_DARRAY: emit_stmt_lval_darray(scope, lval, sig); return; case IVL_VT_CLASS: (void) emit_stmt_lval_class(scope, lval); return; default: break; } /* If there are no selects then just print the name. */ sel_expr = ivl_lval_part_off(lval); if (! sel_expr && (width == ivl_signal_width(sig))) { emit_stmt_lval_name(scope, lval, sig); return; } /* We have some kind of select. */ get_sig_msb_lsb(sig, &msb, &lsb); sel_type = ivl_lval_sel_type(lval); assert(sel_expr); /* A bit select. */ if (width == 1) { emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); } else if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { /* A constant part select. */ emit_stmt_lval_name(scope, lval, sig); emit_scaled_range(scope, sel_expr, width, msb, lsb); } else if (sel_type == IVL_SEL_OTHER) { assert(lsb == 0); assert(msb >= 0); emit_stmt_lval_packed(scope, lval, sig, sel_expr, width); } else { /* An indexed part select. */ emit_stmt_lval_ips(scope, lval, sig, sel_expr, sel_type, width, msb, lsb); } } static unsigned emit_stmt_lval(ivl_scope_t scope, ivl_statement_t stmt) { unsigned count = ivl_stmt_lvals(stmt); unsigned wid = 0; if (count > 1) { unsigned idx; ivl_lval_t lval; fprintf(vlog_out, "{"); for (idx = count - 1; idx > 0; idx -= 1) { lval = ivl_stmt_lval(stmt, idx); wid += ivl_lval_width(lval); emit_stmt_lval_piece(scope, lval); fprintf(vlog_out, ", "); } lval = ivl_stmt_lval(stmt, 0); wid += ivl_lval_width(lval); emit_stmt_lval_piece(scope, lval); fprintf(vlog_out, "}"); } else { ivl_lval_t lval = ivl_stmt_lval(stmt, 0); wid = ivl_lval_width(lval); emit_stmt_lval_piece(scope, lval); } return wid; } /* * Icarus translated = into * begin * = ; * = ; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ static unsigned is_delayed_or_event_assign(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; ivl_statement_t assign, delay, delayed_assign; ivl_statement_type_t delay_type; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; /* We must have two block elements. */ if (ivl_stmt_block_count(stmt) != 2) return 0; /* The first must be an assign. */ assign = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a delayx. */ delay = ivl_stmt_block_stmt(stmt, 1); delay_type = ivl_statement_type(delay); if ((delay_type != IVL_ST_DELAYX) && (delay_type != IVL_ST_WAIT)) return 0; /* The statement for the delayx must be an assign. */ delayed_assign = ivl_stmt_sub_stmt(delay); if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(assign) != 1) return 0; lval = ivl_stmt_lval(assign, 0); /* It must not have an array select. */ if (ivl_lval_idx(lval)) return 0; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return 0; lsig = ivl_lval_sig(lval); /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; /* The R-value must be a single signal. */ rval = ivl_stmt_rval(delayed_assign); if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0; /* It must not be an array word. */ if (ivl_expr_oper1(rval)) return 0; rsig = ivl_expr_signal(rval); /* The two signals must be the same. */ if (lsig != rsig) return 0; /* And finally the three statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delay)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delayed_assign))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*c", get_indent(), ' '); wid = emit_stmt_lval(scope, delayed_assign); fprintf(vlog_out, " = "); if (delay_type == IVL_ST_DELAYX) { fprintf(vlog_out, "#("); emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay), 1); } else { fprintf(vlog_out, "@("); emit_event(scope, delay); } fprintf(vlog_out, ") "); emit_expr(scope, ivl_stmt_rval(assign), wid, 1, 0, 0); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); return 1; } /* * A common routine to emit the basic assignment construct. It can also * translate an assignment with an opcode when allowed. */ static void emit_assign_and_opt_opcode(ivl_scope_t scope, ivl_statement_t stmt, unsigned allow_opcode) { unsigned wid; char opcode; const char *opcode_str; assert (ivl_statement_type(stmt) == IVL_ST_ASSIGN); // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, stmt); /* Get the opcode and the string version of the opcode. */ opcode = ivl_stmt_opcode(stmt); switch (opcode) { case 0: opcode_str = ""; break; case '+': opcode_str = "+"; break; case '-': opcode_str = "-"; break; case '*': opcode_str = "*"; break; case '/': opcode_str = "/"; break; case '%': opcode_str = "%"; break; case '&': opcode_str = "&"; break; case '|': opcode_str = "|"; break; case '^': opcode_str = "^"; break; case 'l': opcode_str = "<<"; break; case 'r': opcode_str = ">>"; break; case 'R': opcode_str = ">>>"; break; default: fprintf(stderr, "%s:%u: vlog95 error: unknown assignment operator " "(%c).\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt), opcode); vlog_errors += 1; opcode_str = ""; break; } if (opcode && ! allow_opcode) { fprintf(stderr, "%s:%u: vlog95 error: assignment operator %s= is " "not allowed in this context.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt), opcode_str); vlog_errors += 1; } fprintf(vlog_out, " = "); if (opcode) { unsigned twid = emit_stmt_lval(scope, stmt); assert(twid == wid); fprintf(vlog_out, " %s ", opcode_str); /* The >>>= assignment operator is only allowed when the allow * signed flag is true. */ if ((! allow_signed) && (opcode == 'R')) { fprintf(stderr, "%s:%u: vlog95 error: >>>= operator is not " "supported.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; } } emit_expr(scope, ivl_stmt_rval(stmt), wid, 1, 0, 0); } /* * Icarus translated for(; ; ) into * * begin * ; * while () begin * * * end * end * This routine looks for this pattern and turns it back into the * appropriate for loop. */ static unsigned is_for_loop(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t assign, while_lp, while_blk, body, incr_assign; /* We must have two block elements. */ if (ivl_stmt_block_count(stmt) != 2) return 0; /* The first must be an assign. */ assign = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a while. */ while_lp = ivl_stmt_block_stmt(stmt, 1); if (ivl_statement_type(while_lp) != IVL_ST_WHILE) return 0; /* The while statement must be a block. */ while_blk = ivl_stmt_sub_stmt(while_lp); if (ivl_statement_type(while_blk) != IVL_ST_BLOCK) return 0; /* It must not be a named block. */ if (ivl_stmt_block_scope(while_blk)) return 0; /* It must have two elements. */ if (ivl_stmt_block_count(while_blk) != 2) return 0; /* The first block element (the body) can be anything. */ body = ivl_stmt_block_stmt(while_blk, 0); /* The second block element must be the increment assign. */ incr_assign = ivl_stmt_block_stmt(while_blk, 1); if (ivl_statement_type(incr_assign) != IVL_ST_ASSIGN) return 0; /* And finally the for statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_lp)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_blk)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(incr_assign))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*cfor(", get_indent(), ' '); /* Emit the initialization statement (no opcode is allowed). */ emit_assign_and_opt_opcode(scope, assign, 0); fprintf(vlog_out, "; "); /* Emit the condition. */ emit_expr(scope, ivl_stmt_cond_expr(while_lp), 0, 0, 0, 0); fprintf(vlog_out, "; "); /* Emit the increment statement (an opcode is allowed). */ emit_assign_and_opt_opcode(scope, incr_assign, 1); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); /* Now emit the body. */ single_indent = 1; emit_stmt(scope, body); return 1; } /* * Icarus translated = repeat() into * begin * = ; * repeat() ; * = ; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ static unsigned is_repeat_event_assign(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; ivl_statement_t assign, event, event_assign, repeat; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; /* We must have three block elements. */ if (ivl_stmt_block_count(stmt) != 3) return 0; /* The first must be an assign. */ assign = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a repeat with an event or an event. */ repeat = ivl_stmt_block_stmt(stmt, 1); if (ivl_statement_type(repeat) != IVL_ST_REPEAT) return 0; /* The repeat must have an event statement. */ event = ivl_stmt_sub_stmt(repeat); if (ivl_statement_type(event) != IVL_ST_WAIT) return 0; /* The third must be an assign. */ event_assign = ivl_stmt_block_stmt(stmt, 2); if (ivl_statement_type(event_assign) != IVL_ST_ASSIGN) return 0; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(assign) != 1) return 0; lval = ivl_stmt_lval(assign, 0); /* It must not have an array select. */ if (ivl_lval_idx(lval)) return 0; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return 0; lsig = ivl_lval_sig(lval); /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; /* The R-value must be a single signal. */ rval = ivl_stmt_rval(event_assign); if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0; /* It must not be an array word. */ if (ivl_expr_oper1(rval)) return 0; rsig = ivl_expr_signal(rval); /* The two signals must be the same. */ if (lsig != rsig) return 0; /* And finally the four statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(repeat)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event_assign))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*c", get_indent(), ' '); wid = emit_stmt_lval(scope, event_assign); fprintf(vlog_out, " ="); if (repeat) { fprintf(vlog_out, " repeat ("); emit_expr(scope, ivl_stmt_cond_expr(repeat), 0, 0, 0, 0); fprintf(vlog_out, ")"); } fprintf(vlog_out, " @("); emit_event(scope, event); fprintf(vlog_out, ") "); emit_expr(scope, ivl_stmt_rval(assign), wid, 1, 0, 0); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); return 1; } /* * Icarus translated wait( into * begin * while ( !== 1'b1) @(); * * end * This routine looks for this pattern and turns it back into a * wait statement. */ static unsigned is_wait(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t while_wait, wait, wait_stmt; ivl_expr_t while_expr, expr; const char *bits; /* We must have two block elements. */ if (ivl_stmt_block_count(stmt) != 2) return 0; /* The first must be a while. */ while_wait = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(while_wait) != IVL_ST_WHILE) return 0; /* That has a wait with a NOOP statement. */ wait = ivl_stmt_sub_stmt(while_wait); if (ivl_statement_type(wait) != IVL_ST_WAIT) return 0; wait_stmt = ivl_stmt_sub_stmt(wait); if (ivl_statement_type(wait_stmt) != IVL_ST_NOOP) return 0; /* Check that the while condition has the correct form. */ while_expr = ivl_stmt_cond_expr(while_wait); if (ivl_expr_type(while_expr) != IVL_EX_BINARY) return 0; if (ivl_expr_opcode(while_expr) != 'N') return 0; /* Has a second operator that is a constant 1'b1. */ expr = ivl_expr_oper2(while_expr); if (ivl_expr_type(expr) != IVL_EX_NUMBER) return 0; if (ivl_expr_width(expr) != 1) return 0; bits = ivl_expr_bits(expr); if (*bits != '1') return 0; // HERE: There is no easy way to verify that the @ sensitivity list // matches the first expression so we don't check for that yet. /* And finally the two statements that represent the wait must * have the same line number as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_wait)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(wait))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*cwait(", get_indent(), ' '); emit_expr(scope, ivl_expr_oper1(while_expr), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_block_stmt(stmt, 1)); return 1; } /* * Check to see if the statement L-value is a port in the given scope. * If it is return the zero based port number. */ static unsigned utask_in_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); ivl_lval_t lval = ivl_stmt_lval(stmt, 0); ivl_signal_t lsig = ivl_lval_sig(lval); const char *sig_name; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(stmt) != 1) return ports; /* It must not have an array select. */ if (ivl_lval_idx(lval)) return ports; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return ports; /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return ports; /* It must have the same scope as the task. */ if (scope != ivl_signal_scope(lsig)) return ports; /* It must be an input or inout port of the task. */ sig_name = ivl_signal_basename(lsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_INPUT) && (port_type != IVL_SIP_INOUT)) continue; if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; } return idx; } /* * Check to see if the statement R-value is a port in the given scope. * If it is return the zero based port number. */ static unsigned utask_out_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); ivl_expr_t rval = ivl_stmt_rval(stmt); ivl_signal_t rsig = 0; ivl_expr_type_t expr_type = ivl_expr_type(rval); const char *sig_name; /* We can have a simple signal. */ if (expr_type == IVL_EX_SIGNAL) { rsig = ivl_expr_signal(rval); /* Or a simple select of a simple signal. */ } else if (expr_type == IVL_EX_SELECT) { ivl_expr_t expr = ivl_expr_oper1(rval); /* We must have a zero select base. */ if (ivl_expr_oper2(rval)) return ports; /* We must be selecting a signal. */ if (ivl_expr_type(expr) != IVL_EX_SIGNAL) return ports; rsig = ivl_expr_signal(expr); /* Or a cast of a simple signal. */ } else if (expr_type == IVL_EX_UNARY) { ivl_expr_t expr = ivl_expr_oper1(rval); char opcode = ivl_expr_opcode(rval); /* This must be a cast opcode. */ if ((opcode != '2') && (opcode != 'v') && (opcode != 'r')) return ports; /* We must be casting a signal. */ if (ivl_expr_type(expr) != IVL_EX_SIGNAL) return ports; rsig = ivl_expr_signal(expr); } else return ports; /* The R-value must have the same scope as the task. */ if (scope != ivl_signal_scope(rsig)) return ports; /* It must not be an array element. */ if (ivl_signal_dimensions(rsig)) return ports; /* It must be an output or inout port of the task. */ sig_name = ivl_signal_basename(rsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_OUTPUT) && (port_type != IVL_SIP_INOUT)) continue; if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; } return idx; } /* * Structure to hold the port information as we extract it from the block. */ typedef struct port_expr_s { ivl_signal_port_t type; union { ivl_statement_t lval; ivl_expr_t rval; } expr; } *port_expr_t; /* * An input prints the R-value and an output or inout print the L-value. */ static void emit_port(ivl_scope_t scope, struct port_expr_s port_expr) { if (port_expr.type == IVL_SIP_INPUT) { // HERE: For a user should the argument width be used here. emit_expr(scope, port_expr.expr.rval, 0, 1, 0, 0); } else { /* This is a self-determined context so we don't care about * the width of the L-value. */ (void) emit_stmt_lval(scope, port_expr.expr.lval); } } /* * Icarus encodes a user task call with arguments as: * begin * = * ... * = * * = * ... * = * end * This routine looks for that pattern and translates it into the * appropriate task call. It returns true (1) if it successfully * translated the block to a task call, otherwise it returns false * (0) to indicate the block needs to be emitted. * * When calling automatic tasks there is an initial ALLOC statement * and a final FREE statement. */ static unsigned is_utask_call_with_args(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports, task_idx = 0; unsigned count = ivl_stmt_block_count(stmt); unsigned lineno = ivl_stmt_lineno(stmt); unsigned start, stop, is_auto = 0; ivl_scope_t task_scope = 0; port_expr_t port_exprs; /* Check to see if the block is of the basic form first. */ for (idx = 0; idx < count; idx += 1) { ivl_statement_t tmp = ivl_stmt_block_stmt(stmt, idx); /* For an automatic task the ALLOC must be first. */ if (ivl_statement_type(tmp) == IVL_ST_ALLOC) { if (idx == 0) { is_auto = 1; continue; } } if (ivl_statement_type(tmp) == IVL_ST_ASSIGN) continue; if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { task_idx = idx; task_scope = ivl_stmt_call(tmp); assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); continue; } /* For an automatic task the FREE must be last. */ if (ivl_statement_type(tmp) == IVL_ST_FREE) { if (idx == count-1) { if (is_auto) continue; } } return 0; } /* If there is no task call or it takes no argument then return. */ if (!task_scope) return 0; ports = ivl_scope_ports(task_scope); if (ports == 0) return 0; /* Allocate space to save the port information and initialize it. */ port_exprs = (port_expr_t) malloc(sizeof(struct port_expr_s)*ports); for (idx = 0; idx < ports; idx += 1) { port_exprs[idx].type = IVL_SIP_NONE; port_exprs[idx].expr.rval = 0; } /* Check that the input arguments are correct. */ if (is_auto) start = 1; else start = 0; for (idx = start; idx < task_idx; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_in_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } port_exprs[port].type = IVL_SIP_INPUT; port_exprs[port].expr.rval = ivl_stmt_rval(assign); } /* Check that the output arguments are correct. */ if (is_auto) stop = count-1; else stop = count; for (idx = task_idx + 1; idx < stop; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_out_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } if (port_exprs[port].type == IVL_SIP_INPUT) { port_exprs[port].type = IVL_SIP_INOUT; // HERE: We probably should verify that the current R-value matches the // new L-value. } else { port_exprs[port].type = IVL_SIP_OUTPUT; } port_exprs[port].expr.lval = assign; } /* Check that the task call has the correct line number. */ if (lineno != ivl_stmt_lineno(ivl_stmt_block_stmt(stmt, task_idx))) { free(port_exprs); return 0; } /* Verify that all the ports were defined. */ for (idx = 0; idx < ports; idx += 1) { if (port_exprs[idx].type == IVL_SIP_NONE) { free(port_exprs); return 0; } } /* Now that we have the arguments figured out, print the task call. */ fprintf(vlog_out, "%*c", get_indent(), ' '); emit_scope_path(scope, task_scope); fprintf(vlog_out, "("); emit_port(scope, port_exprs[0]); for (idx = 1; idx < ports; idx += 1) { fprintf(vlog_out, ", "); emit_port(scope, port_exprs[idx]); } free(port_exprs); fprintf(vlog_out, ");"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); return 1; } static void emit_stmt_assign(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c", get_indent(), ' '); /* Emit the basic assignment (an opcode is allowed).*/ emit_assign_and_opt_opcode(scope, stmt, 1); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_assign_nb(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; fprintf(vlog_out, "%*c", get_indent(), ' '); // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, stmt); fprintf(vlog_out, " <= "); emit_stmt_inter_delay(scope, stmt); emit_expr(scope, ivl_stmt_rval(stmt), wid, 1, 0, 0); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_block(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*cbegin", get_indent(), ' '); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); emit_stmt_block_body(scope, stmt); fprintf(vlog_out, "%*cend\n", get_indent(), ' '); } static void emit_stmt_block_named(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t my_scope = ivl_stmt_block_scope(stmt); fprintf(vlog_out, "%*cbegin: ", get_indent(), ' '); emit_id(ivl_scope_basename(my_scope)); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); emit_stmt_block_body(scope, stmt); fprintf(vlog_out, "%*cend /* %s */\n", get_indent(), ' ', ivl_scope_basename(my_scope)); } static void emit_stmt_case(ivl_scope_t scope, ivl_statement_t stmt) { const char *case_type = 0; unsigned idx, default_case, count = ivl_stmt_case_count(stmt); switch (ivl_statement_type(stmt)) { case IVL_ST_CASE: case IVL_ST_CASER: case_type = "case"; break; case IVL_ST_CASEX: case_type = "casex"; break; case IVL_ST_CASEZ: case_type = "casez"; break; default: assert(0); } fprintf(vlog_out, "%*c%s (", get_indent(), ' ', case_type); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); indent += indent_incr; default_case = count; for (idx = 0; idx < count; idx += 1) { ivl_expr_t expr = ivl_stmt_case_expr(stmt, idx); /* This is the default case so emit it last. */ if (expr == 0) { assert(default_case == count); default_case = idx; continue; } fprintf(vlog_out, "%*c", get_indent(), ' '); emit_expr(scope, expr, 0, 0, 0, 0); fprintf(vlog_out, ":"); single_indent = 1; emit_stmt(scope, ivl_stmt_case_stmt(stmt, idx)); } if (default_case < count) { fprintf(vlog_out, "%*cdefault:", get_indent(), ' '); single_indent = 1; emit_stmt(scope, ivl_stmt_case_stmt(stmt, default_case)); } assert(indent >= indent_incr); indent -= indent_incr; fprintf(vlog_out, "%*cendcase\n", get_indent(), ' '); } static void emit_stmt_cassign(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; fprintf(vlog_out, "%*cassign ", get_indent(), ' '); // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, stmt); fprintf(vlog_out, " = "); emit_expr(scope, ivl_stmt_rval(stmt), wid, 1, 0, 0); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_condit(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t true_stmt = ivl_stmt_cond_true(stmt); ivl_statement_t false_stmt = ivl_stmt_cond_false(stmt); unsigned nest = 0; fprintf(vlog_out, "%*cif (", get_indent(), ' '); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); if (true_stmt) { /* If we have a false statement and the true statement is a * condition that does not have a false clause then we need * to add a begin/end pair to keep the else clause attached * to this condition. */ if (false_stmt && (ivl_statement_type(true_stmt) == IVL_ST_CONDIT) && (! ivl_stmt_cond_false(true_stmt))) nest = 1; if (nest) { fprintf(vlog_out, " begin\n"); indent += indent_incr; } else single_indent = 1; emit_stmt(scope, true_stmt); } else { fprintf(vlog_out, ";\n"); } if (false_stmt) { if (nest) { assert(indent >= indent_incr); indent -= indent_incr; } fprintf(vlog_out, "%*c", get_indent(), ' '); if (nest) fprintf(vlog_out, "end "); fprintf(vlog_out, "else"); single_indent = 1; emit_stmt(scope, false_stmt); } } static void emit_stmt_deassign(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*cdeassign ", get_indent(), ' '); (void) emit_stmt_lval(scope, stmt); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_delay(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c#", get_indent(), ' '); emit_scaled_delay(scope, ivl_stmt_delay_val(stmt)); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); } static void emit_stmt_delayx(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c#(", get_indent(), ' '); emit_scaled_delayx(scope, ivl_stmt_delay_expr(stmt), 1); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); } static unsigned is_func_disable(ivl_scope_t scope, ivl_scope_t disable_scope) { assert(func_rtn_name); /* Find the enclosing function scope. */ while (ivl_scope_type(scope) != IVL_SCT_FUNCTION) { scope = ivl_scope_parent(scope); assert(scope); } /* If the function scope and the scope to be disabled match then this * is a function disable (SystemVerilog return). */ return scope == disable_scope; } static void emit_stmt_disable(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t disable_scope = ivl_stmt_call(stmt); fprintf(vlog_out, "%*cdisable ", get_indent(), ' '); /* A normal disable statement. */ if (disable_scope) { /* If this disable is in a function and it is disabling the * function then emit the appropriate function return name. */ if (func_rtn_name && is_func_disable(scope, disable_scope)) { fprintf(vlog_out, "%s", func_rtn_name); } else emit_scope_path(scope, disable_scope); /* A SystemVerilog disable fork statement cannot be converted. */ } else { fprintf(vlog_out, "fork"); fprintf(stderr, "%s:%u: vlog95 sorry: disable fork is not " "currently translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; } fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } /* * Emit just the statements for this named block since an outer named block * was added to keep all the translated code inside a single named block. */ static void emit_stmt_do_while_body(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, count = ivl_stmt_block_count(stmt); unsigned is_begin = 0; assert(ivl_stmt_block_scope(stmt)); switch (ivl_statement_type(stmt)) { case IVL_ST_BLOCK: fprintf(vlog_out, "%*cbegin", get_indent(), ' '); is_begin = 1; break; case IVL_ST_FORK_JOIN_ANY: fprintf(stderr, "%s:%u: vlog95 sorry: fork/join_any is not " "currently translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; return; case IVL_ST_FORK_JOIN_NONE: fprintf(stderr, "%s:%u: vlog95 sorry: fork/join_none is not " "currently translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; return; case IVL_ST_FORK: fprintf(vlog_out, "%*cfork", get_indent(), ' '); break; /* Only named blocks should make it to this code. */ default: assert(0); break; } emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); indent += indent_incr; for (idx = 0; idx < count; idx += 1) { emit_stmt(scope, ivl_stmt_block_stmt(stmt, idx)); } assert(indent >= indent_incr); indent -= indent_incr; if (is_begin) fprintf(vlog_out, "%*cend\n", get_indent(), ' '); else fprintf(vlog_out, "%*cjoin\n", get_indent(), ' '); } /* * Currently a do/while can be translated in two ways: no named blocks in * the do/while statement and only a named block as the top level statement. * Anything else cannot be translated since a hierarchical variable reference * or disable cannot work correctly when the statement is duplicated. */ typedef enum stmt_named_type_e { NO_NAMED = 0, TOP_NAMED = 1, OTHER_NAMED = 2 } stmt_named_type_t; static stmt_named_type_t get_named_type(ivl_statement_t stmt, unsigned is_top); /* * A block can start as either a NO_NAMED or a TOP_NAMED so pass this in. */ static stmt_named_type_t get_named_type_block(ivl_statement_t stmt, stmt_named_type_t def_type) { stmt_named_type_t rtn = def_type; unsigned idx, count = ivl_stmt_block_count(stmt); for (idx = 0; idx < count; idx += 1) { stmt_named_type_t lrtn; lrtn = get_named_type(ivl_stmt_block_stmt(stmt, idx), 0); if (lrtn > rtn) rtn = lrtn; if (rtn == OTHER_NAMED) break; } return rtn; } static stmt_named_type_t get_named_type_case(ivl_statement_t stmt) { stmt_named_type_t rtn = NO_NAMED; unsigned idx, count = ivl_stmt_case_count(stmt); for (idx = 0; idx < count; idx += 1) { stmt_named_type_t lrtn; lrtn = get_named_type(ivl_stmt_case_stmt(stmt, idx), 0); if (lrtn > rtn) rtn = lrtn; if (rtn == OTHER_NAMED) break; } return rtn; } static stmt_named_type_t get_named_type_condit(ivl_statement_t stmt) { stmt_named_type_t true_rtn, false_rtn; true_rtn = get_named_type(ivl_stmt_cond_true(stmt), 0); false_rtn = get_named_type(ivl_stmt_cond_false(stmt), 0); if (false_rtn > true_rtn) return false_rtn; return true_rtn; } static stmt_named_type_t get_named_type(ivl_statement_t stmt, unsigned is_top) { stmt_named_type_t rtn = NO_NAMED; if (! stmt) return NO_NAMED; switch (ivl_statement_type(stmt)) { case IVL_ST_BLOCK: case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: /* Check to see if this is a named top block or sub block. */ if (ivl_stmt_block_scope(stmt)) { if (is_top) rtn = TOP_NAMED; else return OTHER_NAMED; } rtn = get_named_type_block(stmt, rtn); break; case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: rtn = get_named_type_case(stmt); break; case IVL_ST_CONDIT: rtn = get_named_type_condit(stmt); break; /* These statements only have a single sub-statement that is not * a top level statement relative to the original do/while. */ case IVL_ST_DELAY: case IVL_ST_DELAYX: case IVL_ST_DO_WHILE: case IVL_ST_FOREVER: case IVL_ST_REPEAT: case IVL_ST_WAIT: case IVL_ST_WHILE: rtn = get_named_type(ivl_stmt_sub_stmt(stmt), 0); break; default: /* The rest of the statement types do not have sub-statements. */ break; } return rtn; } /* * Translate a do/while to the following: * * statement * while (expr) statement */ static void emit_stmt_do_while(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt); stmt_named_type_t named_type = get_named_type(sub_stmt, 1); /* If just the original do/while statement is named then emit it as: * * begin : name * * statement * while (expr) statement * end */ if (named_type == TOP_NAMED) { ivl_scope_t my_scope = ivl_stmt_block_scope(sub_stmt); assert(my_scope); fprintf(vlog_out, "%*cbegin: ", get_indent(), ' '); emit_id(ivl_scope_basename(my_scope)); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); indent += indent_incr; emit_scope_variables(my_scope); emit_stmt_do_while_body(my_scope, sub_stmt); fprintf(vlog_out, "%*cwhile (", get_indent(), ' '); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt_do_while_body(my_scope, sub_stmt); assert(indent >= indent_incr); indent -= indent_incr; fprintf(vlog_out, "%*cend /* %s */\n", get_indent(), ' ', ivl_scope_basename(my_scope)); } else { emit_stmt(scope, sub_stmt); fprintf(vlog_out, "%*cwhile (", get_indent(), ' '); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, sub_stmt); } /* * If the do/while has sub-statements that are named it cannot be * translated since the original do/while statement needs to be * duplicated and doing this will create two statements with the * same name. */ if (named_type == OTHER_NAMED) { fprintf(stderr, "%s:%u: vlog95 sorry: do/while with named " "sub-statements cannot be translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; } } static void emit_stmt_force(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; fprintf(vlog_out, "%*cforce ", get_indent(), ' '); // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, stmt); fprintf(vlog_out, " = "); emit_expr(scope, ivl_stmt_rval(stmt), wid, 1, 0, 0); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_forever(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*cforever", get_indent(), ' '); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); } static void emit_stmt_fork(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*cfork", get_indent(), ' '); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); emit_stmt_block_body(scope, stmt); fprintf(vlog_out, "%*cjoin\n", get_indent(), ' '); } static void emit_stmt_fork_named(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t my_scope = ivl_stmt_block_scope(stmt); fprintf(vlog_out, "%*cfork: ", get_indent(), ' '); emit_id(ivl_scope_basename(my_scope)); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); emit_stmt_block_body(scope, stmt); fprintf(vlog_out, "%*cjoin /* %s */\n", get_indent(), ' ', ivl_scope_basename(my_scope)); } static void emit_stmt_release(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*crelease ", get_indent(), ' '); (void) emit_stmt_lval(scope, stmt); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_repeat(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*crepeat (", get_indent(), ' '); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); } static void emit_stmt_stask(ivl_scope_t scope, ivl_statement_t stmt) { unsigned count = ivl_stmt_parm_count(stmt); fprintf(vlog_out, "%*c%s", get_indent(), ' ', ivl_stmt_name(stmt)); if (count != 0) { unsigned idx; ivl_expr_t expr; fprintf(vlog_out, "("); count -= 1; for (idx = 0; idx < count; idx += 1) { expr = ivl_stmt_parm(stmt, idx); if (expr) emit_expr(scope, expr, 0, 0, 0, 0); fprintf(vlog_out, ", "); } expr = ivl_stmt_parm(stmt, count); if (expr) emit_expr(scope, expr, 0, 0, 0, 0); fprintf(vlog_out, ")"); } fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } static void emit_stmt_trigger(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c-> ", get_indent(), ' '); assert(ivl_stmt_nevent(stmt) == 1); emit_event(scope, stmt); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } /* * A user defined task call with arguments is generated as a block with * input assignments, a simple call and then output assignments. This is * handled by the is_utask_call_with_args() routine above. */ static void emit_stmt_utask(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t task_scope = ivl_stmt_call(stmt); assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); assert(ivl_scope_ports(task_scope) == 0); fprintf(vlog_out, "%*c", get_indent(), ' '); emit_scope_path(scope, task_scope); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); } /* Look to see if this is a SystemVerilog wait fork statement. */ static unsigned is_wait_fork(ivl_scope_t scope, ivl_statement_t stmt) { (void)scope; /* Parameter is not used. */ if (ivl_stmt_nevent(stmt) != 1) return 0; if (ivl_stmt_events(stmt, 0) != 0) return 0; assert(ivl_statement_type(ivl_stmt_sub_stmt(stmt)) == IVL_ST_NOOP); fprintf(vlog_out, "%*cwait fork;", get_indent(), ' '); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); fprintf(stderr, "%s:%u: vlog95 sorry: wait fork is not currently " "translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; return 1; } static void emit_stmt_wait(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c@(", get_indent(), ' '); emit_event(scope, stmt); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); } static void emit_stmt_while(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*cwhile (", get_indent(), ' '); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0, 0, 0, 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); } void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) { switch (ivl_statement_type(stmt)) { case IVL_ST_NOOP: /* If this is a statement termination then just finish the * statement, otherwise print an empty begin/end pair. */ if (single_indent) { single_indent = 0; fprintf(vlog_out, ";\n"); } else { fprintf(vlog_out, "%*cbegin\n", get_indent(), ' '); fprintf(vlog_out, "%*cend\n", get_indent(), ' '); } break; case IVL_ST_ALLOC: /* This statement is only used with an automatic task so we * can safely skip it. The automatic task definition will * generate an appropriate error message.*/ break; case IVL_ST_ASSIGN: emit_stmt_assign(scope, stmt); break; case IVL_ST_ASSIGN_NB: emit_stmt_assign_nb(scope, stmt); break; case IVL_ST_BLOCK: if (ivl_stmt_block_scope(stmt)) { emit_stmt_block_named(scope, stmt); } else { if (is_delayed_or_event_assign(scope, stmt)) break; if (is_for_loop(scope, stmt)) break; if (is_repeat_event_assign(scope, stmt)) break; if (is_wait(scope, stmt)) break; if (is_utask_call_with_args(scope, stmt)) break; emit_stmt_block(scope, stmt); } break; case IVL_ST_CASE: case IVL_ST_CASER: case IVL_ST_CASEX: case IVL_ST_CASEZ: emit_stmt_case(scope, stmt); break; case IVL_ST_CASSIGN: emit_stmt_cassign(scope, stmt); break; case IVL_ST_CONDIT: emit_stmt_condit(scope, stmt); break; case IVL_ST_DEASSIGN: emit_stmt_deassign(scope, stmt); break; case IVL_ST_DELAY: emit_stmt_delay(scope, stmt); break; case IVL_ST_DELAYX: emit_stmt_delayx(scope, stmt); break; case IVL_ST_DISABLE: emit_stmt_disable(scope, stmt); break; case IVL_ST_DO_WHILE: emit_stmt_do_while(scope, stmt); break; case IVL_ST_FORCE: emit_stmt_force(scope, stmt); break; case IVL_ST_FOREVER: emit_stmt_forever(scope, stmt); break; case IVL_ST_FORK: if (ivl_stmt_block_scope(stmt)) { emit_stmt_fork_named(scope, stmt); } else { emit_stmt_fork(scope, stmt); } break; case IVL_ST_FORK_JOIN_ANY: fprintf(stderr, "%s:%u: vlog95 sorry: fork/join_any is not " "currently translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; break; case IVL_ST_FORK_JOIN_NONE: fprintf(stderr, "%s:%u: vlog95 sorry: fork/join_none is not " "currently translated.\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); vlog_errors += 1; break; case IVL_ST_FREE: /* This statement is only used with an automatic task so we * can safely skip it. The automatic task definition will * generate an appropriate error message.*/ break; case IVL_ST_RELEASE: emit_stmt_release(scope, stmt); break; case IVL_ST_REPEAT: emit_stmt_repeat(scope, stmt); break; case IVL_ST_STASK: emit_stmt_stask(scope, stmt); break; case IVL_ST_TRIGGER: emit_stmt_trigger(scope, stmt); break; case IVL_ST_UTASK: emit_stmt_utask(scope, stmt); break; case IVL_ST_WAIT: if (is_wait_fork(scope, stmt)) break; emit_stmt_wait(scope, stmt); break; case IVL_ST_WHILE: emit_stmt_while(scope, stmt); break; default: fprintf(vlog_out, "%*c;\n", get_indent(), ' '); fprintf(stderr, "%s:%u: vlog95 error: Unknown statement " "type (%d).\n", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt), (int)ivl_statement_type(stmt)); vlog_errors += 1; break; } } void emit_process(ivl_scope_t scope, ivl_process_t proc) { ivl_statement_t stmt = ivl_process_stmt(proc); if (ivl_statement_type(stmt) == IVL_ST_NOOP) return; fprintf(vlog_out, "\n%*c", get_indent(), ' '); switch (ivl_process_type(proc)) { case IVL_PR_INITIAL: fprintf(vlog_out, "initial"); break; case IVL_PR_ALWAYS: fprintf(vlog_out, "always"); break; case IVL_PR_FINAL: fprintf(vlog_out, "final"); fprintf(stderr, "%s:%u: vlog95 sorry: final blocks are not " "currently translated.\n", ivl_process_file(proc), ivl_process_lineno(proc)); vlog_errors+= 1; break; default: fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Unknown process type (%d).\n", ivl_process_file(proc), ivl_process_lineno(proc), (int)ivl_process_type(proc)); vlog_errors+= 1; break; } if (emit_file_line) { fprintf(vlog_out, " /* %s:%u */", ivl_process_file(proc), ivl_process_lineno(proc)); } if (ivl_statement_type(stmt) == IVL_ST_NOOP) { fprintf(vlog_out, " begin\n%*cend\n", get_indent(), ' '); } else { single_indent = 1; emit_stmt(scope, stmt); } } iverilog-10_1/tgt-vlog95/udp.c000066400000000000000000000155551265551621300162320ustar00rootroot00000000000000/* * Copyright (C) 2011 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include # include # include "config.h" # include "vlog95_priv.h" # include "ivl_alloc.h" static void emit_entry(ivl_udp_t udp, char entry, unsigned *rerun) { const char *value = 0; switch (entry) { case '0': value = " 0 "; break; case '1': value = " 1 "; break; case 'x': value = " x "; break; case '?': /* 0, 1 or x */ value = " ? "; break; case 'b': /* 0 or 1 */ value = " b "; break; case '-': /* Current value. */ value = " - "; break; case '*': /* (??) */ value = " * "; break; case 'r': /* (01) */ value = " r "; break; case 'f': /* (10) */ value = " f "; break; case 'p': /* (01), (0x) or (x1) */ value = " p "; break; case 'n': /* (10), (1x) or (x0) */ value = " n "; break; /* Icarus encodings/extensions. */ case 'h': if (*rerun) { value = " x "; } else { value = " 1 "; *rerun = 1; } break; case 'l': if (*rerun) { value = " x "; } else { value = " 0 "; *rerun = 1; } break; case 'q': value = "(bx)"; break; case 'B': value = "(x?)"; break; case 'F': value = "(x0)"; break; case 'M': value = "(1x)"; break; case 'N': value = "(1?)"; break; case 'P': value = "(0?)"; break; case 'Q': value = "(0x)"; break; case 'R': value = "(x1)"; break; case '%': /* This is effectively the same as q. */ value = "(?x)"; break; case '+': value = "(?1)"; break; case '_': value = "(?0)"; break; default: fprintf(stderr, "%s:%u: vlog95 error: Unknown primitive table " "entry %c in primitive %s.\n", ivl_udp_file(udp), ivl_udp_lineno(udp), entry, ivl_udp_name(udp)); vlog_errors += 1; value = " "; break; } fprintf(vlog_out, "%s ", value); } static void emit_sequ_table(ivl_udp_t udp) { unsigned idx, count = ivl_udp_rows(udp); for (idx = 0; idx < count; idx += 1) { const char *row = ivl_udp_row(udp, idx); unsigned need_to_rerun = 0; do { unsigned entry, inputs = ivl_udp_nin(udp); unsigned rerun, rerunning = need_to_rerun; need_to_rerun = 0; fprintf(vlog_out, "%*c", 2*indent_incr, ' '); for (entry = 1; entry <= inputs; entry += 1) { rerun = rerunning; emit_entry(udp, row[entry], &rerun); if (rerun && ! rerunning) need_to_rerun = rerun; } fprintf(vlog_out, ": "); /* The first entry is the current state. */ rerun = rerunning; emit_entry(udp, row[0], &rerun); if (rerun && ! rerunning) need_to_rerun = rerun; fprintf(vlog_out, ": "); /* The new value is after the inputs. */ rerun = rerunning; emit_entry(udp, row[inputs+1], &rerun); if (rerun && ! rerunning) need_to_rerun = rerun; fprintf(vlog_out, ";\n"); } while (need_to_rerun); } } static void emit_comb_table(ivl_udp_t udp) { unsigned idx, count = ivl_udp_rows(udp); for (idx = 0; idx < count; idx += 1) { const char *row = ivl_udp_row(udp, idx); unsigned need_to_rerun = 0; do { unsigned entry, inputs = ivl_udp_nin(udp); unsigned rerun, rerunning = need_to_rerun; need_to_rerun = 0; fprintf(vlog_out, "%*c", 2*indent_incr, ' '); for (entry = 0; entry < inputs; entry += 1) { rerun = rerunning; emit_entry(udp, row[entry], &rerun); if (rerun && ! rerunning) need_to_rerun = rerun; } /* The new value is after the inputs. */ fprintf(vlog_out, ": "); rerun = rerunning; emit_entry(udp, row[inputs], &rerun); if (rerun && ! rerunning) need_to_rerun = rerun; fprintf(vlog_out, ";\n"); } while (need_to_rerun); } } static void emit_udp(ivl_udp_t udp) { unsigned idx, count; fprintf(vlog_out, "\n"); fprintf(vlog_out, "/* This primitive was originally defined in " "file %s at line %u. */\n", ivl_udp_file(udp), ivl_udp_lineno(udp)); fprintf(vlog_out, "primitive "); emit_id(ivl_udp_name(udp)); fprintf(vlog_out, " ("); emit_id(ivl_udp_port(udp, 0)); count = ivl_udp_nin(udp); for (idx = 1; idx <= count; idx += 1) { fprintf(vlog_out, ", "); emit_id(ivl_udp_port(udp, idx)); } fprintf(vlog_out, ");\n"); fprintf(vlog_out, "%*coutput ", indent_incr, ' '); emit_id(ivl_udp_port(udp, 0)); fprintf(vlog_out, ";\n"); for (idx = 1; idx <= count; idx += 1) { fprintf(vlog_out, "%*cinput ", indent_incr, ' '); emit_id(ivl_udp_port(udp, idx)); fprintf(vlog_out, ";\n"); } if (ivl_udp_sequ(udp)) { char init = ivl_udp_init(udp); fprintf(vlog_out, "\n"); fprintf(vlog_out, "%*creg ", indent_incr, ' '); emit_id(ivl_udp_port(udp, 0)); fprintf(vlog_out, ";\n"); switch (init) { case '0': case '1': break; default: init = 'x'; break; } fprintf(vlog_out, "%*cinitial ", indent_incr, ' '); emit_id(ivl_udp_port(udp, 0)); fprintf(vlog_out, " = 1'b%c;\n", init); } fprintf(vlog_out, "\n"); fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); if (ivl_udp_sequ(udp)) emit_sequ_table(udp); else emit_comb_table(udp); fprintf(vlog_out, "%*cendtable\n", indent_incr, ' '); fprintf(vlog_out, "endprimitive /* %s */\n", ivl_udp_name(udp)); } static ivl_udp_t *udps = 0; static unsigned num_udps = 0; void add_udp_to_list(ivl_udp_t udp) { unsigned idx; /* Look to see if the UDP is already in the list. */ for (idx = 0; idx < num_udps; idx += 1) { if ((ivl_udp_lineno(udp) == ivl_udp_lineno(udps[idx])) && (strcmp(ivl_udp_name(udp), ivl_udp_name(udps[idx])) == 0)) { return; } } num_udps += 1; udps = realloc(udps, num_udps * sizeof(ivl_udp_t)); udps[num_udps-1] = udp; } void emit_udp_list() { unsigned idx; for (idx = 0; idx < num_udps; idx += 1) { emit_udp(udps[idx]); } free(udps); udps = 0; num_udps = 0; } iverilog-10_1/tgt-vlog95/vlog95-s.conf000066400000000000000000000001501265551621300175130ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules flag:DLL=vlog95.tgt flag:DISABLE_CONCATZ_GENERATION=true iverilog-10_1/tgt-vlog95/vlog95.c000066400000000000000000000212641265551621300165610ustar00rootroot00000000000000/* * Copyright (C) 2010-2013 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * * This is the vlog95 target module. It generates a 1364-1995 compliant * netlist from the input netlist. The generated netlist is expected to * be simulation equivalent to the original. */ # include "version_base.h" # include "version_tag.h" # include "config.h" # include "vlog95_priv.h" # include # include static const char*version_string = "Icarus Verilog VLOG95 Code Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (C) 2010-2015 Cary R. (cygcary@yahoo.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" ; FILE*vlog_out; int vlog_errors = 0; int sim_precision = 0; unsigned indent = 0; unsigned indent_incr = 2; unsigned emit_file_line = 0; unsigned allow_signed = 0; ivl_design_t design = 0; int target_design(ivl_design_t des) { ivl_scope_t *roots; unsigned nroots, idx; unsigned has_root_scope = 0; const char*path = ivl_design_flag(des, "-o"); /* Set the indent spacing with the -pspacing flag passed to iverilog * (e.g. -pspacing=4). The default is 2 spaces. */ const char*spacing_str = ivl_design_flag(des, "spacing"); /* Use -pfileline to determine if file and line information is * printed for most lines. (e.g. -pfileline=1). The default is no * file/line information will be printed for individual lines. */ const char*fileline_str = ivl_design_flag(des, "fileline"); /* Use -pallowsigned to allow signed registers/nets and the * $signed() and $unsigned() system tasks as an extension. */ const char*allowsigned_str = ivl_design_flag(des, "allowsigned"); assert(path); /* Check for and use a provided indent spacing. */ if (strcmp(spacing_str, "") != 0) { char *eptr; long value = strtol(spacing_str, &eptr, 0); /* Nothing usable in the spacing string. */ if (spacing_str == eptr) { fprintf(stderr, "vlog95 error: Unable to extract spacing " "increment from string: %s\n", spacing_str); return 1; } /* Extra stuff at the end. */ if (*eptr != 0) { fprintf(stderr, "vlog95 error: Extra characters '%s' " "included at end of spacing string: %s\n", eptr, spacing_str); return 1; } /* The increment must be greater than zero. */ if (value < 1) { fprintf(stderr, "vlog95 error: Spacing increment (%ld) must " "be greater than zero.\n", value); return 1; } /* An increment of more than sixteen is too much. */ if (value > 16) { fprintf(stderr, "vlog95 error: Spacing increment (%ld) must " "be sixteen or less.\n", value); return 1; } indent_incr = value; } /* Check to see if file/line information should be printed. */ if (strcmp(fileline_str, "") != 0) { char *eptr; long value = strtol(fileline_str, &eptr, 0); /* Nothing usable in the file/line string. */ if (fileline_str == eptr) { fprintf(stderr, "vlog95 error: Unable to extract file/line " "information from string: %s\n", fileline_str); return 1; } /* Extra stuff at the end. */ if (*eptr != 0) { fprintf(stderr, "vlog95 error: Extra characters '%s' " "included at end of file/line string: %s\n", eptr, fileline_str); return 1; } /* The file/line flag must be positive. */ if (value < 0) { fprintf(stderr, "vlog95 error: File/line flag (%ld) must " "be positive.\n", value); return 1; } emit_file_line = value > 0; } /* Check to see if we should also print signed constructs. */ if (strcmp(allowsigned_str, "") != 0) { char *eptr; long value = strtol(allowsigned_str, &eptr, 0); /* Nothing usable in the allow signed string. */ if (allowsigned_str == eptr) { fprintf(stderr, "vlog95 error: Unable to extract allow " "signed information from string: %s\n", allowsigned_str); return 1; } /* Extra stuff at the end. */ if (*eptr != 0) { fprintf(stderr, "vlog95 error: Extra characters '%s' " "included at end of allow signed string: " "%s\n", eptr, allowsigned_str); return 1; } /* The allow signed flag must be positive. */ if (value < 0) { fprintf(stderr, "vlog95 error: Allow signed flag (%ld) must " "be positive.\n", value); return 1; } allow_signed = value > 0; } design = des; #ifdef HAVE_FOPEN64 vlog_out = fopen64(path, "w"); #else vlog_out = fopen(path, "w"); #endif if (vlog_out == 0) { perror(path); return -1; } fprintf(vlog_out, "/*\n"); fprintf(vlog_out, " * 1364-1995 Verilog generated by Icarus Verilog " "VLOG95 Code Generator,\n"); fprintf(vlog_out, " * Version: " VERSION " (" VERSION_TAG ")\n"); fprintf(vlog_out, " * Converted using %s delays and %s signed support.\n", ivl_design_delay_sel(des), allow_signed ? "with" : "without"); fprintf(vlog_out, " */\n"); sim_precision = ivl_design_time_precision(des); /* Get all the root modules and then convert each one. */ ivl_design_roots(des, &roots, &nroots); /* Emit any root scope tasks or functions first. */ for (idx = 0; idx < nroots; idx += 1) { switch(ivl_scope_type(roots[idx])) { case IVL_SCT_FUNCTION: case IVL_SCT_TASK: if (! has_root_scope) { fprintf(vlog_out, "module ivl_root_scope;\n"); indent += indent_incr; has_root_scope = 1; } /* Say this task/function has a parent so the * definition is emitted correctly. */ emit_scope(roots[idx], roots[idx]); break; default: break; } } if (has_root_scope) { indent -= indent_incr; assert(indent == 0); fprintf(vlog_out, "endmodule /* ivl_root_scope */\n"); } /* Emit the rest of the scope objects. */ for (idx = 0; idx < nroots; idx += 1) emit_scope(roots[idx], 0); free_emitted_scope_list(); /* Emit any UDP definitions that the design used. */ emit_udp_list(); /* Emit any UDPs that are Icarus generated (D-FF). */ emit_icarus_generated_udps(); /* If there were errors then add this information to the output. */ if (vlog_errors) { fprintf(vlog_out, "\n"); fprintf(vlog_out, "/*\n"); if (vlog_errors == 1) { fprintf(vlog_out, " * There was 1 error during " "translation.\n"); } else { fprintf(vlog_out, " * There were %d errors during " "translation.\n", vlog_errors); } fprintf(vlog_out, " */\n"); /* Add something that makes the file invalid to make sure * the user knows there were errors. */ fprintf(vlog_out, "\n"); } fclose(vlog_out); /* A do nothing call to prevent warnings about this routine not * being used. */ dump_nexus_information(0, 0); return vlog_errors; } const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } iverilog-10_1/tgt-vlog95/vlog95.conf000066400000000000000000000000711265551621300172550ustar00rootroot00000000000000flag:DLL=vlog95.tgt flag:DISABLE_CONCATZ_GENERATION=true iverilog-10_1/tgt-vlog95/vlog95_priv.h000066400000000000000000000126751265551621300176340ustar00rootroot00000000000000#ifndef IVL_vlog95_priv_H #define IVL_vlog95_priv_H /* * Copyright (C) 2010-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include "config.h" # include "ivl_target.h" # include # include # include /* * The design we are emitting. */ extern ivl_design_t design; /* * This is the file that the converted design is written to. */ extern FILE*vlog_out; /* * Keep a count of the fatal errors that happen during code generation. */ extern int vlog_errors; /* * Keep the simulation time precision so that we can scale delays. */ extern int sim_precision; /* * The expression code needs to know when a parameter definition is being * emitted so it can emit the numeric value instead of the name. */ extern ivl_parameter_t emitting_param; /* * The statement code needs to know what name to use for a translated * function return statement (disable). */ extern const char *func_rtn_name; /* * Keep the current indent level. */ extern unsigned indent; extern unsigned indent_incr; /* * Set to non-zero when the user wants to emit all the file and line * number information. */ extern unsigned emit_file_line; /* * Some tools that are mostly 1364-1995 compliant also support signed so * add support for that as an extension. */ extern unsigned allow_signed; /* * Emit various Verilog types. */ extern void emit_event(ivl_scope_t scope, ivl_statement_t stmt); extern void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned width, unsigned is_lval_width, unsigned can_skip_unsigned, unsigned is_full_prec); extern void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic); extern void emit_lpm(ivl_scope_t scope, ivl_lpm_t lpm); extern void emit_process(ivl_scope_t scope, ivl_process_t proc); extern int emit_scope(ivl_scope_t scope, ivl_scope_t parent); extern void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt); extern void emit_tran(ivl_scope_t scope, ivl_switch_t tran); extern void emit_scaled_delay(ivl_scope_t scope, uint64_t delay); extern void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt); extern void emit_scaled_expr(ivl_scope_t scope, ivl_expr_t expr, int msb, int lsb); extern void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, unsigned width, int msb, int lsb); extern void emit_scope_path(ivl_scope_t scope, ivl_scope_t call_scope); extern void emit_scope_variables(ivl_scope_t scope); extern void emit_scope_call_path(ivl_scope_t scope, ivl_scope_t call_scope); extern void emit_scope_module_path(ivl_scope_t scope, ivl_scope_t call_scope); extern void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD); extern void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD, unsigned sign_extend); extern void emit_nexus_port_driver_as_ca(ivl_scope_t scope, ivl_nexus_t nex); extern void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t const_net); extern void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig); extern void emit_icarus_generated_udps(void); extern void add_udp_to_list(ivl_udp_t udp); extern void emit_udp_list(void); extern void emit_sig_file_line(ivl_signal_t sig); extern void emit_id(const char *id); extern void emit_real_number(double value); extern void emit_number(const char *bits, unsigned nbits, unsigned is_signed, const char *file, unsigned lineno); extern void emit_string(const char *string); /* * Find the enclosing module scope. */ extern ivl_scope_t get_module_scope(ivl_scope_t scope); /* * Get an int32_t/uint64_t from a number is possible. The return type is * 0 for a valid value, negative for a number with undefined bits and * positive it the value is too large. The positive value is the minimum * number of bits required to represent the value. */ extern int32_t get_int32_from_number(ivl_expr_t expr, int *return_type); extern int64_t get_int64_from_number(ivl_expr_t expr, int *return_type); extern uint64_t get_uint64_from_number(ivl_expr_t expr, int *return_type); /* * A package is translated to a module with a special name. This routine * does that translation. To avoid a memory leak the calling routine must * use free() to cleanup the string returned. */ extern char * get_package_name(ivl_scope_t scope); /* * Get the appropriate MSB and LSB for a signal. */ extern void get_sig_msb_lsb(ivl_signal_t sig, int *msb, int *lsb); /* * Cleanup functions. */ extern void free_emitted_scope_list(void); /* * Debug routine to dump the various pieces of nexus information. */ extern void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex); #endif /* IVL_vlog95_priv_H */ iverilog-10_1/tgt-vvp/000077500000000000000000000000001265551621300147515ustar00rootroot00000000000000iverilog-10_1/tgt-vvp/Makefile.in000066400000000000000000000070521265551621300170220ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # #ident "$Id: Makefile.in,v 1.26 2007/02/06 05:07:32 steve Exp $" # # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ O = vvp.o draw_class.o draw_enum.o draw_mux.o draw_substitute.o draw_net_input.o \ draw_switch.o draw_ufunc.o draw_vpi.o \ eval_bool.o \ eval_condit.o \ eval_expr.o eval_object.o eval_real.o eval_string.o \ eval_vec4.o \ modpath.o stmt_assign.o vector.o \ vvp_process.o vvp_scope.o all: dep vvp.tgt vvp.conf vvp-s.conf check: all clean: rm -rf *.o dep vvp.tgt vvp.conf vvp-s.conf distclean: clean rm -f Makefile config.log rm -f stamp-vvp_config-h vvp_config.h cppcheck: $(O:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=tgt-vvp/$@ dep: mkdir dep %.o: %.c vvp_config.h $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a else TGTLDFLAGS= TGTDEPLIBS= endif vvp.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) vvp.conf: $(srcdir)/vvp.conf.in Makefile echo 'flag:VVP_EXECUTABLE=$(bindir)/vvp$(suffix)' | cat $(srcdir)/vvp.conf.in - > vvp.conf vvp-s.conf: $(srcdir)/vvp-s.conf.in Makefile echo 'flag:VVP_EXECUTABLE=$(bindir)/vvp$(suffix)' | cat $(srcdir)/vvp-s.conf.in - > vvp-s.conf stamp-vvp_config-h: $(srcdir)/vvp_config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=tgt-vvp/vvp_config.h vvp_config.h: stamp-vvp_config-h install: all installdirs $(libdir)/ivl$(suffix)/vvp.tgt $(libdir)/ivl$(suffix)/vvp.conf $(libdir)/ivl$(suffix)/vvp-s.conf $(libdir)/ivl$(suffix)/vvp.tgt: ./vvp.tgt $(INSTALL_PROGRAM) ./vvp.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.tgt" $(libdir)/ivl$(suffix)/vvp.conf: vvp.conf $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.conf" $(libdir)/ivl$(suffix)/vvp-s.conf: vvp-s.conf $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.tgt" "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.conf" "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp-s.conf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/tgt-vvp/README.txt000066400000000000000000000017211265551621300164500ustar00rootroot00000000000000 THE VVP TARGET SYMBOL NAME CONVENTIONS There are some naming conventions that the vvp target uses for generating symbol names. * wires and regs Nets and variables are named V_ where is the full hierarchical name of the signal. * Logic devices Logic devices (and, or, buf, bufz, etc.) are named L_. In this case the symbol is attached to a functor that is the output of the logic device. GENERAL FUNCTOR WEB STRUCTURE The net of gates, signals and resolvers is formed from the input design. The basic structure is wrapped around the nexus, which is represented by the ivl_nexus_t. Each nexus represents a resolved value. The input of the nexus is fed by a single driver. If the nexus in the design has multiple drivers, the drivers are first fed into a resolver (or a tree of resolvers) to form a single output that is the nexus. The nexus, then, feeds its output to the inputs of other gates, or to the .net objects in the design. iverilog-10_1/tgt-vvp/cppcheck.sup000066400000000000000000000002711265551621300172620ustar00rootroot00000000000000// These are the global access functions called from the compiler so they // are not used here. // target_design() unusedFunction:vvp.c:149 // target_query() unusedFunction:vvp.c:246 iverilog-10_1/tgt-vvp/draw_class.c000066400000000000000000000056621265551621300172500ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include static void show_prop_type_vector(ivl_type_t ptype) { ivl_variable_type_t data_type = ivl_type_base(ptype); unsigned packed_dimensions = ivl_type_packed_dimensions(ptype); assert(packed_dimensions < 2); const char*signed_flag = ivl_type_signed(ptype)? "s" : ""; char code = data_type==IVL_VT_BOOL? 'b' : 'L'; if (packed_dimensions == 0) { fprintf(vvp_out, "\"%s%c1\"", signed_flag, code); } else { assert(packed_dimensions == 1); assert(ivl_type_packed_lsb(ptype,0) == 0); assert(ivl_type_packed_msb(ptype,0) >= 0); fprintf(vvp_out, "\"%s%c%d\"", signed_flag, code, ivl_type_packed_msb(ptype,0)+1); } } static void show_prop_type(ivl_type_t ptype) { ivl_variable_type_t data_type = ivl_type_base(ptype); unsigned packed_dimensions = ivl_type_packed_dimensions(ptype); switch (data_type) { case IVL_VT_REAL: fprintf(vvp_out, "\"r\""); break; case IVL_VT_STRING: fprintf(vvp_out, "\"S\""); break; case IVL_VT_BOOL: case IVL_VT_LOGIC: show_prop_type_vector(ptype); break; case IVL_VT_DARRAY: case IVL_VT_CLASS: fprintf(vvp_out, "\"o\""); if (packed_dimensions > 0) { unsigned idx; fprintf(vvp_out, " "); for (idx = 0 ; idx < packed_dimensions ; idx += 1) { fprintf(vvp_out, "[%d:%d]", ivl_type_packed_msb(ptype,idx), ivl_type_packed_lsb(ptype,idx)); } } break; default: fprintf(vvp_out, "\"\""); assert(0); break; } } void draw_class_in_scope(ivl_type_t classtype) { int idx; fprintf(vvp_out, "C%p .class \"%s\" [%d]\n", classtype, ivl_type_name(classtype), ivl_type_properties(classtype)); for (idx = 0 ; idx < ivl_type_properties(classtype) ; idx += 1) { fprintf(vvp_out, " %3d: \"%s\", ", idx, ivl_type_prop_name(classtype,idx)); show_prop_type(ivl_type_prop_type(classtype,idx)); fprintf(vvp_out, "\n"); } fprintf(vvp_out, " ;\n"); } iverilog-10_1/tgt-vvp/draw_enum.c000066400000000000000000000057641265551621300171120ustar00rootroot00000000000000/* * Copyright (c) 2010-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include "ivl_alloc.h" # include # include # include # include // HERE: This still needs work! Fall back to 'b format for // very large constants. We also should pass the file and // line number information for the enum type and the MSB and // LSB should be used instead of the width. /* * If it fits we always print this as a uint64_t the run time will turn * it back into a signed value when needed. */ static void draw_enum2_value(ivl_enumtype_t enumtype, unsigned idx) { uint64_t val, mask; const char*bits = ivl_enum_bits(enumtype, idx); const char*bit; unsigned len = strlen(bits); // HERE: If this doesn't fit then write it as a enum4 and modify the run // time to deal with the conversion. assert(len <= sizeof(uint64_t)*8); val = 0; for (bit = bits, mask = 1 ; *bit != 0 ; bit += 1, mask <<= 1) { if (*bit == '1') val |= mask; } fprintf(vvp_out, "%" PRIu64, val); } static void draw_enum4_value(ivl_enumtype_t enumtype, unsigned idx) { const char*bits = ivl_enum_bits(enumtype, idx); const char*bit; fprintf(vvp_out, "%u'b", ivl_enum_width(enumtype)); for (bit = bits+strlen(bits) ; bit > bits ; bit -= 1) fputc(bit[-1], vvp_out); } void draw_enumeration_in_scope(ivl_enumtype_t enumtype) { unsigned idx; unsigned name_count = ivl_enum_names(enumtype); const char*dtype = ivl_enum_type(enumtype)==IVL_VT_BOOL? "2" : "4"; const char*stype = ivl_enum_signed(enumtype) ? "/s" : ""; fprintf(vvp_out, "enum%p .enum%s%s (%u)\n", enumtype, dtype, stype, ivl_enum_width(enumtype)); for (idx = 0 ; idx < name_count ; idx += 1) { fprintf(vvp_out, " \"%s\" ", ivl_enum_name(enumtype, idx)); switch (ivl_enum_type(enumtype)) { case IVL_VT_BOOL: draw_enum2_value(enumtype, idx); break; case IVL_VT_LOGIC: draw_enum4_value(enumtype, idx); break; default: assert(0); } if ((idx+1) < name_count) fputc(',', vvp_out); fputc('\n', vvp_out); } fprintf(vvp_out, " ;\n"); } iverilog-10_1/tgt-vvp/draw_mux.c000066400000000000000000000137151265551621300167520ustar00rootroot00000000000000/* * Copyright (c) 2002-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include /* * This draws a simple A/B mux. The mux can have any width, enough * MUXZ nodes are created to support the vector. */ static void draw_lpm_mux_ab(ivl_lpm_t net, const char*muxz) { unsigned width = ivl_lpm_width(net); ivl_expr_t d_rise, d_fall, d_decay; const char*dly; const char* input[3]; /* Only support A-B muxes in this function. */ assert(ivl_lpm_size(net) == 2); assert(ivl_lpm_selects(net) == 1); d_rise = ivl_lpm_delay(net, 0); d_fall = ivl_lpm_delay(net, 1); d_decay = ivl_lpm_delay(net, 2); ivl_drive_t str0 = ivl_lpm_drive0(net); ivl_drive_t str1 = ivl_lpm_drive1(net); dly = ""; if (d_rise != 0) { unsigned dly_width = width; if (data_type_of_nexus(ivl_lpm_q(net)) == IVL_VT_REAL) dly_width = 0; dly = "/d"; if (number_is_immediate(d_rise, 64, 0) && number_is_immediate(d_fall, 64, 0) && number_is_immediate(d_decay, 64, 0)) { assert( ! number_is_unknown(d_rise)); assert( ! number_is_unknown(d_fall)); assert( ! number_is_unknown(d_decay)); fprintf(vvp_out, "L_%p .delay %u (%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", net, dly_width, get_number_immediate64(d_rise), get_number_immediate64(d_fall), get_number_immediate64(d_decay), net); } else { ivl_signal_t sig; // We do not currently support calculating the decay from // the rise and fall variable delays. assert(d_decay != 0); assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL); assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL); assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL); fprintf(vvp_out, "L_%p .delay %u L_%p/d", net, dly_width, net); sig = ivl_expr_signal(d_rise); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(d_fall); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(d_decay); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0;\n", sig); } } input[0] = draw_net_input(ivl_lpm_data(net,0)); input[1] = draw_net_input(ivl_lpm_data(net,1)); input[2] = draw_net_input(ivl_lpm_select(net)); fprintf(vvp_out, "L_%p%s .functor %s %u", net, dly, muxz, width); if (str0!=IVL_DR_STRONG || str1!=IVL_DR_STRONG) fprintf(vvp_out, " [%d %d]", str0, str1); fprintf(vvp_out, ", %s", input[0]); fprintf(vvp_out, ", %s", input[1]); fprintf(vvp_out, ", %s", input[2]); fprintf(vvp_out, ", C4<>;\n"); } static void draw_lpm_mux_nest(ivl_lpm_t net, const char*muxz) { unsigned idx, level; unsigned width = ivl_lpm_width(net); unsigned swidth = ivl_lpm_selects(net); char*select_input; assert(swidth < 8*sizeof(unsigned)); assert(ivl_lpm_size(net) == (1U << swidth)); select_input = strdup(draw_net_input(ivl_lpm_select(net))); fprintf(vvp_out, "L_%p/0s .part %s, 0, 1; Bit 0 of the select\n", net, select_input); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 2) { fprintf(vvp_out, "L_%p/0/%d .functor %s %u", net, idx/2, muxz, width); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+0))); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+1))); fprintf(vvp_out, ", L_%p/0s, C4<>;\n", net); } for (level = 1 ; level < swidth-1 ; level += 1) { fprintf(vvp_out, "L_%p/%us .part %s, %u, 1; Bit %u of the select\n", net, level, select_input, level, level); for (idx = 0 ; idx < (ivl_lpm_size(net) >> level); idx += 2) { fprintf(vvp_out, "L_%p/%u/%d .functor %s %u", net, level, idx/2, muxz, width); fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx+0); fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx+1); fprintf(vvp_out, ", L_%p/%us", net, level); fprintf(vvp_out, ", C4<>;\n"); } } fprintf(vvp_out, "L_%p/%ds .part %s, %d, 1; Bit %d of the select\n", net, swidth-1, select_input, swidth-1, swidth-1); fprintf(vvp_out, "L_%p .functor %s %u", net, muxz, width); fprintf(vvp_out, ", L_%p/%d/0", net, swidth-2); fprintf(vvp_out, ", L_%p/%d/1", net, swidth-2); fprintf(vvp_out, ", L_%p/%ds", net, swidth-1); fprintf(vvp_out, ", C4<>;\n"); free(select_input); } void draw_lpm_mux(ivl_lpm_t net) { const char*muxz = "MUXZ"; /* The output of the mux defines the type of the mux. the ivl_target should guarantee that all the inputs are the same type as the output. */ switch (data_type_of_nexus(ivl_lpm_q(net))) { case IVL_VT_REAL: muxz = "MUXR"; break; default: muxz = "MUXZ"; break; } if ((ivl_lpm_size(net) == 2) && (ivl_lpm_selects(net) == 1)) { draw_lpm_mux_ab(net, muxz); return; } /* Here we are at the worst case, we generate a tree of MUXZ devices to handle the arbitrary size. */ draw_lpm_mux_nest(net, muxz); } iverilog-10_1/tgt-vvp/draw_net_input.c000066400000000000000000000571661265551621300201560ustar00rootroot00000000000000/* * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include # include # include # include "ivl_alloc.h" static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex) { unsigned idx; ivl_signal_type_t out = IVL_SIT_TRI; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_signal_type_t stype; ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; stype = ivl_signal_type(sig); if (stype == IVL_SIT_REG) continue; if (stype == IVL_SIT_TRI) continue; if (stype == IVL_SIT_NONE) continue; if (stype == IVL_SIT_UWIRE) return IVL_SIT_UWIRE; out = stype; } return out; } static ivl_variable_type_t signal_data_type_of_nexus(ivl_nexus_t nex) { unsigned idx; ivl_variable_type_t out = IVL_VT_NO_TYPE; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_variable_type_t vtype; ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; vtype = ivl_signal_data_type(sig); if (out == IVL_VT_NO_TYPE && vtype == IVL_VT_BOOL) { out = vtype; continue; } if (out != IVL_VT_LOGIC && vtype == IVL_VT_LOGIC) { out = vtype; continue; } if (vtype == IVL_VT_REAL) { out = vtype; break; } } return out; } static char* draw_C4_to_string(ivl_net_const_t cptr) { const char*bits = ivl_const_bits(cptr); unsigned idx; size_t result_len = 5 + ivl_const_width(cptr); char*result = malloc(result_len); char*dp = result; strcpy(dp, "C4<"); dp += strlen(dp); for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { char bitchar = bits[ivl_const_width(cptr)-idx-1]; *dp++ = bitchar; assert(dp >= result); assert((unsigned)(dp - result) < result_len); } strcpy(dp, ">"); return result; } static char* draw_C8_to_string(ivl_net_const_t cptr, ivl_drive_t dr0, ivl_drive_t dr1) { size_t nresult = 5 + 3*ivl_const_width(cptr); char*result = malloc(nresult); const char*bits = ivl_const_bits(cptr); unsigned idx; char dr0c = "01234567"[dr0]; char dr1c = "01234567"[dr1]; char*dp = result; strcpy(dp, "C8<"); dp += strlen(dp); for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { switch (bits[ivl_const_width(cptr)-idx-1]) { case '0': *dp++ = dr0c; *dp++ = dr0c; *dp++ = '0'; break; case '1': *dp++ = dr1c; *dp++ = dr1c; *dp++ = '1'; break; case 'x': case 'X': *dp++ = dr0c; *dp++ = dr1c; *dp++ = 'x'; break; case 'z': case 'Z': *dp++ = '0'; *dp++ = '0'; *dp++ = 'z'; break; default: assert(0); break; } assert(dp >= result); assert((unsigned)(dp - result) < nresult); } strcpy(dp, ">"); return result; } static struct vvp_nexus_data*new_nexus_data(void) { struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data)); return data; } static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr) { ivl_net_logic_t logic; if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG) return 1; if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG) return 1; logic = ivl_nexus_ptr_log(nptr); if (logic != 0) { /* These logic gates are able to generate unusual strength values and so their outputs are considered strength aware. */ if (ivl_logic_type(logic) == IVL_LO_BUFIF0) return 1; if (ivl_logic_type(logic) == IVL_LO_BUFIF1) return 1; if (ivl_logic_type(logic) == IVL_LO_PMOS) return 1; if (ivl_logic_type(logic) == IVL_LO_NMOS) return 1; if (ivl_logic_type(logic) == IVL_LO_CMOS) return 1; } return 0; } /* * Given a nexus, look for a signal that has module delay * paths. Return that signal. (There should be no more than 1.) If we * don't find any, then return nil. */ static ivl_signal_t find_modpath(ivl_nexus_t nex) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; if (ivl_signal_npath(sig) == 0) continue; return sig; } return 0; } static void str_repeat(char*buf, const char*str, unsigned rpt) { unsigned idx; size_t len = strlen(str); for (idx = 0 ; idx < rpt ; idx += 1) { strcpy(buf, str); buf += len; } } /* * This function draws a BUFT to drive a net pullup or pulldown value. * If the drive strength is strong we can draw a C4<> constant as the * pull value, otherwise we need to draw a C8<> constant. */ static char* draw_net_pull(ivl_net_logic_t lptr, ivl_drive_t drive, const char*level) { char*result; char tmp[32]; if (drive == IVL_DR_STRONG) { size_t result_len = 5 + ivl_logic_width(lptr); result = malloc(result_len); char*dp = result; strcpy(dp, "C4<"); dp += strlen(dp); str_repeat(dp, level, ivl_logic_width(lptr)); dp += ivl_logic_width(lptr); *dp++ = '>'; *dp = 0; assert(dp >= result); assert((unsigned)(dp - result) <= result_len); } else { char val[4]; size_t result_len = 5 + 3*ivl_logic_width(lptr); result = malloc(result_len); char*dp = result; val[0] = "01234567"[drive]; val[1] = val[0]; val[2] = level[0]; val[3] = 0; strcpy(dp, "C8<"); dp += strlen(dp); str_repeat(dp, val, ivl_logic_width(lptr)); dp += 3*ivl_logic_width(lptr); *dp++ = '>'; *dp = 0; assert(dp >= result); assert((unsigned)(dp - result) <= result_len); } /* Make the constant an argument to a BUFZ, which is what we use to drive the PULLed value. */ fprintf(vvp_out, "L_%p .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n", lptr, result); snprintf(tmp, sizeof tmp, "L_%p", lptr); result = realloc(result, strlen(tmp)+1); strcpy(result, tmp); return result; } /* * This function takes a nexus and looks for an input functor. It then * draws to the output a string that represents that functor. What we * are trying to do here is find the input to the net that is attached * to this nexus. */ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) { unsigned nptr_pin = ivl_nexus_ptr_pin(nptr); ivl_net_const_t cptr; ivl_net_logic_t lptr; ivl_signal_t sptr; ivl_lpm_t lpm; lptr = ivl_nexus_ptr_log(nptr); if (lptr && ((ivl_logic_type(lptr)==IVL_LO_BUFZ)||(ivl_logic_type(lptr)==IVL_LO_BUFT)) && (nptr_pin == 0)) do { if (! can_elide_bufz(lptr, nptr)) break; return strdup(draw_net_input(ivl_logic_pin(lptr, 1))); } while(0); if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) { return draw_net_pull(lptr, ivl_nexus_ptr_drive0(nptr), "0"); } if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { return draw_net_pull(lptr, ivl_nexus_ptr_drive1(nptr), "1"); } if (lptr && (nptr_pin == 0)) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lptr); return strdup(tmp); } sptr = ivl_nexus_ptr_sig(nptr); if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) { char tmp[128]; /* Input is a .var. This device may be a non-zero pin because it may be an array of reg vectors. */ snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin); if (ivl_signal_dimensions(sptr) > 0) { fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n", sptr, nptr_pin, sptr, nptr_pin); } return strdup(tmp); } cptr = ivl_nexus_ptr_con(nptr); if (cptr) { char *result = 0; ivl_expr_t d_rise, d_fall, d_decay; unsigned dly_width = 0; /* Constants should have exactly 1 pin, with a literal value. */ assert(nptr_pin == 0); switch (ivl_const_type(cptr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: case IVL_VT_STRING: if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) { result = draw_C4_to_string(cptr); } else { result = draw_C8_to_string(cptr, ivl_nexus_ptr_drive0(nptr), ivl_nexus_ptr_drive1(nptr)); } dly_width = ivl_const_width(cptr); break; case IVL_VT_REAL: result = draw_Cr_to_string(ivl_const_real(cptr)); dly_width = 0; break; default: assert(0); break; } d_rise = ivl_const_delay(cptr, 0); d_fall = ivl_const_delay(cptr, 1); d_decay = ivl_const_delay(cptr, 2); /* We have a delayed constant, so we need to build some code. */ if (d_rise != 0) { char tmp[128]; fprintf(vvp_out, "L_%p/d .functor BUFT 1, %s, " "C4<0>, C4<0>, C4<0>;\n", cptr, result); free(result); /* Is this a fixed or variable delay? */ if (number_is_immediate(d_rise, 64, 0) && number_is_immediate(d_fall, 64, 0) && number_is_immediate(d_decay, 64, 0)) { assert(! number_is_unknown(d_rise)); assert(! number_is_unknown(d_fall)); assert(! number_is_unknown(d_decay)); fprintf(vvp_out, "L_%p .delay %u " "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", cptr, dly_width, get_number_immediate64(d_rise), get_number_immediate64(d_fall), get_number_immediate64(d_decay), cptr); } else { ivl_signal_t sig; // We do not currently support calculating the decay // from the rise and fall variable delays. assert(d_decay != 0); assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL); assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL); assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL); fprintf(vvp_out, "L_%p .delay %u L_%p/d", cptr, dly_width, cptr); sig = ivl_expr_signal(d_rise); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(d_fall); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(d_decay); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0;\n", sig); } snprintf(tmp, sizeof tmp, "L_%p", cptr); result = strdup(tmp); } else { char tmp[64]; fprintf(vvp_out, "L_%p .functor BUFT 1, %s, " "C4<0>, C4<0>, C4<0>;\n", cptr, result); free(result); snprintf(tmp, sizeof tmp, "L_%p", cptr); result = strdup(tmp); } return result; } lpm = ivl_nexus_ptr_lpm(nptr); if (lpm) switch (ivl_lpm_type(lpm)) { case IVL_LPM_FF: case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_REAL: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: case IVL_LPM_SFUNC: case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: case IVL_LPM_SIGN_EXT: case IVL_LPM_SUB: case IVL_LPM_MULT: case IVL_LPM_MUX: case IVL_LPM_POW: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_UFUNC: case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ case IVL_LPM_REPEAT: case IVL_LPM_SUBSTITUTE: if (ivl_lpm_q(lpm) == nex) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lpm); return strdup(tmp); } break; } fprintf(stderr, "vvp.tgt error: no input to nexus.\n"); assert(0); return strdup("C"); } static char* draw_island_port(ivl_island_t island, int island_input_flag, ivl_nexus_t nex, struct vvp_nexus_data*nex_data, const char*src) { char result[64]; if (ivl_island_flag_test(island,0) == 0) { fprintf(vvp_out, "I%p .island tran;\n", island); ivl_island_flag_set(island,0,1); } snprintf(result, sizeof result, "p%p", nex); assert(nex_data->island == 0); nex_data->island = island; assert(nex_data->island_input == 0); nex_data->island_input = strdup(result); if (island_input_flag) { fprintf(vvp_out, "p%p .import I%p, %s;\n", nex, island, src); return strdup(src); } else { fprintf(vvp_out, "p%p .port I%p, %s;\n", nex, island, src); return strdup(nex_data->island_input); } } /* * This routine is called to display an error message when a uwire or * wire real has multiple drivers. */ typedef enum mdriver_type_e { MDRV_UWIRE = 0, MDRV_REAL = 1 } mdriver_type_t; static void display_multi_driver_error(ivl_nexus_t nex, unsigned ndrivers, mdriver_type_t type) { unsigned idx; unsigned scope_len = UINT_MAX; ivl_signal_t sig = 0; /* Find the signal. */ for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t tsig = ivl_nexus_ptr_sig(ptr); if (tsig != 0) { ivl_scope_t scope; unsigned len; if (ivl_signal_local(tsig)) continue; /* If this is not a local signal then find the signal * that has the shortest scope (is the furthest up * the hierarchy). */ scope = ivl_signal_scope(tsig); assert(scope); len = strlen(ivl_scope_name(scope)); if (len < scope_len) { scope_len = len; sig = tsig; } } } assert(sig); fprintf(stderr, "%s:%u: vvp.tgt error: ", ivl_signal_file(sig), ivl_signal_lineno(sig)); switch (type) { case MDRV_UWIRE: if (ivl_signal_type(sig) != IVL_SIT_UWIRE) { fprintf(stderr, "(implicit) "); } fprintf(stderr, "uwire"); break; case MDRV_REAL: assert(ivl_signal_type(sig) == IVL_SIT_TRI); if (ivl_signal_data_type(sig) != IVL_VT_REAL) { fprintf(stderr, "(implicit) "); } fprintf(stderr, "wire real"); break; default: assert(0);; } fprintf(stderr, " \"%s\" must have a single driver, found (%u).\n", ivl_signal_basename(sig), ndrivers); vvp_errors += 1; } /* * This function draws the input to a net into a string. What that * means is that it returns a static string that can be used to * represent a resolved driver to a nexus. If there are multiple * drivers to the nexus, then it writes out the resolver declarations * needed to perform strength resolution. * * The string that this returns is malloced, and that means that the * caller must free the string or store it permanently. This function * does *not* check for a previously calculated string. Use the * draw_net_input for the general case. */ static ivl_nexus_ptr_t *drivers = 0x0; static unsigned adrivers = 0; void EOC_cleanup_drivers() { free(drivers); drivers = NULL; adrivers = 0; } static void draw_net_input_x(ivl_nexus_t nex, struct vvp_nexus_data*nex_data) { ivl_island_t island = 0; int island_input_flag = -1; ivl_signal_type_t res; char result[512]; unsigned idx; char**driver_labels; unsigned ndrivers = 0; const char*resolv_type; char*nex_private = 0; /* Accumulate nex_data flags. */ int nex_flags = 0; res = signal_type_of_nexus(nex); switch (res) { case IVL_SIT_TRI: case IVL_SIT_UWIRE: resolv_type = "tri"; break; case IVL_SIT_TRI0: resolv_type = "tri0"; nex_flags |= VVP_NEXUS_DATA_STR; break; case IVL_SIT_TRI1: resolv_type = "tri1"; nex_flags |= VVP_NEXUS_DATA_STR; break; case IVL_SIT_TRIAND: resolv_type = "triand"; break; case IVL_SIT_TRIOR: resolv_type = "trior"; break; default: fprintf(stderr, "vvp.tgt: Unsupported signal type: %d\n", res); assert(0); resolv_type = "tri"; break; } for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_switch_t sw = 0; ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx); /* If this object is part of an island, then we'll be making a port. If this nexus is an output from any switches in the island, then set island_input_flag to false. Save the island cookie. */ if ( (sw = ivl_nexus_ptr_switch(nptr)) ) { assert(island == 0 || island == ivl_switch_island(sw)); island = ivl_switch_island(sw); if (nex == ivl_switch_a(sw)) { nex_flags |= VVP_NEXUS_DATA_STR; island_input_flag = 0; } else if (nex == ivl_switch_b(sw)) { nex_flags |= VVP_NEXUS_DATA_STR; island_input_flag = 0; } else if (island_input_flag == -1) { assert(nex == ivl_switch_enable(sw)); island_input_flag = 1; } } /* Skip input only pins. */ if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ)) continue; /* Mark the strength-aware flag if the driver can generate values other than the standard "6" strength. */ if (nexus_drive_is_strength_aware(nptr)) nex_flags |= VVP_NEXUS_DATA_STR; /* Save this driver. */ if (ndrivers >= adrivers) { adrivers += 4; drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t)); } drivers[ndrivers] = nptr; ndrivers += 1; } if (island_input_flag < 0) island_input_flag = 0; /* Save the nexus driver count in the nex_data. */ assert(nex_data); nex_data->drivers_count = ndrivers; nex_data->flags |= nex_flags; /* If the nexus has no drivers, then send a constant HiZ or 0.0 into the net. */ if (ndrivers == 0) { unsigned wid = width_of_nexus(nex); /* For real nets put 0.0. */ if (signal_data_type_of_nexus(nex) == IVL_VT_REAL) { nex_private = draw_Cr_to_string(0.0); } else { unsigned jdx; char*tmp = malloc(wid + 5); nex_private = tmp; strcpy(tmp, "C4<"); tmp += strlen(tmp); switch (res) { case IVL_SIT_TRI: case IVL_SIT_UWIRE: for (jdx = 0 ; jdx < wid ; jdx += 1) *tmp++ = 'z'; break; case IVL_SIT_TRI0: for (jdx = 0 ; jdx < wid ; jdx += 1) *tmp++ = '0'; break; case IVL_SIT_TRI1: for (jdx = 0 ; jdx < wid ; jdx += 1) *tmp++ = '1'; break; default: assert(0); } *tmp++ = '>'; *tmp = 0; } /* Create an "open" driver to hold the HiZ or 0.0. We need to do this so that .nets have something to hang onto. */ char buf[64]; snprintf(buf, sizeof buf, "o%p", nex); fprintf(vvp_out, "%s .functor BUFZ %u, %s; HiZ drive\n", buf, wid, nex_private); nex_private = realloc(nex_private, strlen(buf)+1); strcpy(nex_private, buf); if (island) { char*tmp2 = draw_island_port(island, island_input_flag, nex, nex_data, nex_private); free(nex_private); nex_private = tmp2; } assert(nex_data->net_input == 0); nex_data->net_input = nex_private; return; } /* A uwire is a tri with only one driver. */ if (res == IVL_SIT_UWIRE) { if (ndrivers > 1) { display_multi_driver_error(nex, ndrivers, MDRV_UWIRE); } res = IVL_SIT_TRI; } /* If the nexus has exactly one driver, then simply draw it. Note that this will *not* work if the nexus is not a TRI type nexus. */ if (ndrivers == 1 && res == IVL_SIT_TRI) { ivl_signal_t path_sig = find_modpath(nex); if (path_sig) { char*nex_str = draw_net_input_drive(nex, drivers[0]); char modpath_label[64]; snprintf(modpath_label, sizeof modpath_label, "V_%p/m", path_sig); nex_private = strdup(modpath_label); draw_modpath(path_sig, nex_str); } else { nex_private = draw_net_input_drive(nex, drivers[0]); } if (island) { char*tmp = draw_island_port(island, island_input_flag, nex, nex_data, nex_private); free(nex_private); nex_private = tmp; } assert(nex_data->net_input == 0); nex_data->net_input = nex_private; return; } /* We currently only support one driver on real nets. */ if (ndrivers > 1 && signal_data_type_of_nexus(nex) == IVL_VT_REAL) { display_multi_driver_error(nex, ndrivers, MDRV_REAL); } driver_labels = malloc(ndrivers * sizeof(char*)); for (idx = 0; idx < ndrivers; idx += 1) { driver_labels[idx] = draw_net_input_drive(nex, drivers[idx]); } fprintf(vvp_out, "RS_%p .resolv %s", nex, resolv_type); for (idx = 0; idx < ndrivers; idx += 1) { fprintf(vvp_out, ", %s", driver_labels[idx]); free(driver_labels[idx]); } fprintf(vvp_out, ";\n"); free(driver_labels); snprintf(result, sizeof result, "RS_%p", nex); if (island) nex_private = draw_island_port(island, island_input_flag, nex, nex_data, result); else nex_private = strdup(result); assert(nex_data->net_input == 0); nex_data->net_input = nex_private; } /* * Get a cached description of the nexus input, or create one if this * nexus has not been cached yet. This is a wrapper for the common * case call to draw_net_input_x. */ const char*draw_net_input(ivl_nexus_t nex) { struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*) ivl_nexus_get_private(nex); /* If this nexus already has a label, then its input is already figured out. Just return the existing label. */ if (nex_data && nex_data->net_input) return nex_data->net_input; if (nex_data == 0) { nex_data = new_nexus_data(); ivl_nexus_set_private(nex, nex_data); } assert(nex_data->net_input == 0); draw_net_input_x(nex, nex_data); return nex_data->net_input; } const char*draw_island_net_input(ivl_island_t island, ivl_nexus_t nex) { struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*) ivl_nexus_get_private(nex); /* If this nexus already has a label, then its input is already figured out. Just return the existing label. */ if (nex_data && nex_data->island_input) { assert(nex_data->island == island); return nex_data->island_input; } if (nex_data == 0) { nex_data = new_nexus_data(); ivl_nexus_set_private(nex, nex_data); } assert(nex_data->net_input == 0); draw_net_input_x(nex, nex_data); assert(nex_data->island == island); assert(nex_data->island_input); return nex_data->island_input; } iverilog-10_1/tgt-vvp/draw_substitute.c000066400000000000000000000024241265551621300203470ustar00rootroot00000000000000/* * Copyright (c) 2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include void draw_lpm_substitute(ivl_lpm_t net) { unsigned swidth = width_of_nexus(ivl_lpm_data(net,1)); fprintf(vvp_out, "L_%p .substitute %u, %u %u", net, ivl_lpm_width(net), ivl_lpm_base(net), swidth); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,0))); fprintf(vvp_out, ", %s;\n", draw_net_input(ivl_lpm_data(net,1))); } iverilog-10_1/tgt-vvp/draw_switch.c000066400000000000000000000075401265551621300174410ustar00rootroot00000000000000/* * Copyright (c) 2008-2010,2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include static void draw_tran_island(ivl_island_t island) { fprintf(vvp_out, "I%p .island tran;\n", island); ivl_island_flag_set(island, 0, 1); } void draw_switch_in_scope(ivl_switch_t sw) { ivl_island_t island; ivl_nexus_t nex_a, nex_b, enable; const char*str_a, *str_b, *str_e; ivl_expr_t rise_exp = ivl_switch_delay(sw, 0); ivl_expr_t fall_exp = ivl_switch_delay(sw, 1); ivl_expr_t decay_exp= ivl_switch_delay(sw, 2); if ((rise_exp || fall_exp || decay_exp) && (!number_is_immediate(rise_exp, 64, 0) || number_is_unknown(rise_exp) || !number_is_immediate(fall_exp, 64, 0) || number_is_unknown(fall_exp) || !number_is_immediate(decay_exp, 64, 0) || number_is_unknown(decay_exp))) { fprintf(stderr, "%s:%u: error: Invalid tranif delay expression.\n", ivl_switch_file(sw), ivl_switch_lineno(sw)); vvp_errors += 1; } island = ivl_switch_island(sw); if (ivl_island_flag_test(island, 0) == 0) draw_tran_island(island); nex_a = ivl_switch_a(sw); assert(nex_a); str_a = draw_island_net_input(island, nex_a); nex_b = ivl_switch_b(sw); assert(nex_b); str_b = draw_island_net_input(island, nex_b); enable = ivl_switch_enable(sw); str_e = 0; char str_e_buf[4 + 2*sizeof(void*)]; if (enable && rise_exp) { assert(fall_exp && decay_exp); /* If the enable has a delay, then generate a .delay node to delay the input by the specified amount. Do the delay outside of the island so that the island processing doesn't have to deal with it. */ const char*raw = draw_net_input(enable); snprintf(str_e_buf, sizeof str_e_buf, "p%p", sw); str_e = str_e_buf; fprintf(vvp_out, "%s/d .delay 1 " "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") %s;\n", str_e, get_number_immediate64(rise_exp), get_number_immediate64(fall_exp), get_number_immediate64(decay_exp), raw); fprintf(vvp_out, "%s .import I%p, %s/d;\n", str_e, island, str_e); } else if (enable) { str_e = draw_island_net_input(island, enable); } switch (ivl_switch_type(sw)) { case IVL_SW_TRAN: fprintf(vvp_out, " .tran"); break; case IVL_SW_TRANIF0: fprintf(vvp_out, " .tranif0"); break; case IVL_SW_TRANIF1: fprintf(vvp_out, " .tranif1"); break; case IVL_SW_TRAN_VP: fprintf(vvp_out, " .tranvp %u %u %u,", ivl_switch_width(sw), ivl_switch_part(sw), ivl_switch_offset(sw)); break; default: fprintf(stderr, "%s:%u: tgt-vvp sorry: resistive switch modeling " "is not currently supported.\n", ivl_switch_file(sw), ivl_switch_lineno(sw)); vvp_errors += 1; return; } fprintf(vvp_out, " I%p, %s %s", island, str_a, str_b); if (enable) { fprintf(vvp_out, ", %s", str_e); } fprintf(vvp_out, ";\n"); } iverilog-10_1/tgt-vvp/draw_ufunc.c000066400000000000000000000150471265551621300172610ustar00rootroot00000000000000/* * Copyright (c) 2005-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include static void function_argument_logic(ivl_signal_t port, ivl_expr_t expr) { unsigned ewidth, pwidth; /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); ewidth = ivl_expr_width(expr); pwidth = ivl_signal_width(port); draw_eval_vec4(expr); if (ewidth < pwidth) fprintf(vvp_out, " %%pad/u %u;\n", pwidth); } static void function_argument_real(ivl_signal_t port, ivl_expr_t expr) { /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); draw_eval_real(expr); } static void draw_eval_function_argument(ivl_signal_t port, ivl_expr_t expr) { ivl_variable_type_t dtype = ivl_signal_data_type(port); switch (dtype) { case IVL_VT_BOOL: /* For now, treat bit2 variables as bit4 variables. */ case IVL_VT_LOGIC: function_argument_logic(port, expr); break; case IVL_VT_REAL: function_argument_real(port, expr); break; case IVL_VT_CLASS: vvp_errors += draw_eval_object(expr); break; case IVL_VT_STRING: draw_eval_string(expr); break; case IVL_VT_DARRAY: vvp_errors += draw_eval_object(expr); break; default: fprintf(stderr, "XXXX function argument %s type=%d?!\n", ivl_signal_basename(port), dtype); assert(0); } } static void draw_send_function_argument(ivl_signal_t port) { ivl_variable_type_t dtype = ivl_signal_data_type(port); switch (dtype) { case IVL_VT_BOOL: /* For now, treat bit2 variables as bit4 variables. */ case IVL_VT_LOGIC: fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", port, ivl_signal_width(port)); break; case IVL_VT_REAL: fprintf(vvp_out, " %%store/real v%p_0;\n", port); break; case IVL_VT_CLASS: fprintf(vvp_out, " %%store/obj v%p_0;\n", port); break; case IVL_VT_STRING: fprintf(vvp_out, " %%store/str v%p_0;\n", port); break; case IVL_VT_DARRAY: fprintf(vvp_out, " %%store/obj v%p_0;\n", port); break; default: fprintf(stderr, "XXXX function argument %s type=%d?!\n", ivl_signal_basename(port), dtype); assert(0); } } static void draw_ufunc_preamble(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } /* Evaluate the expressions and send the results to the function ports. Do this in two passes - evaluate, then send - this avoids the function input variables being overwritten if the same (non-automatic) function is called in one of the expressions. */ assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_eval_function_argument(port, ivl_expr_parm(expr, idx)); } for (idx = ivl_expr_parms(expr) ; idx > 0 ; idx -= 1) { ivl_signal_t port = ivl_scope_port(def, idx); draw_send_function_argument(port); } /* Call the function */ fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); } static void draw_ufunc_epilogue(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); /* If this is an automatic function, free the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%free S_%p;\n", def); } } /* * A call to a user defined function generates a result that is the * result of this expression. * * The result of the function is placed by the function execution into * a signal within the scope of the function that also has a basename * the same as the function. The ivl_target API handled the result * mapping already, and we get the name of the result signal as * parameter 0 of the function definition. */ void draw_ufunc_vec4(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call function code. */ draw_ufunc_preamble(expr); assert(ivl_signal_dimensions(retval) == 0); fprintf(vvp_out, " %%load/vec4 v%p_0;\n", retval); draw_ufunc_epilogue(expr); } void draw_ufunc_real(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); /* Load the result into a word. */ fprintf(vvp_out, " %%load/real v%p_0;\n", retval); draw_ufunc_epilogue(expr); } void draw_ufunc_string(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); /* Load the result into a word. */ fprintf(vvp_out, " %%load/str v%p_0;\n", retval); draw_ufunc_epilogue(expr); } void draw_ufunc_object(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); /* Load the result into the object stack. */ fprintf(vvp_out, " %%load/obj v%p_0;\n", retval); draw_ufunc_epilogue(expr); } iverilog-10_1/tgt-vvp/draw_vpi.c000066400000000000000000000361221265551621300167340ustar00rootroot00000000000000/* * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include "ivl_alloc.h" struct args_info { char*text; /* True ('s' or 'u' if this argument is a calculated vec4. */ char vec_flag; /* True if this argument is a calculated string. */ char str_flag; /* True if this argument is a calculated real. */ char real_flag; /* Stack position if this argument is a calculated value. */ unsigned stack; /* Expression width: Only used of vec_flag is true. */ unsigned vec_wid; struct args_info *child; /* Arguments can be nested. */ }; static const char* magic_sfuncs[] = { "$time", "$stime", "$realtime", "$simtime", 0 }; static int is_magic_sfunc(const char*name) { int idx; for (idx = 0 ; magic_sfuncs[idx] ; idx += 1) if (strcmp(magic_sfuncs[idx],name) == 0) return 1; return 0; } static int is_fixed_memory_word(ivl_expr_t net) { ivl_signal_t sig; if (ivl_expr_type(net) != IVL_EX_SIGNAL) return 0; sig = ivl_expr_signal(net); if (ivl_signal_dimensions(sig) == 0) return 1; if (ivl_signal_type(sig) == IVL_SIT_REG) return 0; if (number_is_immediate(ivl_expr_oper1(net), IMM_WID, 0)) return 1; return 0; } static int get_vpi_taskfunc_signal_arg(struct args_info *result, ivl_expr_t expr) { char buffer[4096]; switch (ivl_expr_type(expr)) { case IVL_EX_SIGNAL: /* If the signal node is narrower than the signal itself, then this is a part select so I'm going to need to evaluate the expression. Also, if the signedness of the expression is different from the signedness of the signal. This could be caused by a $signed or $unsigned system function. If I don't need to do any evaluating, then skip it as I'll be passing the handle to the signal itself. */ if (ivl_expr_width(expr) != ivl_signal_width(ivl_expr_signal(expr))) { /* This should never happen since we have IVL_EX_SELECT. */ return 0; } else if (ivl_expr_signed(expr) != ivl_signal_signed(ivl_expr_signal(expr))) { return 0; } else if (is_fixed_memory_word(expr)) { /* This is a word of a non-array, or a word of a net array, so we can address the word directly. */ ivl_signal_t sig = ivl_expr_signal(expr); unsigned use_word = 0; ivl_expr_t word_ex = ivl_expr_oper1(expr); if (word_ex) { /* Some array select have been evaluated. */ if (number_is_immediate(word_ex,IMM_WID, 0)) { assert(! number_is_unknown(word_ex)); use_word = get_number_immediate(word_ex); word_ex = 0; } } if (word_ex) return 0; assert(word_ex == 0); snprintf(buffer, sizeof buffer, "v%p_%u", sig, use_word); result->text = strdup(buffer); return 1; } else { /* What's left, this is the work of a var array. Create the right code to handle it. */ ivl_signal_t sig = ivl_expr_signal(expr); unsigned use_word = 0; unsigned use_word_defined = 0; ivl_expr_t word_ex = ivl_expr_oper1(expr); if (word_ex) { /* Some array select have been evaluated. */ if (number_is_immediate(word_ex, IMM_WID, 0)) { assert(! number_is_unknown(word_ex)); use_word = get_number_immediate(word_ex); use_word_defined = 1; word_ex = 0; } } if (word_ex && (ivl_expr_type(word_ex)==IVL_EX_SIGNAL || ivl_expr_type(word_ex)==IVL_EX_SELECT)) { /* Special case: the index is a signal/select. */ result->child = calloc(1, sizeof(struct args_info)); if (get_vpi_taskfunc_signal_arg(result->child, word_ex)) { snprintf(buffer, sizeof buffer, "&A", sig, result->child->text); free(result->child->text); } else { free(result->child); result->child = NULL; return 0; } } else if (word_ex) { /* Fallback case: Give up and evaluate expression. */ return 0; } else { assert(use_word_defined); snprintf(buffer, sizeof buffer, "&A", sig, use_word); } result->text = strdup(buffer); return 1; } case IVL_EX_SELECT: { ivl_expr_t vexpr = ivl_expr_oper1(expr); ivl_expr_t bexpr; ivl_expr_t wexpr; assert(vexpr); /* This code is only for signals or selects. */ if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL && ivl_expr_type(vexpr) != IVL_EX_SELECT) return 0; /* If the expression is a substring expression, then the xPV method of passing the argument will not work and we have to resort to the default method. */ if (ivl_expr_value(vexpr) == IVL_VT_STRING) return 0; /* If the sub-expression is a DARRAY, then this select is a dynamic-array word select. Handle that elsewhere. */ if (ivl_expr_value(vexpr) == IVL_VT_DARRAY) return 0; /* The signal is part of an array. */ /* Add &APV<> code here when it is finished. */ bexpr = ivl_expr_oper2(expr); /* This is a pad operation. */ if (!bexpr) return 0; wexpr = ivl_expr_oper1(vexpr); /* If vexpr has an operand, then that operand is a word index and we are taking a select from an array word. This would come up in expressions like "array[][]" where wexpr is */ if (wexpr && number_is_immediate(wexpr, 64, 1) && number_is_immediate(bexpr, 64, 1)) { assert(! number_is_unknown(bexpr)); assert(! number_is_unknown(wexpr)); snprintf(buffer, sizeof buffer, "&APV", ivl_expr_signal(vexpr), get_number_immediate(wexpr), get_number_immediate(bexpr), ivl_expr_width(expr)); } else if (wexpr) { return 0; /* This is a constant bit/part select. */ } else if (number_is_immediate(bexpr, 64, 1)) { assert(! number_is_unknown(bexpr)); snprintf(buffer, sizeof buffer, "&PV", ivl_expr_signal(vexpr), get_number_immediate(bexpr), ivl_expr_width(expr)); /* This is an indexed bit/part select. */ } else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL || ivl_expr_type(bexpr) == IVL_EX_SELECT) { /* Special case: the base is a signal/select. */ result->child = calloc(1, sizeof(struct args_info)); if (get_vpi_taskfunc_signal_arg(result->child, bexpr)) { snprintf(buffer, sizeof buffer, "&PV", ivl_expr_signal(vexpr), result->child->text, ivl_expr_width(expr)); free(result->child->text); } else { free(result->child); result->child = NULL; return 0; } } else { /* Fallback case: Punt and let caller handle it. */ return 0; } result->text = strdup(buffer); return 1; } default: return 0; } } static void draw_vpi_taskfunc_args(const char*call_string, ivl_statement_t tnet, ivl_expr_t fnet) { unsigned idx; unsigned parm_count = tnet ? ivl_stmt_parm_count(tnet) : ivl_expr_parms(fnet); struct args_info *args = calloc(parm_count, sizeof(struct args_info)); char buffer[4096]; ivl_parameter_t par; /* Keep track of how much string stack this function call is going to need. We'll need this for making stack references, and also to clean out the stack when done. */ unsigned vec4_stack_need = 0; unsigned str_stack_need = 0; unsigned real_stack_need = 0; /* Figure out how many expressions are going to be evaluated for this task call. I won't need to evaluate expressions for items that are VPI objects directly. */ for (idx = 0 ; idx < parm_count ; idx += 1) { ivl_expr_t expr = tnet ? ivl_stmt_parm(tnet, idx) : ivl_expr_parm(fnet, idx); switch (ivl_expr_type(expr)) { /* These expression types can be handled directly, with VPI handles of their own. Therefore, skip them in the process of evaluating expressions. */ case IVL_EX_NONE: args[idx].text = strdup("\" \""); continue; case IVL_EX_ARRAY: snprintf(buffer, sizeof buffer, "v%p", ivl_expr_signal(expr)); args[idx].text = strdup(buffer); continue; case IVL_EX_NUMBER: { if (( par = ivl_expr_parameter(expr) )) { snprintf(buffer, sizeof buffer, "P_%p", par); } else { unsigned bit, wid = ivl_expr_width(expr); const char*bits = ivl_expr_bits(expr); char*dp; snprintf(buffer, sizeof buffer, "%u'%sb", wid, ivl_expr_signed(expr)? "s" : ""); dp = buffer + strlen(buffer); for (bit = wid ; bit > 0 ; bit -= 1) *dp++ = bits[bit-1]; *dp++ = 0; assert(dp >= buffer); assert((unsigned)(dp - buffer) <= sizeof buffer); } args[idx].text = strdup(buffer); continue; } case IVL_EX_STRING: if (( par = ivl_expr_parameter(expr) )) { snprintf(buffer, sizeof buffer, "P_%p", par); args[idx].text = strdup(buffer); } else { size_t needed_len = strlen(ivl_expr_string(expr)) + 3; args[idx].text = malloc(needed_len); snprintf(args[idx].text, needed_len, "\"%s\"", ivl_expr_string(expr)); } continue; case IVL_EX_REALNUM: if (( par = ivl_expr_parameter(expr) )) { snprintf(buffer, sizeof buffer, "P_%p", par); args[idx].text = strdup(buffer); continue; } break; case IVL_EX_ENUMTYPE: snprintf(buffer, sizeof buffer, "enum%p", ivl_expr_enumtype(expr)); args[idx].text = strdup(buffer); continue; case IVL_EX_EVENT: snprintf(buffer, sizeof buffer, "E_%p", ivl_expr_event(expr)); args[idx].text = strdup(buffer); continue; case IVL_EX_SCOPE: snprintf(buffer, sizeof buffer, "S_%p", ivl_expr_scope(expr)); args[idx].text = strdup(buffer); continue; case IVL_EX_SFUNC: if (is_magic_sfunc(ivl_expr_name(expr))) { snprintf(buffer, sizeof buffer, "%s", ivl_expr_name(expr)); args[idx].text = strdup(buffer); continue; } break; case IVL_EX_SIGNAL: case IVL_EX_SELECT: args[idx].stack = vec4_stack_need; if (get_vpi_taskfunc_signal_arg(&args[idx], expr)) { if (args[idx].vec_flag) { vec4_stack_need += 1; } else { args[idx].stack = 0; } continue; } else { args[idx].stack = 0; break; } /* Everything else will need to be evaluated and passed as a constant to the vpi task. */ default: break; } switch (ivl_expr_value(expr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: draw_eval_vec4(expr); args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; args[idx].str_flag = 0; args[idx].real_flag = 0; args[idx].stack = vec4_stack_need; args[idx].vec_wid = ivl_expr_width(expr); vec4_stack_need += 1; buffer[0] = 0; break; case IVL_VT_REAL: draw_eval_real(expr); args[idx].vec_flag = 0; args[idx].str_flag = 0; args[idx].real_flag = 1; args[idx].stack = real_stack_need; real_stack_need += 1; buffer[0] = 0; break; case IVL_VT_STRING: /* Eval the string into the stack, and tell VPI about the stack position. */ draw_eval_string(expr); args[idx].vec_flag = 0; args[idx].str_flag = 1; args[idx].real_flag = 0; args[idx].stack = str_stack_need; args[idx].real_flag = 0; str_stack_need += 1; buffer[0] = 0; break; default: fprintf(vvp_out, "\nXXXX Unexpected argument: call_string=<%s>, arg=%u, type=%d\n", call_string, idx, ivl_expr_value(expr)); fflush(vvp_out); assert(0); } args[idx].text = strdup(buffer); } fprintf(vvp_out, "%s", call_string); for (idx = 0 ; idx < parm_count ; idx += 1) { struct args_info*ptr; if (args[idx].str_flag) { /* If this is a stack reference, then calculate the stack depth and use that to generate the completed string. */ unsigned pos = str_stack_need - args[idx].stack - 1; fprintf(vvp_out, ", S<%u,str>",pos); } else if (args[idx].real_flag) { unsigned pos = real_stack_need - args[idx].stack - 1; fprintf(vvp_out, ", W<%u,r>",pos); } else if (args[idx].vec_flag) { unsigned pos = vec4_stack_need - args[idx].stack - 1; char sign_flag = args[idx].vec_flag; unsigned wid = args[idx].vec_wid; fprintf(vvp_out, ", S<%u,vec4,%c%u>",pos, sign_flag, wid); } else { fprintf(vvp_out, ", %s", args[idx].text); } free(args[idx].text); /* Free the nested children. */ ptr = args[idx].child; while (ptr != NULL) { struct args_info*tptr = ptr; ptr = ptr->child; free(tptr); } } free(args); fprintf(vvp_out, " {%u %u %u}", vec4_stack_need, real_stack_need, str_stack_need); fprintf(vvp_out, ";\n"); } void draw_vpi_task_call(ivl_statement_t tnet) { unsigned parm_count = ivl_stmt_parm_count(tnet); const char *command = "error"; switch (ivl_stmt_sfunc_as_task(tnet)) { case IVL_SFUNC_AS_TASK_ERROR: command = "%vpi_call"; break; case IVL_SFUNC_AS_TASK_WARNING: command = "%vpi_call/w"; break; case IVL_SFUNC_AS_TASK_IGNORE: command = "%vpi_call/i"; break; } if (parm_count == 0) { fprintf(vvp_out, " %s %u %u \"%s\" {0 0 0};\n", command, ivl_file_table_index(ivl_stmt_file(tnet)), ivl_stmt_lineno(tnet), ivl_stmt_name(tnet)); } else { char call_string[1024]; sprintf(call_string, " %s %u %u \"%s\"", command, ivl_file_table_index(ivl_stmt_file(tnet)), ivl_stmt_lineno(tnet), ivl_stmt_name(tnet)); draw_vpi_taskfunc_args(call_string, tnet, 0); } } void draw_vpi_func_call(ivl_expr_t fnet) { char call_string[1024]; sprintf(call_string, " %%vpi_func %u %u \"%s\" %u", ivl_file_table_index(ivl_expr_file(fnet)), ivl_expr_lineno(fnet), ivl_expr_name(fnet), ivl_expr_width(fnet)); draw_vpi_taskfunc_args(call_string, 0, fnet); } void draw_vpi_rfunc_call(ivl_expr_t fnet) { char call_string[1024]; sprintf(call_string, " %%vpi_func/r %u %u \"%s\"", ivl_file_table_index(ivl_expr_file(fnet)), ivl_expr_lineno(fnet), ivl_expr_name(fnet)); draw_vpi_taskfunc_args(call_string, 0, fnet); } iverilog-10_1/tgt-vvp/eval_bool.c000066400000000000000000000045331265551621300170640ustar00rootroot00000000000000/* * Copyright (c) 2005-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file includes functions for evaluating REAL expressions. */ # include "vvp_config.h" # include "vvp_priv.h" # include # include #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_INTTYPES_H # ifndef __STDC_FORMAT_MACROS # define __STDC_FORMAT_MACROS 1 # endif # include #else #endif # include # include /* * Evaluate the bool64 the hard way, by evaluating the logic vector * and converting it to a bool64. */ static int eval_bool64_logic(ivl_expr_t expr) { int res; const char*s_flag = ""; draw_eval_vec4(expr); res = allocate_word(); if (ivl_expr_signed(expr)) s_flag = "/s"; fprintf(vvp_out, " %%ix/vec4%s %d;\n", s_flag, res); return res; } static int draw_number_bool64(ivl_expr_t expr) { int res; const char*bits = ivl_expr_bits(expr); uint64_t val = 0; unsigned long idx, low, hig; for (idx = 0 ; idx < ivl_expr_width(expr) ; idx += 1) { if (bits[idx] == '1') val |= 1UL << idx; } res = allocate_word(); low = val % UINT64_C(0x100000000); hig = val / UINT64_C(0x100000000); fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", res, low, hig); return res; } int draw_eval_bool64(ivl_expr_t expr) { int res; switch (ivl_expr_type(expr)) { case IVL_EX_NUMBER: res = draw_number_bool64(expr); break; default: res = eval_bool64_logic(expr); break; } return res; } iverilog-10_1/tgt-vvp/eval_condit.c000066400000000000000000000167421265551621300174160ustar00rootroot00000000000000/* * Copyright (c) 2014-2016 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include static int draw_condition_fallback(ivl_expr_t expr) { int use_flag = allocate_flag(); /* Evaluate the condition expression, including optionally reducing it to a single bit. Put the result into a flag bit for use by all the tests. */ draw_eval_vec4(expr); if (ivl_expr_width(expr) > 1) fprintf(vvp_out, " %%or/r;\n"); fprintf(vvp_out, " %%flag_set/vec4 %d;\n", use_flag); return use_flag; } static int draw_condition_binary_compare(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); if ((ivl_expr_value(le) == IVL_VT_REAL) || (ivl_expr_value(re) == IVL_VT_REAL)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_type(re)==IVL_EX_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_type(le)==IVL_EX_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_CLASS) && (ivl_expr_value(re)==IVL_VT_CLASS)) { return draw_condition_fallback(expr); } unsigned use_wid = ivl_expr_width(le); if (ivl_expr_width(re) > use_wid) use_wid = ivl_expr_width(re); /* If the le is constant, then swap the operands so that we can possibly take advantage of the immediate version of the %cmp instruction. */ if (ivl_expr_width(le)==use_wid && test_immediate_vec4_ok(le)) { ivl_expr_t tmp = le; le = re; re = tmp; } draw_eval_vec4(le); resize_vec4_wid(le, use_wid); char use_opcode = ivl_expr_opcode(expr); if (ivl_expr_width(re)==use_wid && test_immediate_vec4_ok(re)) { /* Special case: If the right operand can be handled as an immediate operand, then use that instead. */ if (use_opcode=='n' || use_opcode=='N') draw_immediate_vec4(re, "%cmpi/ne"); else draw_immediate_vec4(re, "%cmpi/e"); } else { draw_eval_vec4(re); resize_vec4_wid(re, use_wid); if (use_opcode=='n' || use_opcode=='N') fprintf(vvp_out, " %%cmp/ne;\n"); else fprintf(vvp_out, " %%cmp/e;\n"); } switch (ivl_expr_opcode(expr)) { case 'n': /* != */ case 'e': /* == */ return 4; break; case 'N': /* !== */ case 'E': /* === */ return 6; default: assert(0); return -1; } } static int draw_condition_binary_real_le(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); ivl_expr_t tmp; char use_opcode = ivl_expr_opcode(expr); /* If this is a > or >=, then convert it to < or <= by swapping the operands. Adjust the opcode to match. */ switch (use_opcode) { case 'G': tmp = le; le = re; re = tmp; use_opcode = 'L'; break; case '>': tmp = le; le = re; re = tmp; use_opcode = '<'; break; } draw_eval_real(le); draw_eval_real(re); fprintf(vvp_out, " %%cmp/wr;\n"); switch (use_opcode) { case '<': return 5; case 'L': fprintf(vvp_out, " %%flag_or 5, 4;\n"); return 5; default: assert(0); return -1; } } static int draw_condition_binary_le(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); ivl_expr_t tmp; if ((ivl_expr_value(le) == IVL_VT_REAL) || (ivl_expr_value(re) == IVL_VT_REAL)) { return draw_condition_binary_real_le(expr); } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_type(re)==IVL_EX_STRING)) { return draw_condition_fallback(expr); } if ((ivl_expr_type(le)==IVL_EX_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { return draw_condition_fallback(expr); } char use_opcode = ivl_expr_opcode(expr); char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u'; /* If this is a > or >=, then convert it to < or <= by swapping the operands. Adjust the opcode to match. */ switch (use_opcode) { case 'G': tmp = le; le = re; re = tmp; use_opcode = 'L'; break; case '>': tmp = le; le = re; re = tmp; use_opcode = '<'; break; } /* NOTE: I think I would rather the elaborator handle the operand widths. When that happens, take this code out. */ unsigned use_wid = ivl_expr_width(le); if (ivl_expr_width(re) > use_wid) use_wid = ivl_expr_width(re); draw_eval_vec4(le); resize_vec4_wid(le, use_wid); if (ivl_expr_width(re)==use_wid && test_immediate_vec4_ok(re)) { /* Special case: If the right operand can be handled as an immediate operand, then use that instead. */ char opcode[8]; snprintf(opcode, sizeof opcode, "%%cmpi/%c", s_flag); draw_immediate_vec4(re, opcode); } else { draw_eval_vec4(re); resize_vec4_wid(re, use_wid); fprintf(vvp_out, " %%cmp/%c;\n", s_flag); } switch (use_opcode) { case '<': return 5; case 'L': fprintf(vvp_out, " %%flag_or 5, 4;\n"); return 5; default: assert(0); return -1; } } static int draw_condition_binary_lor(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); int lx = draw_eval_condition(le); if (lx < 8) { int tmp = allocate_flag(); fprintf(vvp_out, " %%flag_mov %d, %d;\n", tmp, lx); lx = tmp; } int rx = draw_eval_condition(re); fprintf(vvp_out, " %%flag_or %d, %d;\n", rx, lx); clr_flag(lx); return rx; } static int draw_condition_binary(ivl_expr_t expr) { switch (ivl_expr_opcode(expr)) { case 'e': /* == */ case 'E': /* === */ case 'n': /* != */ case 'N': /* !== */ return draw_condition_binary_compare(expr); case '<': case '>': case 'L': /* <= */ case 'G': /* >= */ return draw_condition_binary_le(expr); case 'o': /* Logical or (||) */ return draw_condition_binary_lor(expr); default: return draw_condition_fallback(expr); } } int draw_eval_condition(ivl_expr_t expr) { switch (ivl_expr_type(expr)) { case IVL_EX_BINARY: return draw_condition_binary(expr); default: return draw_condition_fallback(expr); } } iverilog-10_1/tgt-vvp/eval_expr.c000066400000000000000000000211121265551621300170770ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include # include "ivl_alloc.h" int number_is_unknown(ivl_expr_t expr) { const char*bits; unsigned idx; if (ivl_expr_type(expr) == IVL_EX_ULONG) return 0; assert(ivl_expr_type(expr) == IVL_EX_NUMBER); bits = ivl_expr_bits(expr); for (idx = 0 ; idx < ivl_expr_width(expr) ; idx += 1) if ((bits[idx] != '0') && (bits[idx] != '1')) return 1; return 0; } /* * This function returns TRUE if the number can be represented as a * lim_wid immediate value. This amounts to verifying that any upper * bits are the same. For a negative value we do not support the most * negative twos-complement value since it can not be negated. This * code generator always emits positive values, hence the negation * requirement. */ int number_is_immediate(ivl_expr_t expr, unsigned lim_wid, int negative_ok_flag) { const char *bits; unsigned nbits = ivl_expr_width(expr); char pad_bit = '0'; unsigned idx; /* We can only convert numbers to an immediate value. */ if (ivl_expr_type(expr) != IVL_EX_NUMBER && ivl_expr_type(expr) != IVL_EX_ULONG && ivl_expr_type(expr) != IVL_EX_DELAY) return 0; /* If a negative value is OK, then we really have one less * significant bit because of the sign bit. */ if (negative_ok_flag) lim_wid -= 1; /* This is an unsigned value so it can not have the -2**N problem. */ if (ivl_expr_type(expr) == IVL_EX_ULONG) { unsigned long imm; if (lim_wid >= 8*sizeof(unsigned long)) return 1; /* At this point we know that lim_wid is smaller than an * unsigned long variable. */ imm = ivl_expr_uvalue(expr); if (imm < (1UL << lim_wid)) return 1; else return 0; } /* This is an unsigned value so it can not have the -2**N problem. */ if (ivl_expr_type(expr) == IVL_EX_DELAY) { uint64_t imm; if (lim_wid >= 8*sizeof(uint64_t)) return 1; /* At this point we know that lim_wid is smaller than a * uint64_t variable. */ imm = ivl_expr_delay_val(expr); if (imm < ((uint64_t)1 << lim_wid)) return 1; else return 0; } bits = ivl_expr_bits(expr); if (ivl_expr_signed(expr) && bits[nbits-1]=='1') pad_bit = '1'; if (pad_bit == '1' && !negative_ok_flag) return 0; for (idx = lim_wid ; idx < nbits ; idx += 1) if (bits[idx] != pad_bit) return 0; /* If we have a negative number make sure it is not too big. */ if (pad_bit == '1') { for (idx = 0; idx < lim_wid; idx += 1) if (bits[idx] == '1') return 1; return 0; } return 1; } /* * We can return positive or negative values. You must verify that the * number is not unknown (number_is_unknown) and is small enough * (number_is_immediate). */ long get_number_immediate(ivl_expr_t expr) { long imm = 0; switch (ivl_expr_type(expr)) { case IVL_EX_ULONG: imm = ivl_expr_uvalue(expr); break; case IVL_EX_NUMBER: { const char*bits = ivl_expr_bits(expr); unsigned nbits = ivl_expr_width(expr); unsigned idx; /* We can not copy more bits than fit into a long. */ if (nbits > 8*sizeof(long)) nbits = 8*sizeof(long); for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]){ case '0': break; case '1': imm |= 1L << idx; break; default: assert(0); } if (ivl_expr_signed(expr) && bits[nbits-1]=='1' && nbits < 8*sizeof(long)) imm |= -1L << nbits; break; } default: assert(0); } return imm; } uint64_t get_number_immediate64(ivl_expr_t expr) { uint64_t imm = 0; switch (ivl_expr_type(expr)) { case IVL_EX_ULONG: imm = ivl_expr_uvalue(expr); break; case IVL_EX_NUMBER: { const char*bits = ivl_expr_bits(expr); unsigned nbits = ivl_expr_width(expr); unsigned idx; for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]){ case '0': break; case '1': assert(idx < 64); imm |= UINT64_C(1) << idx; break; default: assert(0); } if (ivl_expr_signed(expr) && bits[nbits-1]=='1' && nbits < 64) imm |= (-UINT64_C(1)) << nbits; break; } default: assert(0); } return imm; } void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) { switch (ivl_expr_type(expr)) { case IVL_EX_NUMBER: case IVL_EX_ULONG: { assert(number_is_immediate(expr, IMM_WID, 1)); if (number_is_unknown(expr)) { /* We are loading a 'bx so mimic %ix/get. */ fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", ix); fprintf(vvp_out, " %%flag_set/imm 4, 1;\n"); break; } long imm = get_number_immediate(expr); if (imm >= 0) { fprintf(vvp_out, " %%ix/load %u, %ld, 0;\n", ix, imm); } else { fprintf(vvp_out, " %%ix/load %u, 0, 0; loading %ld\n", ix, imm); fprintf(vvp_out, " %%ix/sub %u, %ld, 0;\n", ix, -imm); } /* This can not have have a X/Z value so clear flag 4. */ fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); } break; /* Special case: There is an %ix instruction for reading index values directly from variables. In this case, try to use that special instruction. */ case IVL_EX_SIGNAL: { ivl_signal_t sig = ivl_expr_signal(expr); unsigned word = 0; if (ivl_signal_dimensions(sig) > 0) { ivl_expr_t ixe; const char*type = ivl_expr_signed(expr) ? "/s" : ""; /* Detect the special case that this is a variable array. In this case, the ix/getv will not work, so do it the hard way. */ if (ivl_signal_type(sig) == IVL_SIT_REG) { draw_eval_vec4(expr); fprintf(vvp_out, " %%ix/vec4%s %u;\n", type, ix); break; } ixe = ivl_expr_oper1(expr); if (number_is_immediate(ixe, IMM_WID, 0)) { assert(! number_is_unknown(ixe)); word = get_number_immediate(ixe); } else { draw_eval_vec4(expr); fprintf(vvp_out, " %%ix/vec4%s %u;\n", type, ix); break; } } const char*type = ivl_signal_signed(sig) ? "/s" : ""; fprintf(vvp_out, " %%ix/getv%s %u, v%p_%u;\n", type, ix, sig, word); break; } default: draw_eval_vec4(expr); /* Is this a signed expression? */ if (ivl_expr_signed(expr)) { fprintf(vvp_out, " %%ix/vec4/s %u;\n", ix); } else { fprintf(vvp_out, " %%ix/vec4 %u;\n", ix); } break; } } /* * This function, in addition to setting the value into index 0, sets * bit 4 to 1 if the value is unknown. */ void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix) { switch (ivl_expr_value(expr)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: eval_logic_into_integer(expr, ix); break; case IVL_VT_REAL: draw_eval_real(expr); fprintf(vvp_out, " %%cvt/sr %u;\n", ix); break; default: fprintf(stderr, "XXXX ivl_expr_value == %d\n", ivl_expr_value(expr)); assert(0); } } char *process_octal_codes(const char *in, unsigned width) { unsigned idx = 0; unsigned ridx = 0; unsigned str_len = strlen(in); char *out = (char *)malloc(str_len+1); /* If we do not have any octal codes just return the input. */ if (width/8 == str_len) { strcpy(out, in); return out; } while (ridx < str_len) { /* An octal constant always has three digits. */ if (in[ridx] == '\\') { out[idx] = (in[ridx+1]-'0')*64 + (in[ridx+2]-'0')*8 + (in[ridx+3]-'0'); idx += 1; ridx += 4; } else { out[idx] = in[ridx]; idx += 1; ridx += 1; } } out[idx] = '\0'; return out; } iverilog-10_1/tgt-vvp/eval_object.c000066400000000000000000000214571265551621300174030ustar00rootroot00000000000000/* * Copyright (c) 2012-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include static int eval_darray_new(ivl_expr_t ex) { int errors = 0; unsigned size_reg = allocate_word(); ivl_expr_t size_expr = ivl_expr_oper1(ex); ivl_expr_t init_expr = ivl_expr_oper2(ex); draw_eval_expr_into_integer(size_expr, size_reg); // The new function has a net_type that contains the details // of the type. ivl_type_t net_type = ivl_expr_net_type(ex); assert(net_type); ivl_type_t element_type = ivl_type_element(net_type); assert(element_type); switch (ivl_type_base(element_type)) { int msb, lsb, wid; case IVL_VT_REAL: // REAL objects are not packable. assert(ivl_type_packed_dimensions(element_type) == 0); fprintf(vvp_out, " %%new/darray %u, \"r\";\n", size_reg); break; case IVL_VT_STRING: // STRING objects are not packable. assert(ivl_type_packed_dimensions(element_type) == 0); fprintf(vvp_out, " %%new/darray %u, \"S\";\n", size_reg); break; case IVL_VT_BOOL: // bool objects are vectorable, but for now only support // a single dimensions. assert(ivl_type_packed_dimensions(element_type) == 1); msb = ivl_type_packed_msb(element_type, 0); lsb = ivl_type_packed_lsb(element_type, 0); wid = msb>=lsb? msb - lsb : lsb - msb; wid += 1; fprintf(vvp_out, " %%new/darray %u, \"%sb%d\";\n", size_reg, ivl_type_signed(element_type) ? "s" : "", wid); break; case IVL_VT_LOGIC: // logic objects are vectorable, but for now only support // a single dimensions. assert(ivl_type_packed_dimensions(element_type) == 1); msb = ivl_type_packed_msb(element_type, 0); lsb = ivl_type_packed_lsb(element_type, 0); wid = msb>=lsb? msb - lsb : lsb - msb; wid += 1; fprintf(vvp_out, " %%new/darray %u, \"%sv%d\";\n", size_reg, ivl_type_signed(element_type) ? "s" : "", wid); break; default: assert(0); break; } clr_word(size_reg); if (init_expr && ivl_expr_type(init_expr)==IVL_EX_ARRAY_PATTERN) { unsigned idx; switch (ivl_type_base(element_type)) { case IVL_VT_BOOL: for (idx = 0 ; idx < ivl_expr_parms(init_expr) ; idx += 1) { draw_eval_vec4(ivl_expr_parm(init_expr,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%set/dar/obj/vec4 3;\n"); fprintf(vvp_out, " %%pop/vec4 1;\n"); } break; case IVL_VT_REAL: for (idx = 0 ; idx < ivl_expr_parms(init_expr) ; idx += 1) { draw_eval_real(ivl_expr_parm(init_expr,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%set/dar/obj/real 3;\n"); fprintf(vvp_out, " %%pop/real 1;\n"); } break; case IVL_VT_STRING: for (idx = 0 ; idx < ivl_expr_parms(init_expr) ; idx += 1) { draw_eval_string(ivl_expr_parm(init_expr,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%set/dar/obj/str 3;\n"); fprintf(vvp_out, " %%pop/str 1;\n"); } break; default: fprintf(vvp_out, "; ERROR: Sorry, this type not supported here.\n"); errors += 1; break; } } else if (init_expr && number_is_immediate(size_expr,32,0)) { /* In this case, there is an init expression, the expression is NOT an array_pattern, and the size expression used to calculate the size of the array is a constant. Generate an unrolled set of assignments. */ long idx; long cnt = get_number_immediate(size_expr); unsigned wid; switch (ivl_type_base(element_type)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: wid = width_of_packed_type(element_type); for (idx = 0 ; idx < cnt ; idx += 1) { draw_eval_vec4(init_expr); fprintf(vvp_out, " %%parti/%c %u, %ld, 6;\n", ivl_expr_signed(init_expr) ? 's' : 'u', wid, idx * wid); fprintf(vvp_out, " %%ix/load 3, %ld, 0;\n", cnt - idx - 1); fprintf(vvp_out, " %%set/dar/obj/vec4 3;\n"); fprintf(vvp_out, " %%pop/vec4 1;\n"); } break; case IVL_VT_REAL: draw_eval_real(init_expr); for (idx = 0 ; idx < cnt ; idx += 1) { fprintf(vvp_out, " %%ix/load 3, %ld, 0;\n", idx); fprintf(vvp_out, " %%set/dar/obj/real 3;\n"); } fprintf(vvp_out, " %%pop/real 1;\n"); break; case IVL_VT_STRING: draw_eval_string(init_expr); for (idx = 0 ; idx < cnt ; idx += 1) { fprintf(vvp_out, " %%ix/load 3, %ld, 0;\n", idx); fprintf(vvp_out, " %%set/dar/obj/str 3;\n"); } fprintf(vvp_out, " %%pop/str 1;\n"); break; default: fprintf(vvp_out, "; ERROR: Sorry, this type not supported here.\n"); errors += 1; break; } } else if (init_expr) { fprintf(vvp_out, "; ERROR: Sorry, I don't know how to work with this size expr.\n"); errors += 1; } return errors; } static int eval_class_new(ivl_expr_t ex) { ivl_type_t class_type = ivl_expr_net_type(ex); fprintf(vvp_out, " %%new/cobj C%p;\n", class_type); return 0; } static int eval_object_null(ivl_expr_t ex) { (void)ex; /* Parameter is not used. */ fprintf(vvp_out, " %%null;\n"); return 0; } static int eval_object_property(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); int idx = 0; ivl_expr_t idx_expr = 0; /* If there is an array index expression, then this is an array'ed property, and we need to calculate the index for the expression. */ if ( (idx_expr = ivl_expr_oper1(expr)) ) { idx = allocate_word(); draw_eval_expr_into_integer(idx_expr, idx); } fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/obj %u, %d; eval_object_property\n", pidx, idx); fprintf(vvp_out, " %%pop/obj 1, 1;\n"); if (idx != 0) clr_word(idx); return 0; } static int eval_object_shallowcopy(ivl_expr_t ex) { int errors = 0; ivl_expr_t dest = ivl_expr_oper1(ex); ivl_expr_t src = ivl_expr_oper2(ex); errors += draw_eval_object(dest); errors += draw_eval_object(src); /* The %scopy opcode pops the top of the object stack as the source object, and shallow-copies it to the new top, the destination object. The destination is left on the top of the stack. */ fprintf(vvp_out, " %%scopy;\n"); return errors; } static int eval_object_signal(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); /* Simple case: This is a simple variable. Generate a load statement to load the string into the stack. */ if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); return 0; } /* There is a word select expression, so load the index into a register and load from the array. */ ivl_expr_t word_ex = ivl_expr_oper1(expr); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%load/obja v%p, %d;\n", sig, word_ix); clr_word(word_ix); return 0; } static int eval_object_ufunc(ivl_expr_t ex) { draw_ufunc_object(ex); return 0; } int draw_eval_object(ivl_expr_t ex) { switch (ivl_expr_type(ex)) { case IVL_EX_NEW: switch (ivl_expr_value(ex)) { case IVL_VT_CLASS: return eval_class_new(ex); case IVL_VT_DARRAY: return eval_darray_new(ex); default: fprintf(vvp_out, "; ERROR: draw_eval_object: Invalid type (%d) for \n", ivl_expr_value(ex)); return 0; } case IVL_EX_NULL: return eval_object_null(ex); case IVL_EX_PROPERTY: return eval_object_property(ex); case IVL_EX_SHALLOWCOPY: return eval_object_shallowcopy(ex); case IVL_EX_SIGNAL: return eval_object_signal(ex); case IVL_EX_UFUNC: return eval_object_ufunc(ex); default: fprintf(vvp_out, "; ERROR: draw_eval_object: Invalid expression type %d\n", ivl_expr_type(ex)); return 1; } } iverilog-10_1/tgt-vvp/eval_real.c000066400000000000000000000336771265551621300170670ustar00rootroot00000000000000/* * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file includes functions for evaluating REAL expressions. */ # include "vvp_priv.h" # include # include # include # include # include static unsigned long word_alloc_mask = 0x0f; int allocate_word() { int res = 4; int max = WORD_COUNT; while (res < max && (1U << res) & word_alloc_mask) res += 1; if (res >= max) { fprintf(stderr, "vvp.tgt error: Thread words exhausted.\n"); exit(1); } word_alloc_mask |= 1U << res; return res; } void clr_word(int res) { int max = WORD_COUNT; assert(res < max); word_alloc_mask &= ~ (1U << res); } static void draw_binary_real(ivl_expr_t expr) { switch (ivl_expr_opcode(expr)) { case 'E': case 'N': case 'l': case 'r': case 'R': case '&': case '|': case '^': case 'A': case 'O': case 'X': /* These should be caught in draw_eval_real(). */ assert(0); } draw_eval_real(ivl_expr_oper1(expr)); draw_eval_real(ivl_expr_oper2(expr)); switch (ivl_expr_opcode(expr)) { case '+': fprintf(vvp_out, " %%add/wr;\n"); break; case '-': fprintf(vvp_out, " %%sub/wr;\n"); break; case '*': fprintf(vvp_out, " %%mul/wr;\n"); break; case '/': fprintf(vvp_out, " %%div/wr;\n"); break; case '%': fprintf(vvp_out, " %%mod/wr;\n"); break; case 'p': fprintf(vvp_out, " %%pow/wr;\n"); break; case 'm': fprintf(vvp_out, " %%min/wr;\n"); break; case 'M': fprintf(vvp_out, " %%max/wr;\n"); break; default: fprintf(stderr, "XXXX draw_binary_real(%c)\n", ivl_expr_opcode(expr)); assert(0); } } static void draw_number_real(ivl_expr_t expr) { unsigned int idx; const char*bits = ivl_expr_bits(expr); unsigned wid = ivl_expr_width(expr); unsigned long mant = 0; int vexp = 0x1000; /* If this is a negative number, then arrange for the 2's complement to be calculated as we scan through the value. Real values are sign-magnitude, and this negation gets us a magnitude. */ int negate = 0; int carry = 0; if (ivl_expr_signed(expr) && (bits[wid-1] == '1')) { negate = 1; carry = 1; } for (idx = 0 ; idx < wid && idx < IMM_WID ; idx += 1) { int cur_bit = bits[idx] == '1'? 1 : 0; if (negate) { cur_bit ^= 1; cur_bit += carry; carry = (cur_bit >> 1) & 1; cur_bit &= 1; } if (cur_bit) mant |= 1 << idx; } for ( ; idx < wid ; idx += 1) { if (ivl_expr_signed(expr) && (bits[idx] == bits[IMM_WID-1])) continue; if (bits[idx] == '0') continue; fprintf(stderr, "vvp.tgt error: mantissa doesn't fit!\n"); assert(0); } /* If required, add in a sign bit. */ if (negate) vexp |= 0x4000; fprintf(vvp_out, " %%pushi/real %lu, %d; load(num)= %c%lu (wid=%u)\n", mant, vexp, (vexp&0x4000)? '-' : '+', mant, wid); } static void draw_property_real(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/r %u;\n", pidx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } static void draw_realnum_real(ivl_expr_t expr) { double value = ivl_expr_dvalue(expr); double fract; int expo, vexp; unsigned long mant; int sign = 0; /* Handle the special case that the value is +-inf. */ if (isinf(value)) { if (value > 0) fprintf(vvp_out, " %%pushi/real 0, %d; load=+inf\n", 0x3fff); else fprintf(vvp_out, " %%pushi/real 0, %d; load=-inf\n", 0x7fff); return; } /* Handle the special case that the value is NaN. */ if (value != value) { fprintf(vvp_out, " %%pushi/real 1, %d; load=NaN\n", 0x3fff); return; } if (value < 0) { sign = 0x4000; value *= -1; } fract = frexp(value, &expo); fract = ldexp(fract, 31); mant = fract; expo -= 31; vexp = expo + 0x1000; assert(vexp >= 0); assert(vexp < 0x2000); vexp += sign; fprintf(vvp_out, " %%pushi/real %lu, %d; load=%#g\n", mant, vexp, ivl_expr_dvalue(expr)); /* Capture the residual bits, if there are any. Note that an IEEE754 mantissa has 52 bits, 31 of which were accounted for already. */ fract -= floor(fract); fract = ldexp(fract, 22); mant = fract; expo -= 22; vexp = expo + 0x1000; assert(vexp >= 0); assert(vexp < 0x2000); vexp += sign; if (mant != 0) { fprintf(vvp_out, " %%pushi/real %lu, %d; load=%#g\n", mant, vexp, ivl_expr_dvalue(expr)); fprintf(vvp_out, " %%add/wr;\n"); } } /* * The real value of a logic expression is the integer value of the * expression converted to real. */ static void draw_real_logic_expr(ivl_expr_t expr) { draw_eval_vec4(expr); const char*sign_flag = ivl_expr_signed(expr)? "/s" : ""; if (ivl_expr_width(expr) > 64) { fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); } else { int res = allocate_word(); fprintf(vvp_out, " %%ix/vec4%s %d;\n", sign_flag, res); if (ivl_expr_signed(expr)) fprintf(vvp_out, " %%cvt/rs %d;\n", res); else fprintf(vvp_out, " %%cvt/ru %d;\n", res); clr_word(res); } } static void draw_select_real(ivl_expr_t expr) { /* The sube references the expression to be selected from. */ ivl_expr_t sube = ivl_expr_oper1(expr); /* This is the select expression */ ivl_expr_t shift= ivl_expr_oper2(expr); /* Assume the sub-expression is a signal */ ivl_signal_t sig = ivl_expr_signal(sube); assert(ivl_signal_data_type(sig) == IVL_VT_DARRAY); draw_eval_expr_into_integer(shift, 3); fprintf(vvp_out, " %%load/dar/r v%p_0;\n", sig); } static void draw_sfunc_real(ivl_expr_t expr) { switch (ivl_expr_value(expr)) { case IVL_VT_REAL: if (ivl_expr_parms(expr) == 0) { fprintf(vvp_out, " %%vpi_func/r %u %u \"%s\" {0 0 0};\n", ivl_file_table_index(ivl_expr_file(expr)), ivl_expr_lineno(expr), ivl_expr_name(expr)); } else { draw_vpi_rfunc_call(expr); } break; case IVL_VT_VECTOR: /* If the value of the sfunc is a vector, then evaluate it as a vector, then convert the result to a real (via an index register) for the result. */ draw_real_logic_expr(expr); break; default: assert(0); } } static void draw_signal_real_real(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/real v%p_0;\n", sig); return; } ivl_expr_t word_ex = ivl_expr_oper1(expr); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%load/ar v%p, %d;\n", sig, word_ix); clr_word(word_ix); } static void draw_signal_real(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); switch (ivl_signal_data_type(sig)) { case IVL_VT_LOGIC: draw_real_logic_expr(expr); return; case IVL_VT_REAL: draw_signal_real_real(expr); return; default: fprintf(stderr, "vvp.tgt error: signal_data_type=%d\n", ivl_signal_data_type(sig)); assert(0); return; } } /* If we have nested ternary operators they are likely tail recursive. * This code is structured to allow this recursion without overflowing * the available thread words. */ static void draw_ternary_real(ivl_expr_t expr) { ivl_expr_t cond = ivl_expr_oper1(expr); ivl_expr_t true_ex = ivl_expr_oper2(expr); ivl_expr_t false_ex = ivl_expr_oper3(expr); unsigned lab_true = local_count++; unsigned lab_out = local_count++; int cond_flag = allocate_flag(); /* Evaluate the ternary condition. */ draw_eval_vec4(cond); if (ivl_expr_width(cond) > 1) fprintf(vvp_out, " %%or/r;\n"); fprintf(vvp_out, " %%flag_set/vec4 %d;\n", cond_flag); /* Evaluate the true expression second. */ fprintf(vvp_out, " %%jmp/1 T_%u.%u, %d;\n", thread_count, lab_true, cond_flag); /* Evaluate the false expression. */ draw_eval_real(false_ex); fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d; End of false expr.\n", thread_count, lab_out, cond_flag); /* If the conditional is undefined then blend the real words. */ draw_eval_real(true_ex); fprintf(vvp_out, " %%blend/wr;\n"); fprintf(vvp_out, " %%jmp T_%u.%u; End of blend\n", thread_count, lab_out); /* Evaluate the true expression. */ fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_true); draw_eval_real(true_ex); /* This is the out label. */ fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out); clr_flag(cond_flag); } static void increment(ivl_expr_t e, bool pre) { ivl_signal_t sig = ivl_expr_signal(e); fprintf(vvp_out, " %%load/real v%p_0;\n", sig); if (!pre) fprintf(vvp_out, " %%dup/real;\n"); fprintf(vvp_out, " %%pushi/real 1, 0x1000;\n"); fprintf(vvp_out, " %%add/wr;\n"); if ( pre) fprintf(vvp_out, " %%dup/real;\n"); fprintf(vvp_out, " %%store/real v%p_0;\n", sig); } static void decrement(ivl_expr_t e, bool pre) { ivl_signal_t sig = ivl_expr_signal(e); fprintf(vvp_out, " %%load/real v%p_0;\n", sig); if (!pre) fprintf(vvp_out, " %%dup/real;\n"); fprintf(vvp_out, " %%pushi/real 1, 0x1000;\n"); fprintf(vvp_out, " %%sub/wr;\n"); if ( pre) fprintf(vvp_out, " %%dup/real;\n"); fprintf(vvp_out, " %%store/real v%p_0;\n", sig); } static void draw_unary_real(ivl_expr_t expr) { ivl_expr_t sube; /* If the opcode is a ~ or a ! then the sub expression must not be * a real expression, so use vector evaluation and then convert * that result to a real value. */ if ((ivl_expr_opcode(expr) == '~') || (ivl_expr_opcode(expr) == '!')) { draw_real_logic_expr(expr); return; } sube = ivl_expr_oper1(expr); if (ivl_expr_opcode(expr) == 'r') { /* Cast an integer value to a real. */ const char *suffix = ""; assert(ivl_expr_value(sube) != IVL_VT_REAL); draw_eval_vec4(sube); if (ivl_expr_signed(sube)) suffix = "/s"; fprintf(vvp_out, " %%cvt/rv%s;\n", suffix); return; } if (ivl_expr_opcode(expr) == '+') { draw_eval_real(sube); return; } if (ivl_expr_opcode(expr) == '-') { fprintf(vvp_out, " %%pushi/real 0, 0; load 0.0\n"); draw_eval_real(sube); fprintf(vvp_out, " %%sub/wr;\n"); return; } if (ivl_expr_opcode(expr) == 'm') { /* abs() */ draw_eval_real(sube); fprintf(vvp_out, " %%abs/wr;\n"); return; } if (ivl_expr_opcode(expr) == 'v') { /* Handled in eval_expr.c. */ fprintf(stderr, "vvp.tgt error: real -> integer cast in real " "context.\n"); assert(0); } switch (ivl_expr_opcode(expr)) { case 'I': increment(sube, true); return; case 'i': increment(sube, false); return; case 'D': decrement(sube, true); return; case 'd': decrement(sube, false); return; } fprintf(stderr, "vvp.tgt error: unhandled real unary operator: %c.\n", ivl_expr_opcode(expr)); assert(0); } void draw_eval_real(ivl_expr_t expr) { /* If this expression/sub-expression is not real then we need * to evaluate it as a bit value and then convert the bit based * result to a real value. This is required to get integer * division to work correctly. */ if (ivl_expr_value(expr) != IVL_VT_REAL) { draw_real_logic_expr(expr); return; } switch (ivl_expr_type(expr)) { case IVL_EX_BINARY: draw_binary_real(expr); break; case IVL_EX_NUMBER: draw_number_real(expr); break; case IVL_EX_PROPERTY: draw_property_real(expr); break; case IVL_EX_REALNUM: draw_realnum_real(expr); break; case IVL_EX_SELECT: draw_select_real(expr); break; case IVL_EX_SFUNC: draw_sfunc_real(expr); break; case IVL_EX_SIGNAL: draw_signal_real(expr); break; case IVL_EX_TERNARY: draw_ternary_real(expr); break; case IVL_EX_UFUNC: draw_ufunc_real(expr); break; case IVL_EX_UNARY: draw_unary_real(expr); break; default: if (ivl_expr_value(expr) == IVL_VT_VECTOR) { draw_eval_vec4(expr); const char*sign_flag = ivl_expr_signed(expr)? "/s" : ""; int res = allocate_word(); fprintf(vvp_out, " %%ix/vec4%s %d;\n", sign_flag, res); fprintf(vvp_out, " %%cvt/rs %d;\n", res); clr_word(res); } else { fprintf(stderr, "XXXX Evaluate real expression (%d)\n", ivl_expr_type(expr)); fprintf(vvp_out, " ; XXXX Evaluate real expression (%d)\n", ivl_expr_type(expr)); return; } break; } } iverilog-10_1/tgt-vvp/eval_string.c000066400000000000000000000135131265551621300174350ustar00rootroot00000000000000/* * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include static void fallback_eval(ivl_expr_t expr) { draw_eval_vec4(expr); fprintf(vvp_out, " %%pushv/str; Cast BOOL/LOGIC to string\n"); } static void string_ex_concat(ivl_expr_t expr) { unsigned repeat; assert(ivl_expr_parms(expr) != 0); assert(ivl_expr_repeat(expr) != 0); /* Push the first string onto the stack, no matter what. */ draw_eval_string(ivl_expr_parm(expr,0)); for (repeat = 0 ; repeat < ivl_expr_repeat(expr) ; repeat += 1) { unsigned idx; for (idx = (repeat==0)? 1 : 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_expr_t sub = ivl_expr_parm(expr,idx); /* Special case: If operand is a string literal, then concat it using the %concati/str instruction. */ if (ivl_expr_type(sub) == IVL_EX_STRING) { fprintf(vvp_out, " %%concati/str \"%s\";\n", ivl_expr_string(sub)); continue; } draw_eval_string(sub); fprintf(vvp_out, " %%concat/str;\n"); } } } static void string_ex_property(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/str %u;\n", pidx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } static void string_ex_signal(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); if (ivl_signal_data_type(sig) != IVL_VT_STRING) { fallback_eval(expr); return; } /* Simple case: This is a simple variable. Generate a load statement to load the string into the stack. */ if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/str v%p_0;\n", sig); return; } /* There is a word select expression, so load the index into a register and load from the array. */ ivl_expr_t word_ex = ivl_expr_oper1(expr); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%load/stra v%p, %d;\n", sig, word_ix); clr_word(word_ix); } static void string_ex_select(ivl_expr_t expr) { /* The sube references the expression to be selected from. */ ivl_expr_t sube = ivl_expr_oper1(expr); /* This is the select expression */ ivl_expr_t shift= ivl_expr_oper2(expr); /* Assume the sub-expression is a signal */ ivl_signal_t sig = ivl_expr_signal(sube); assert(ivl_signal_data_type(sig) == IVL_VT_DARRAY || ivl_signal_data_type(sig) == IVL_VT_QUEUE); draw_eval_expr_into_integer(shift, 3); fprintf(vvp_out, " %%load/dar/str v%p_0;\n", sig); } static void string_ex_string(ivl_expr_t expr) { const char*val = ivl_expr_string(expr); /* Special case: The elaborator converts the string "" to an 8-bit zero, which is in turn escaped to the 4-character string \000. Detect this special case and convert it back to an empty string. [Perhaps elaboration should be fixed?] */ if (ivl_expr_width(expr)==8 && (strcmp(val,"\\000") == 0)) { fprintf(vvp_out, " %%pushi/str \"\";\n"); return; } fprintf(vvp_out, " %%pushi/str \"%s\";\n", val); } static void string_ex_substr(ivl_expr_t expr) { ivl_expr_t arg; unsigned arg1; unsigned arg2; assert(ivl_expr_parms(expr) == 3); arg = ivl_expr_parm(expr,0); draw_eval_string(arg); /* Evaluate the arguments... */ arg = ivl_expr_parm(expr, 1); arg1 = allocate_word(); draw_eval_expr_into_integer(arg, arg1); arg = ivl_expr_parm(expr, 2); arg2 = allocate_word(); draw_eval_expr_into_integer(arg, arg2); fprintf(vvp_out, " %%substr %u, %u;\n", arg1, arg2); clr_word(arg1); clr_word(arg2); } static void string_ex_pop(ivl_expr_t expr) { const char*fb; ivl_expr_t arg; if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) fb = "b"; else fb = "f"; arg = ivl_expr_parm(expr, 0); assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/str v%p_0;\n", fb, ivl_expr_signal(arg)); } void draw_eval_string(ivl_expr_t expr) { switch (ivl_expr_type(expr)) { case IVL_EX_STRING: string_ex_string(expr); break; case IVL_EX_SIGNAL: string_ex_signal(expr); break; case IVL_EX_CONCAT: string_ex_concat(expr); break; case IVL_EX_PROPERTY: string_ex_property(expr); break; case IVL_EX_SELECT: string_ex_select(expr); break; case IVL_EX_SFUNC: if (strcmp(ivl_expr_name(expr), "$ivl_string_method$substr") == 0) string_ex_substr(expr); else if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) string_ex_pop(expr); else if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_front")==0) string_ex_pop(expr); else fallback_eval(expr); break; case IVL_EX_UFUNC: draw_ufunc_string(expr); break; default: fallback_eval(expr); break; } } iverilog-10_1/tgt-vvp/eval_vec4.c000066400000000000000000001063261265551621300167750ustar00rootroot00000000000000/* * Copyright (c) 2013-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file includes functions for evaluating VECTOR expressions. */ # include "vvp_priv.h" # include # include # include # include # include void resize_vec4_wid(ivl_expr_t expr, unsigned wid) { if (ivl_expr_width(expr) == wid) return; if (ivl_expr_signed(expr)) fprintf(vvp_out, " %%pad/s %u;\n", wid); else fprintf(vvp_out, " %%pad/u %u;\n", wid); } /* * Test if the draw_immediate_vec4 instruction can be used. */ int test_immediate_vec4_ok(ivl_expr_t re) { const char*bits; unsigned idx; if (ivl_expr_type(re) != IVL_EX_NUMBER) return 0; if (ivl_expr_width(re) <= 32) return 1; bits = ivl_expr_bits(re); for (idx = 32 ; idx < ivl_expr_width(re) ; idx += 1) { if (bits[idx] != '0') return 0; } return 1; } static void make_immediate_vec4_words(ivl_expr_t re, unsigned long*val0p, unsigned long*valxp, unsigned*widp) { unsigned long val0 = 0; unsigned long valx = 0; unsigned wid = ivl_expr_width(re); const char*bits = ivl_expr_bits(re); unsigned idx; for (idx = 0 ; idx < wid ; idx += 1) { assert( ((val0|valx)&0x80000000UL) == 0UL ); val0 <<= 1; valx <<= 1; switch (bits[wid-idx-1]) { case '0': break; case '1': val0 |= 1; break; case 'x': val0 |= 1; valx |= 1; break; case 'z': val0 |= 0; valx |= 1; break; default: assert(0); break; } } *val0p = val0; *valxp = valx; *widp = wid; } void draw_immediate_vec4(ivl_expr_t re, const char*opcode) { unsigned long val0, valx; unsigned wid; make_immediate_vec4_words(re, &val0, &valx, &wid); fprintf(vvp_out, " %s %lu, %lu, %u;\n", opcode, val0, valx, wid); } static void draw_binary_vec4_arith(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); unsigned lwid = ivl_expr_width(le); unsigned rwid = ivl_expr_width(re); unsigned ewid = ivl_expr_width(expr); int signed_flag = ivl_expr_signed(le) && ivl_expr_signed(re) ? 1 : 0; const char*signed_string = signed_flag? "/s" : ""; /* All the arithmetic operations handled here require that the operands (and the result) be the same width. We further assume that the core has not given us an operand wider then the expression width. So padd operands as needed. */ draw_eval_vec4(le); if (lwid != ewid) { fprintf(vvp_out, " %%pad/%c %u;\n", ivl_expr_signed(le)? 's' : 'u', ewid); } /* Special case: If the re expression can be collected into an immediate operand, and the instruction supports it, then generate an immediate instruction instead of the generic version. */ if (rwid==ewid && test_immediate_vec4_ok(re)) { switch (ivl_expr_opcode(expr)) { case '+': draw_immediate_vec4(re, "%addi"); return; case '-': draw_immediate_vec4(re, "%subi"); return; case '*': draw_immediate_vec4(re, "%muli"); return; default: break; } } draw_eval_vec4(re); if (rwid != ewid) { fprintf(vvp_out, " %%pad/%c %u;\n", ivl_expr_signed(re)? 's' : 'u', ewid); } switch (ivl_expr_opcode(expr)) { case '+': fprintf(vvp_out, " %%add;\n"); break; case '-': fprintf(vvp_out, " %%sub;\n"); break; case '*': fprintf(vvp_out, " %%mul;\n"); break; case '/': fprintf(vvp_out, " %%div%s;\n", signed_string); break; case '%': fprintf(vvp_out, " %%mod%s;\n", signed_string); break; case 'p': /* Note that the power operator is signed if EITHER of the operands is signed. This is different from other arithmetic operators. */ if (ivl_expr_signed(le) || ivl_expr_signed(re)) signed_string = "/s"; fprintf(vvp_out, " %%pow%s;\n", signed_string); break; default: assert(0); break; } } static void draw_binary_vec4_bitwise(ivl_expr_t expr) { draw_eval_vec4(ivl_expr_oper1(expr)); draw_eval_vec4(ivl_expr_oper2(expr)); switch (ivl_expr_opcode(expr)) { case '&': fprintf(vvp_out, " %%and;\n"); break; case '|': fprintf(vvp_out, " %%or;\n"); break; case '^': fprintf(vvp_out, " %%xor;\n"); break; case 'A': /* ~& */ fprintf(vvp_out, " %%nand;\n"); break; case 'O': /* ~| */ fprintf(vvp_out, " %%nor;\n"); break; case 'X': /* ~^ */ fprintf(vvp_out, " %%xnor;\n"); break; default: assert(0); break; } } static void draw_binary_vec4_compare_real(ivl_expr_t expr) { draw_eval_real(ivl_expr_oper1(expr)); draw_eval_real(ivl_expr_oper2(expr)); switch (ivl_expr_opcode(expr)) { case 'e': /* == */ fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); } } static void draw_binary_vec4_compare_string(ivl_expr_t expr) { draw_eval_string(ivl_expr_oper1(expr)); draw_eval_string(ivl_expr_oper2(expr)); switch (ivl_expr_opcode(expr)) { case 'e': /* == */ fprintf(vvp_out, " %%cmp/str;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/str;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); } } static void draw_binary_vec4_compare_class(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); if (ivl_expr_type(le) == IVL_EX_NULL) { ivl_expr_t tmp = le; le = re; re = tmp; } /* Special case: If both operands are null, then the expression has a constant value. */ if (ivl_expr_type(le)==IVL_EX_NULL && ivl_expr_type(re)==IVL_EX_NULL) { switch (ivl_expr_opcode(expr)) { case 'e': /* == */ fprintf(vvp_out, " %%pushi/vec4 1, 0, 1;\n"); break; case 'n': /* != */ fprintf(vvp_out, " %%pushi/vec4 0, 0, 1;\n"); break; default: assert(0); break; } return; } /* A signal/variable is compared to null. Implement this with the %test_nul statement, which peeks at the variable contents directly. */ if (ivl_expr_type(re)==IVL_EX_NULL && ivl_expr_type(le)==IVL_EX_SIGNAL) { ivl_signal_t sig= ivl_expr_signal(le); if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%test_nul v%p_0;\n", sig); } else { ivl_expr_t word_ex = ivl_expr_oper1(le); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%test_nul/a v%p, %d;\n", sig, word_ix); clr_word(word_ix); } fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (ivl_expr_opcode(expr) == 'n') fprintf(vvp_out, " %%inv;\n"); return; } if (ivl_expr_type(re)==IVL_EX_NULL && ivl_expr_type(le)==IVL_EX_PROPERTY) { ivl_signal_t sig = ivl_expr_signal(le); unsigned pidx = ivl_expr_property_idx(le); ivl_expr_t idx_expr = ivl_expr_oper1(le); int idx = 0; /* If the property has an array index, then evaluate it into an index register. */ if ( idx_expr ) { idx = allocate_word(); draw_eval_expr_into_integer(idx_expr, idx); } fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%test_nul/prop %u, %d;\n", pidx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (ivl_expr_opcode(expr) == 'n') fprintf(vvp_out, " %%inv;\n"); if (idx != 0) clr_word(idx); return; } fprintf(stderr, "SORRY: Compare class handles not implemented\n"); fprintf(vvp_out, " ; XXXX compare class handles. re-type=%d, le-type=%d\n", ivl_expr_type(re), ivl_expr_type(le)); vvp_errors += 1; } static void draw_binary_vec4_compare(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); if ((ivl_expr_value(le) == IVL_VT_REAL) || (ivl_expr_value(re) == IVL_VT_REAL)) { draw_binary_vec4_compare_real(expr); return; } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { draw_binary_vec4_compare_string(expr); return; } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_type(re)==IVL_EX_STRING)) { draw_binary_vec4_compare_string(expr); return; } if ((ivl_expr_type(le)==IVL_EX_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { draw_binary_vec4_compare_string(expr); return; } if ((ivl_expr_value(le)==IVL_VT_CLASS) && (ivl_expr_value(re)==IVL_VT_CLASS)) { draw_binary_vec4_compare_class(expr); return; } unsigned use_wid = ivl_expr_width(le); if (ivl_expr_width(re) > use_wid) use_wid = ivl_expr_width(re); draw_eval_vec4(le); resize_vec4_wid(le, use_wid); draw_eval_vec4(re); resize_vec4_wid(re, use_wid); switch (ivl_expr_opcode(expr)) { case 'e': /* == */ fprintf(vvp_out, " %%cmp/e;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/e;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%inv;\n"); break; case 'E': /* === */ fprintf(vvp_out, " %%cmp/e;\n"); fprintf(vvp_out, " %%flag_get/vec4 6;\n"); break; case 'N': /* !== */ fprintf(vvp_out, " %%cmp/e;\n"); fprintf(vvp_out, " %%flag_get/vec4 6;\n"); fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); } } static void draw_binary_vec4_land(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); /* Push the left expression. Reduce it to a single bit if necessary. */ draw_eval_vec4(le); if (ivl_expr_width(le) > 1) fprintf(vvp_out, " %%or/r;\n"); /* Now push the right expression. Again, reduce to a single bit if necessary. */ draw_eval_vec4(re); if (ivl_expr_width(re) > 1) fprintf(vvp_out, " %%or/r;\n"); fprintf(vvp_out, " %%and;\n"); if (ivl_expr_width(expr) > 1) fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr)); } static void draw_binary_vec4_le_real(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); switch (ivl_expr_opcode(expr)) { case '<': draw_eval_real(le); draw_eval_real(re); fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); break; case 'L': /* <= */ draw_eval_real(le); draw_eval_real(re); fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); fprintf(vvp_out, " %%or;\n"); break; case '>': draw_eval_real(re); draw_eval_real(le); fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); break; case 'G': /* >= */ draw_eval_real(re); draw_eval_real(le); fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); fprintf(vvp_out, " %%or;\n"); break; default: assert(0); break; } } static void draw_binary_vec4_le_string(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); switch (ivl_expr_opcode(expr)) { case '<': draw_eval_string(le); draw_eval_string(re); fprintf(vvp_out, " %%cmp/str;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); break; case 'L': /* <= */ draw_eval_string(le); draw_eval_string(re); fprintf(vvp_out, " %%cmp/str;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); fprintf(vvp_out, " %%or;\n"); break; case '>': draw_eval_string(re); draw_eval_string(le); fprintf(vvp_out, " %%cmp/str;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); break; case 'G': /* >= */ draw_eval_string(re); draw_eval_string(le); fprintf(vvp_out, " %%cmp/str;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); fprintf(vvp_out, " %%or;\n"); break; default: assert(0); break; } } static void draw_binary_vec4_le(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); ivl_expr_t tmp; if ((ivl_expr_value(le) == IVL_VT_REAL) || (ivl_expr_value(re) == IVL_VT_REAL)) { draw_binary_vec4_le_real(expr); return; } char use_opcode = ivl_expr_opcode(expr); char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u'; /* If this is a > or >=, then convert it to < or <= by swapping the operands. Adjust the opcode to match. */ switch (use_opcode) { case 'G': tmp = le; le = re; re = tmp; use_opcode = 'L'; break; case '>': tmp = le; le = re; re = tmp; use_opcode = '<'; break; } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { draw_binary_vec4_le_string(expr); return; } if ((ivl_expr_value(le)==IVL_VT_STRING) && (ivl_expr_type(re)==IVL_EX_STRING)) { draw_binary_vec4_le_string(expr); return; } if ((ivl_expr_type(le)==IVL_EX_STRING) && (ivl_expr_value(re)==IVL_VT_STRING)) { draw_binary_vec4_le_string(expr); return; } /* NOTE: I think I would rather the elaborator handle the operand widths. When that happens, take this code out. */ unsigned use_wid = ivl_expr_width(le); if (ivl_expr_width(re) > use_wid) use_wid = ivl_expr_width(re); draw_eval_vec4(le); resize_vec4_wid(le, use_wid); if (ivl_expr_width(re)==use_wid && test_immediate_vec4_ok(re)) { /* Special case: If the right operand can be handled as an immediate operand, then use that instead. */ char opcode[8]; snprintf(opcode, sizeof opcode, "%%cmpi/%c", s_flag); draw_immediate_vec4(re, opcode); } else { draw_eval_vec4(re); resize_vec4_wid(re, use_wid); fprintf(vvp_out, " %%cmp/%c;\n", s_flag); } switch (use_opcode) { case 'L': fprintf(vvp_out, " %%flag_get/vec4 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 5;\n"); fprintf(vvp_out, " %%or;\n"); break; case '<': fprintf(vvp_out, " %%flag_get/vec4 5;\n"); break; default: assert(0); break; } } static void draw_binary_vec4_lor(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); /* Push the left expression. Reduce it to a single bit if necessary. */ draw_eval_vec4(le); if (ivl_expr_width(le) > 1) fprintf(vvp_out, " %%or/r;\n"); /* Now push the right expression. Again, reduce to a single bit if necessary. */ draw_eval_vec4(re); if (ivl_expr_width(re) > 1) fprintf(vvp_out, " %%or/r;\n"); fprintf(vvp_out, " %%or;\n"); if (ivl_expr_width(expr) > 1) fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr)); } static void draw_binary_vec4_lrs(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); // Push the left expression onto the stack. draw_eval_vec4(le); // Calculate the shift amount into an index register. int use_index_reg = allocate_word(); assert(use_index_reg >= 0); draw_eval_expr_into_integer(re, use_index_reg); // Emit the actual shift instruction. This will pop the top of // the stack and replace it with the result of the shift. switch (ivl_expr_opcode(expr)) { case 'l': /* << */ fprintf(vvp_out, " %%shiftl %d;\n", use_index_reg); break; case 'r': /* >> */ fprintf(vvp_out, " %%shiftr %d;\n", use_index_reg); break; case 'R': /* >>> */ if (ivl_expr_signed(le)) fprintf(vvp_out, " %%shiftr/s %d;\n", use_index_reg); else fprintf(vvp_out, " %%shiftr %d;\n", use_index_reg); break; default: assert(0); break; } clr_word(use_index_reg); } static void draw_binary_vec4(ivl_expr_t expr) { switch (ivl_expr_opcode(expr)) { case 'a': /* Logical && */ draw_binary_vec4_land(expr); break; case '+': case '-': case '*': case '/': case '%': case 'p': /* ** (power) */ draw_binary_vec4_arith(expr); break; case '&': case '|': case '^': case 'A': /* NAND (~&) */ case 'O': /* NOR (~|) */ case 'X': /* exclusive nor (~^) */ draw_binary_vec4_bitwise(expr); break; case 'e': /* == */ case 'E': /* === */ case 'n': /* !== */ case 'N': /* !== */ draw_binary_vec4_compare(expr); break; case 'G': /* >= */ case 'L': /* <= */ case '>': case '<': draw_binary_vec4_le(expr); break; case 'l': /* << */ case 'r': /* >> */ case 'R': /* >>> */ draw_binary_vec4_lrs(expr); break; case 'o': /* || (logical or) */ draw_binary_vec4_lor(expr); break; default: fprintf(stderr, "vvp.tgt error: unsupported binary (%c)\n", ivl_expr_opcode(expr)); assert(0); } } /* * This handles two special cases: * 1) Making a large IVL_EX_NUMBER as an immediate value. In this * case, start with a %pushi/vec4 to get the stack started, then * continue with %concati/vec4 instructions to build that number * up. * * 2) Concatenating a large IVL_EX_NUMBER to the current top of the * stack. In this case, start with %concati/vec4 and continue * generating %concati/vec4 instructions to finish up the large number. */ static void draw_concat_number_vec4(ivl_expr_t expr, int as_concati) { unsigned long val0 = 0; unsigned long valx = 0; unsigned wid = ivl_expr_width(expr); const char*bits = ivl_expr_bits(expr); unsigned idx; int accum = 0; int count_pushi = as_concati? 1 : 0; /* Scan the literal bits, MSB first. */ for (idx = 0 ; idx < wid ; idx += 1) { val0 <<= 1; valx <<= 1; switch (bits[wid-idx-1]) { case '0': break; case '1': val0 |= 1; break; case 'x': val0 |= 1; valx |= 1; break; case 'z': val0 |= 0; valx |= 1; break; default: assert(0); break; } accum += 1; /* Collect as many bits as can be written by a single %pushi/vec4 instruction. This may be more than 32 if the higher bits are zero, but if the currently accumulated value fills what a %pushi/vec4 can do, then write it out, generate a %concat/vec4, and set up to handle more bits. */ if ( (val0|valx) & 0x80000000UL ) { if (count_pushi) { fprintf(vvp_out, " %%concati/vec4 %lu, %lu, %d;\n", val0, valx, accum); } else { fprintf(vvp_out, " %%pushi/vec4 %lu, %lu, %d;\n", val0, valx, accum); } accum = 0; val0 = 0; valx = 0; count_pushi += 1; } } if (accum) { if (count_pushi) { fprintf(vvp_out, " %%concati/vec4 %lu, %lu, %d;\n", val0, valx, accum); } else { fprintf(vvp_out, " %%pushi/vec4 %lu, %lu, %d;\n", val0, valx, accum); } } } static void draw_concat_vec4(ivl_expr_t expr) { /* Repeat the concatenation this many times to make a super-concatenation. */ unsigned repeat = ivl_expr_repeat(expr); /* This is the number of expressions that go into the concatenation. */ unsigned num_sube = ivl_expr_parms(expr); unsigned sub_idx = 0; ivl_expr_t sube; assert(num_sube > 0); /* Start with the most-significant bits. */ sube = ivl_expr_parm(expr, sub_idx); draw_eval_vec4(sube); /* Evaluate, but skip any zero replication expressions at the * head of this concatenation. */ while ((ivl_expr_type(sube) == IVL_EX_CONCAT) && (ivl_expr_repeat(sube) == 0)) { fprintf(vvp_out, " %%pop/vec4 1; skip zero replication\n"); sub_idx += 1; sube = ivl_expr_parm(expr, sub_idx); draw_eval_vec4(sube); } for ( sub_idx += 1 ; sub_idx < num_sube ; sub_idx += 1) { /* Concatenate progressively lower parts. */ sube = ivl_expr_parm(expr, sub_idx); /* Special case: The next expression is a NUMBER that can be concatenated using %concati/vec4 instructions. */ if (ivl_expr_type(sube) == IVL_EX_NUMBER) { draw_concat_number_vec4(sube, 1); continue; } draw_eval_vec4(sube); /* Evaluate, but skip any zero replication expressions in the * rest of this concatenation. */ if ((ivl_expr_type(sube) == IVL_EX_CONCAT) && (ivl_expr_repeat(sube) == 0)) { fprintf(vvp_out, " %%pop/vec4 1; skip zero replication\n"); continue; } fprintf(vvp_out, " %%concat/vec4; draw_concat_vec4\n"); } if (repeat > 1) { fprintf(vvp_out, " %%replicate %u;\n", repeat); } } /* * Push a number into the vec4 stack using %pushi/vec4 * instructions. The %pushi/vec4 instruction can only handle up to 32 * non-zero bits, so if there are more than that, then generate * multiple %pushi/vec4 statements, and use %concat/vec4 statements to * concatenate the vectors into the desired result. */ static void draw_number_vec4(ivl_expr_t expr) { draw_concat_number_vec4(expr, 0); } static void draw_property_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/v %u;\n", pidx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } static void draw_select_vec4(ivl_expr_t expr) { // This is the sub-expression to part-select. ivl_expr_t subexpr = ivl_expr_oper1(expr); // This is the base of the part select ivl_expr_t base = ivl_expr_oper2(expr); // This is the part select width unsigned wid = ivl_expr_width(expr); // Is the select base expression signed or unsigned? char sign_suff = ivl_expr_signed(base)? 's' : 'u'; // Special Case: If the sub-expression is a STRING, then this // is a select from that string. if (ivl_expr_value(subexpr)==IVL_VT_STRING) { assert(base); assert(wid==8); draw_eval_string(subexpr); int base_idx = allocate_word(); draw_eval_expr_into_integer(base, base_idx); fprintf(vvp_out, " %%substr/vec4 %d, %u;\n", base_idx, wid); fprintf(vvp_out, " %%pop/str 1;\n"); clr_word(base_idx); return; } if (ivl_expr_value(subexpr)==IVL_VT_DARRAY) { ivl_signal_t sig = ivl_expr_signal(subexpr); assert(sig); assert( (ivl_signal_data_type(sig)==IVL_VT_DARRAY) || (ivl_signal_data_type(sig)==IVL_VT_QUEUE) ); assert(base); draw_eval_expr_into_integer(base, 3); fprintf(vvp_out, " %%load/dar/vec4 v%p_0;\n", sig); return; } if (test_immediate_vec4_ok(base)) { unsigned long val0, valx; unsigned base_wid; make_immediate_vec4_words(base, &val0, &valx, &base_wid); assert(valx == 0); draw_eval_vec4(subexpr); fprintf(vvp_out, " %%parti/%c %u, %lu, %u;\n", sign_suff, wid, val0, base_wid); } else { draw_eval_vec4(subexpr); draw_eval_vec4(base); fprintf(vvp_out, " %%part/%c %u;\n", sign_suff, wid); } } static void draw_select_pad_vec4(ivl_expr_t expr) { // This is the sub-expression to pad/truncate ivl_expr_t subexpr = ivl_expr_oper1(expr); // This is the target width of the expression unsigned wid = ivl_expr_width(expr); // Push the sub-expression onto the stack. draw_eval_vec4(subexpr); // Special case: The expression is already the correct width, // so there is nothing to be done. if (wid == ivl_expr_width(subexpr)) return; if (ivl_expr_signed(expr)) fprintf(vvp_out, " %%pad/s %u;\n", wid); else fprintf(vvp_out, " %%pad/u %u;\n", wid); } /* * This function handles the special case of a call to the internal * functions $ivl_darray_method$pop_back et al. The first (and only) * argument is the signal that represents a dynamic queue. Generate a * %qpop instruction to pop a value and push it to the vec4 stack. */ static void draw_darray_pop(ivl_expr_t expr) { const char*fb; if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) fb = "b"; else fb = "f"; ivl_expr_t arg = ivl_expr_parm(expr, 0); assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/v v%p_0;\n", fb, ivl_expr_signal(arg)); } static void draw_sfunc_vec4(ivl_expr_t expr) { unsigned parm_count = ivl_expr_parms(expr); /* Special case: If there are no arguments to print, then the %vpi_call statement is easy to draw. */ if (parm_count == 0) { assert(ivl_expr_value(expr)==IVL_VT_LOGIC || ivl_expr_value(expr)==IVL_VT_BOOL); fprintf(vvp_out, " %%vpi_func %u %u \"%s\" %u {0 0 0};\n", ivl_file_table_index(ivl_expr_file(expr)), ivl_expr_lineno(expr), ivl_expr_name(expr), ivl_expr_width(expr)); return; } if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) { draw_darray_pop(expr); return; } if (strcmp(ivl_expr_name(expr),"$ivl_darray_method$pop_front")==0) { draw_darray_pop(expr); return; } draw_vpi_func_call(expr); } static void draw_signal_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); /* Handle the simple case, a signal expression that is a simple vector, no array dimensions. */ if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/vec4 v%p_0;\n", sig); return; } /* calculate the array index... */ int addr_index = allocate_word(); draw_eval_expr_into_integer(ivl_expr_oper1(expr), addr_index); fprintf(vvp_out, " %%load/vec4a v%p, %d;\n", sig, addr_index); clr_word(addr_index); } static void draw_string_vec4(ivl_expr_t expr) { unsigned wid = ivl_expr_width(expr); char*fp = process_octal_codes(ivl_expr_string(expr), wid); char*p = fp; unsigned long tmp = 0; unsigned tmp_wid = 0; int push_flag = 0; for (unsigned idx = 0 ; idx < wid ; idx += 8) { tmp <<= 8; tmp |= (unsigned long)*p; p += 1; tmp_wid += 8; if (tmp_wid == 32) { fprintf(vvp_out, " %%pushi/vec4 %lu, 0, 32; draw_string_vec4\n", tmp); tmp = 0; tmp_wid = 0; if (push_flag == 0) push_flag += 1; else fprintf(vvp_out, " %%concat/vec4; draw_string_vec4\n"); } } if (tmp_wid > 0) { fprintf(vvp_out, " %%pushi/vec4 %lu, 0, %u; draw_string_vec4\n", tmp, tmp_wid); if (push_flag != 0) fprintf(vvp_out, " %%concat/vec4; draw_string_vec4\n"); } free(fp); } static void draw_ternary_vec4(ivl_expr_t expr) { ivl_expr_t cond = ivl_expr_oper1(expr); ivl_expr_t true_ex = ivl_expr_oper2(expr); ivl_expr_t false_ex = ivl_expr_oper3(expr); unsigned lab_true = local_count++; unsigned lab_out = local_count++; int use_flag = draw_eval_condition(cond); /* The condition flag is used after possibly other statements, so we need to put it into a non-common place. Allocate a safe flag bit and move the condition to the flag position. */ if (use_flag < 8) { int tmp_flag = allocate_flag(); assert(tmp_flag >= 8); fprintf(vvp_out, " %%flag_mov %d, %d;\n", tmp_flag, use_flag); use_flag = tmp_flag; } fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d;\n", thread_count, lab_true, use_flag); /* If the condition is true or xz (not false), we need the true expression. If the condition is true, then we ONLY need the true expression. */ draw_eval_vec4(true_ex); fprintf(vvp_out, " %%jmp/1 T_%u.%u, %d;\n", thread_count, lab_out, use_flag); fprintf(vvp_out, "T_%u.%u ; End of true expr.\n", thread_count, lab_true); /* If the condition is false or xz (not true), we need the false expression. If the condition is false, then we ONLY need the false expression. */ draw_eval_vec4(false_ex); fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d;\n", thread_count, lab_out, use_flag); fprintf(vvp_out, " ; End of false expr.\n"); /* Here, the condition is not true or false, it is xz. Both the true and false expressions have been pushed onto the stack, we just need to blend the bits. */ fprintf(vvp_out, " %%blend;\n"); fprintf(vvp_out, "T_%u.%u;\n", thread_count, lab_out); clr_flag(use_flag); } static void draw_unary_inc_dec(ivl_expr_t sub, bool incr, bool pre) { ivl_signal_t sig = 0; unsigned wid = 0; switch (ivl_expr_type(sub)) { case IVL_EX_SELECT: { ivl_expr_t e1 = ivl_expr_oper1(sub); sig = ivl_expr_signal(e1); wid = ivl_expr_width(e1); break; } case IVL_EX_SIGNAL: sig = ivl_expr_signal(sub); wid = ivl_expr_width(sub); break; default: assert(0); break; } draw_eval_vec4(sub); const char*cmd = incr? "%add" : "%sub"; if (pre) { /* prefix means we add the result first, and store the result, as well as leaving a copy on the stack. */ fprintf(vvp_out, " %%pushi/vec4 1, 0, %u;\n", wid); fprintf(vvp_out, " %s;\n", cmd); fprintf(vvp_out, " %%dup/vec4;\n"); fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", sig, wid); } else { /* The post-fix decrement returns the non-decremented version, so there is a slight re-arrange. */ fprintf(vvp_out, " %%dup/vec4;\n"); fprintf(vvp_out, " %%pushi/vec4 1, 0, %u;\n", wid); fprintf(vvp_out, " %s;\n", cmd); fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", sig, wid); } } static void draw_unary_vec4(ivl_expr_t expr) { ivl_expr_t sub = ivl_expr_oper1(expr); if (debug_draw) { fprintf(vvp_out, " ; %s:%u:draw_unary_vec4: opcode=%c\n", ivl_expr_file(expr), ivl_expr_lineno(expr), ivl_expr_opcode(expr)); } switch (ivl_expr_opcode(expr)) { case '&': draw_eval_vec4(sub); fprintf(vvp_out, " %%and/r;\n"); break; case '|': draw_eval_vec4(sub); fprintf(vvp_out, " %%or/r;\n"); break; case '^': draw_eval_vec4(sub); fprintf(vvp_out, " %%xor/r;\n"); break; case '~': draw_eval_vec4(sub); fprintf(vvp_out, " %%inv;\n"); break; case '!': draw_eval_vec4(sub); fprintf(vvp_out, " %%nor/r;\n"); break; case '-': draw_eval_vec4(sub); fprintf(vvp_out, " %%inv;\n"); fprintf(vvp_out, " %%pushi/vec4 1, 0, %u;\n", ivl_expr_width(sub)); fprintf(vvp_out, " %%add;\n"); break; case 'A': /* nand (~&) */ draw_eval_vec4(sub); fprintf(vvp_out, " %%nand/r;\n"); break; case 'D': /* pre-decrement (--x) */ draw_unary_inc_dec(sub, false, true); break; case 'd': /* post_decrement (x--) */ draw_unary_inc_dec(sub, false, false); break; case 'I': /* pre-increment (++x) */ draw_unary_inc_dec(sub, true, true); break; case 'i': /* post-increment (x++) */ draw_unary_inc_dec(sub, true, false); break; case 'N': /* nor (~|) */ draw_eval_vec4(sub); fprintf(vvp_out, " %%nor/r;\n"); break; case 'X': /* xnor (~^) */ draw_eval_vec4(sub); fprintf(vvp_out, " %%xnor/r;\n"); break; case 'm': /* abs(m) */ draw_eval_vec4(sub); if (! ivl_expr_signed(sub)) break; /* Test if (m) < 0 */ fprintf(vvp_out, " %%dup/vec4;\n"); fprintf(vvp_out, " %%pushi/vec4 0, 0, %u;\n", ivl_expr_width(sub)); fprintf(vvp_out, " %%cmp/s;\n"); fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, local_count); /* If so, calculate -(m) */ fprintf(vvp_out, " %%inv;\n"); fprintf(vvp_out, " %%pushi/vec4 1, 0, %u;\n", ivl_expr_width(sub)); fprintf(vvp_out, " %%add;\n"); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_count); local_count += 1; break; case 'v': /* Cast real to vec4 */ assert(ivl_expr_value(sub) == IVL_VT_REAL); draw_eval_real(sub); fprintf(vvp_out, " %%cvt/vr %u;\n", ivl_expr_width(expr)); break; case '2': /* Cast expression to bool */ switch (ivl_expr_value(sub)) { case IVL_VT_LOGIC: draw_eval_vec4(sub); fprintf(vvp_out, " %%cast2;\n"); resize_vec4_wid(sub, ivl_expr_width(expr)); break; case IVL_VT_BOOL: draw_eval_vec4(sub); resize_vec4_wid(sub, ivl_expr_width(expr)); break; case IVL_VT_REAL: draw_eval_real(sub); fprintf(vvp_out, " %%cvt/vr %u;\n", ivl_expr_width(expr)); break; default: assert(0); break; } break; default: fprintf(stderr, "XXXX Unary operator %c not implemented\n", ivl_expr_opcode(expr)); break; } } void draw_eval_vec4(ivl_expr_t expr) { if (debug_draw) { fprintf(vvp_out, " ; %s:%u:draw_eval_vec4: expr_type=%d\n", ivl_expr_file(expr), ivl_expr_lineno(expr), ivl_expr_type(expr)); } switch (ivl_expr_type(expr)) { case IVL_EX_BINARY: draw_binary_vec4(expr); return; case IVL_EX_CONCAT: draw_concat_vec4(expr); return; case IVL_EX_NUMBER: draw_number_vec4(expr); return; case IVL_EX_SELECT: if (ivl_expr_oper2(expr)==0) draw_select_pad_vec4(expr); else draw_select_vec4(expr); return; case IVL_EX_SFUNC: draw_sfunc_vec4(expr); return; case IVL_EX_SIGNAL: draw_signal_vec4(expr); return; case IVL_EX_STRING: draw_string_vec4(expr); return; case IVL_EX_TERNARY: draw_ternary_vec4(expr); return; case IVL_EX_UFUNC: draw_ufunc_vec4(expr); return; case IVL_EX_UNARY: draw_unary_vec4(expr); return; case IVL_EX_PROPERTY: draw_property_vec4(expr); return; default: break; } fprintf(stderr, "XXXX Evaluate VEC4 expression (%d)\n", ivl_expr_type(expr)); fprintf(vvp_out, "; XXXX Evaluate VEC4 expression (%d)\n", ivl_expr_type(expr)); } iverilog-10_1/tgt-vvp/modpath.c000066400000000000000000000116611265551621300165560ustar00rootroot00000000000000/* * Copyright (c) 2007-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include "ivl_alloc.h" static ivl_signal_t find_path_source_port(ivl_delaypath_t path) { unsigned idx; ivl_nexus_t nex = ivl_path_source(path); ivl_scope_t path_scope = ivl_path_scope(path); for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; if (ivl_signal_port(sig) == IVL_SIP_NONE) continue; /* The path source scope must match the modpath scope.*/ if (ivl_signal_scope(sig) != path_scope) continue; return sig; } return 0; } /* * Draw a .modpath record. The label is the label to use for this * record. The driver is the label of the net that feeds into the * modpath device. (Note that there is only 1 driver.) The path_sig is * the signal that is the output of this modpath. From that signal we * can find all the modpath source nodes to generate the complete * modpath record. */ static void draw_modpath_record(const char*label, const char*driver, ivl_signal_t path_sig) { unsigned idx; typedef const char*ccharp; ccharp*src_drivers; ccharp*con_drivers; unsigned width = ivl_signal_width(path_sig); src_drivers = calloc(ivl_signal_npath(path_sig), sizeof(ccharp)); con_drivers = calloc(ivl_signal_npath(path_sig), sizeof(ccharp)); for (idx = 0 ; idx < ivl_signal_npath(path_sig) ; idx += 1) { ivl_delaypath_t path = ivl_signal_path(path_sig, idx); ivl_nexus_t src = ivl_path_source(path); ivl_nexus_t con = ivl_path_condit(path); src_drivers[idx] = draw_net_input(src); if (con) con_drivers[idx] = draw_net_input(con); else if (ivl_path_is_condit(path)) con_drivers[idx] = ""; else con_drivers[idx] = 0; } fprintf(vvp_out, " .scope S_%p;\n", ivl_path_scope(ivl_signal_path(path_sig,0))); fprintf(vvp_out, "%s .modpath %u %s v%p_0", label, width, driver, path_sig); for (idx = 0 ; idx < ivl_signal_npath(path_sig); idx += 1) { ivl_delaypath_t path = ivl_signal_path(path_sig, idx); int ppos = ivl_path_source_posedge(path); int pneg = ivl_path_source_negedge(path); const char*edge = ppos? " +" : pneg ? " -" : ""; ivl_signal_t src_sig; fprintf(vvp_out, ",\n %s%s", src_drivers[idx], edge); fprintf(vvp_out, " (%"PRIu64",%"PRIu64",%"PRIu64 ", %"PRIu64",%"PRIu64",%"PRIu64 ", %"PRIu64",%"PRIu64",%"PRIu64 ", %"PRIu64",%"PRIu64",%"PRIu64, ivl_path_delay(path, IVL_PE_01), ivl_path_delay(path, IVL_PE_10), ivl_path_delay(path, IVL_PE_0z), ivl_path_delay(path, IVL_PE_z1), ivl_path_delay(path, IVL_PE_1z), ivl_path_delay(path, IVL_PE_z0), ivl_path_delay(path, IVL_PE_0x), ivl_path_delay(path, IVL_PE_x1), ivl_path_delay(path, IVL_PE_1x), ivl_path_delay(path, IVL_PE_x0), ivl_path_delay(path, IVL_PE_xz), ivl_path_delay(path, IVL_PE_zx)); if (con_drivers[idx]) { fprintf(vvp_out, " ? %s", con_drivers[idx]); } fprintf(vvp_out, ")"); src_sig = find_path_source_port(path); fprintf(vvp_out, " v%p_0", src_sig); } fprintf(vvp_out, ";\n"); free(src_drivers); free(con_drivers); } struct modpath_item { ivl_signal_t path_sig; char*drive_label; struct modpath_item*next; }; static struct modpath_item*modpath_list = 0; void draw_modpath(ivl_signal_t path_sig, char*drive_label) { struct modpath_item*cur = calloc(1, sizeof(struct modpath_item)); cur->path_sig = path_sig; cur->drive_label = drive_label; cur->next = modpath_list; modpath_list = cur; } void cleanup_modpath(void) { while (modpath_list) { struct modpath_item*cur = modpath_list; char modpath_label[64]; modpath_list = cur->next; snprintf(modpath_label, sizeof modpath_label, "V_%p/m", cur->path_sig); draw_modpath_record(modpath_label, cur->drive_label, cur->path_sig); free(cur->drive_label); free(cur); } } iverilog-10_1/tgt-vvp/stmt_assign.c000066400000000000000000000747511265551621300174660ustar00rootroot00000000000000/* * Copyright (c) 2011-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include /* * These functions handle the blocking assignment. Use the %set * instruction to perform the actual assignment, and calculate any * lvalues and rvalues that need calculating. * * The set_to_lvariable function takes a particular nexus and generates * the %set statements to assign the value. * * The show_stmt_assign function looks at the assign statement, scans * the l-values, and matches bits of the r-value with the correct * nexus. */ enum slice_type_e { SLICE_NO_TYPE = 0, SLICE_SIMPLE_VECTOR, SLICE_PART_SELECT_STATIC, SLICE_PART_SELECT_DYNAMIC, SLICE_MEMORY_WORD_STATIC, SLICE_MEMORY_WORD_DYNAMIC }; struct vec_slice_info { enum slice_type_e type; union { struct { unsigned long use_word; } simple_vector; struct { unsigned long part_off; } part_select_static; struct { /* Index reg that holds the memory word index */ int word_idx_reg; /* Stored x/non-x flag */ unsigned x_flag; } part_select_dynamic; struct { unsigned long use_word; } memory_word_static; struct { /* Index reg that holds the memory word index */ int word_idx_reg; /* Stored x/non-x flag */ unsigned x_flag; } memory_word_dynamic; } u_; }; static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned wid) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned long part_off = 0; /* Although Verilog doesn't support it, we'll handle here the case of an l-value part select of an array word if the address is constant. */ ivl_expr_t word_ix = ivl_lval_idx(lval); unsigned long use_word = 0; if (part_off_ex == 0) { part_off = 0; } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } /* If the word index is a constant expression, then evaluate it to select the word, and pay no further heed to the expression itself. */ if (word_ix && number_is_immediate(word_ix, IMM_WID, 0)) { assert(! number_is_unknown(word_ix)); use_word = get_number_immediate(word_ix); word_ix = 0; } if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0 && part_off==0 && wid==ivl_signal_width(sig)) { slice->type = SLICE_SIMPLE_VECTOR; slice->u_.simple_vector.use_word = use_word; fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); } else if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0) { assert(use_word == 0); slice->type = SLICE_PART_SELECT_STATIC; slice->u_.part_select_static.part_off = part_off; fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); fprintf(vvp_out, " %%pushi/vec4 %lu, 0, 32;\n", part_off); fprintf(vvp_out, " %%part/u %u;\n", wid); } else if (ivl_signal_dimensions(sig)==0 && part_off_ex!=0 && word_ix==0) { assert(use_word == 0); assert(part_off == 0); slice->type = SLICE_PART_SELECT_DYNAMIC; slice->u_.part_select_dynamic.word_idx_reg = allocate_word(); slice->u_.part_select_dynamic.x_flag = allocate_flag(); fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); draw_eval_vec4(part_off_ex); fprintf(vvp_out, " %%flag_mov %u, 4;\n", slice->u_.part_select_dynamic.x_flag); fprintf(vvp_out, " %%dup/vec4;\n"); fprintf(vvp_out, " %%ix/vec4 %d;\n", slice->u_.part_select_dynamic.word_idx_reg); fprintf(vvp_out, " %%part/u %u;\n", wid); } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { slice->type = SLICE_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); fprintf(vvp_out, " %%load/vec4a v%p, 3;\n", sig); } else { assert(wid <= 32); fprintf(vvp_out, " %%pushi/vec4 4294967295, 4294967295, %u;\n", wid); } } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { slice->type = SLICE_MEMORY_WORD_DYNAMIC; slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); slice->u_.memory_word_dynamic.x_flag = allocate_flag(); draw_eval_expr_into_integer(word_ix, slice->u_.memory_word_dynamic.word_idx_reg); fprintf(vvp_out, " %%flag_mov %u, 4;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%load/vec4a v%p, %d;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); } else { assert(0); } } /* * This loads the l-value values into the top of the stack, and also * leaves in the slices the information needed to store the slice * results back. */ static void get_vec_from_lval(ivl_statement_t net, struct vec_slice_info*slices) { unsigned lidx; unsigned cur_bit; unsigned wid = ivl_stmt_lwidth(net); cur_bit = 0; for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval; unsigned bit_limit = wid - cur_bit; lval = ivl_stmt_lval(net, lidx); if (bit_limit > ivl_lval_width(lval)) bit_limit = ivl_lval_width(lval); get_vec_from_lval_slice(lval, slices+lidx, bit_limit); if (lidx > 0) { fprintf(vvp_out, " %%concat/vec4;\n"); } cur_bit += bit_limit; } } static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned wid) { //unsigned skip_set = transient_id++; ivl_signal_t sig = ivl_lval_sig(lval); int part_off_idx; /* If the slice of the l-value is a BOOL variable, then cast the data to a BOOL vector so that the stores can be valid. */ if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); } switch (slice->type) { default: fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); assert(0); break; case SLICE_SIMPLE_VECTOR: fprintf(vvp_out, " %%store/vec4 v%p_%lu, 0, %u;\n", sig, slice->u_.simple_vector.use_word, wid); break; case SLICE_PART_SELECT_STATIC: part_off_idx = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", part_off_idx, slice->u_.part_select_static.part_off); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/vec4 v%p_0, %d, %u;\n", sig, part_off_idx, wid); clr_word(part_off_idx); break; case SLICE_PART_SELECT_DYNAMIC: fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.part_select_dynamic.x_flag); fprintf(vvp_out, " %%store/vec4 v%p_0, %d, %u;\n", sig, slice->u_.part_select_dynamic.word_idx_reg, wid); clr_word(slice->u_.part_select_dynamic.word_idx_reg); clr_flag(slice->u_.part_select_dynamic.x_flag); break; case SLICE_MEMORY_WORD_STATIC: if (slice->u_.memory_word_static.use_word < ivl_signal_array_count(sig)) { int word_idx = allocate_word(); fprintf(vvp_out," %%flag_set/imm 4, 0;\n"); fprintf(vvp_out," %%ix/load %d, %lu, 0;\n", word_idx, slice->u_.memory_word_static.use_word); fprintf(vvp_out," %%store/vec4a v%p, %d, 0;\n", sig, word_idx); clr_word(word_idx); } else { fprintf(vvp_out," ; Skip this slice write to v%p [%lu]\n", sig, slice->u_.memory_word_static.use_word); } break; case SLICE_MEMORY_WORD_DYNAMIC: fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%store/vec4a v%p, %d, 0;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); clr_word(slice->u_.memory_word_dynamic.word_idx_reg); clr_flag(slice->u_.memory_word_dynamic.x_flag); break; } } static void put_vec_to_lval(ivl_statement_t net, struct vec_slice_info*slices) { unsigned lidx; unsigned cur_bit; unsigned wid = ivl_stmt_lwidth(net); cur_bit = 0; for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval; unsigned bit_limit = wid - cur_bit; lval = ivl_stmt_lval(net, lidx); if (bit_limit > ivl_lval_width(lval)) bit_limit = ivl_lval_width(lval); if (lidx+1 < ivl_stmt_lvals(net)) fprintf(vvp_out, " %%split/vec4 %u;\n", bit_limit); put_vec_to_lval_slice(lval, slices+lidx, bit_limit); cur_bit += bit_limit; } } static ivl_type_t draw_lval_expr(ivl_lval_t lval) { ivl_lval_t lval_nest = ivl_lval_nest(lval); ivl_signal_t lval_sig = ivl_lval_sig(lval); ivl_type_t sub_type; if (lval_nest) { sub_type = draw_lval_expr(lval_nest); } else { assert(lval_sig); sub_type = ivl_signal_net_type(lval_sig); assert(ivl_type_base(sub_type) == IVL_VT_CLASS); fprintf(vvp_out, " %%load/obj v%p_0;\n", lval_sig); } assert(ivl_type_base(sub_type) == IVL_VT_CLASS); if (ivl_lval_idx(lval)) { fprintf(vvp_out, " ; XXXX Don't know how to handle ivl_lval_idx values here.\n"); } fprintf(vvp_out, " %%prop/obj %d, 0; draw_lval_expr\n", ivl_lval_property_idx(lval)); fprintf(vvp_out, " %%pop/obj 1, 1;\n"); return ivl_type_prop_type(sub_type, ivl_lval_property_idx(lval)); } /* * Store a vector from the vec4 stack to the statement l-values. This * all assumes that the value to be assigned is already on the top of * the stack. * * NOTE TO SELF: The %store/vec4 takes a width, but the %assign/vec4 * instructions do not, instead relying on the expression width. I * think that it the proper way to do it, so soon I should change the * %store/vec4 to not include the width operand. */ static void store_vec4_to_lval(ivl_statement_t net) { for (unsigned lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval = ivl_stmt_lval(net,lidx); ivl_signal_t lsig = ivl_lval_sig(lval); ivl_lval_t nest = ivl_lval_nest(lval); unsigned lwid = ivl_lval_width(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); /* This is non-nil if the l-val is the word of a memory, and nil otherwise. */ ivl_expr_t word_ex = ivl_lval_idx(lval); if (lidx+1 < ivl_stmt_lvals(net)) fprintf(vvp_out, " %%split/vec4 %u;\n", lwid); if (word_ex) { /* Handle index into an array */ int word_index = allocate_word(); int part_index = 0; /* Calculate the word address into word_index */ draw_eval_expr_into_integer(word_ex, word_index); /* If there is a part_offset, calculate it into part_index. */ if (part_off_ex) { int flag_index = allocate_flag(); part_index = allocate_word(); fprintf(vvp_out, " %%flag_mov %d, 4;\n", flag_index); draw_eval_expr_into_integer(part_off_ex, part_index); fprintf(vvp_out, " %%flag_or 4, %d;\n", flag_index); clr_flag(flag_index); } assert(lsig); fprintf(vvp_out, " %%store/vec4a v%p, %d, %d;\n", lsig, word_index, part_index); clr_word(word_index); if (part_index) clr_word(part_index); } else if (part_off_ex) { /* Dynamically calculated part offset */ int offset_index = allocate_word(); draw_eval_expr_into_integer(part_off_ex, offset_index); /* Note that flag4 is set by the eval above. */ assert(lsig); if (ivl_signal_type(lsig)==IVL_SIT_UWIRE) { fprintf(vvp_out, " %%force/vec4/off v%p_0, %d;\n", lsig, offset_index); } else { fprintf(vvp_out, " %%store/vec4 v%p_0, %d, %u;\n", lsig, offset_index, lwid); } clr_word(offset_index); } else if (nest) { /* No offset expression, but the l-value is nested, which probably means that it is a class member. We will use a property assign function. */ assert(!lsig); ivl_type_t sub_type = draw_lval_expr(nest); assert(ivl_type_base(sub_type) == IVL_VT_CLASS); fprintf(vvp_out, " %%store/prop/v %d, %u;\n", ivl_lval_property_idx(lval), lwid); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else { /* No offset expression, so use simpler store function. */ assert(lsig); assert(lwid == ivl_signal_width(lsig)); fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", lsig, lwid); } } } static int show_stmt_assign_vector(ivl_statement_t net) { ivl_expr_t rval = ivl_stmt_rval(net); //struct vector_info res; //struct vector_info lres = {0, 0}; struct vec_slice_info*slices = 0; int idx_reg; /* If this is a compressed assignment, then get the contents of the l-value. We need these values as part of the r-value calculation. */ if (ivl_stmt_opcode(net) != 0) { slices = calloc(ivl_stmt_lvals(net), sizeof(struct vec_slice_info)); get_vec_from_lval(net, slices); } /* Handle the special case that the expression is a real value. Evaluate the real expression, then convert the result to a vector. Then store that vector into the l-value. */ if (ivl_expr_value(rval) == IVL_VT_REAL) { draw_eval_real(rval); /* This is the accumulated with of the l-value of the assignment. */ unsigned wid = ivl_stmt_lwidth(net); /* Convert a calculated real value to a vec4 value of the given width. We need to include the width of the result because real values to not have any inherit width. The real value will be popped, and a vec4 value pushed. */ fprintf(vvp_out, " %%cvt/vr %u;\n", wid); } else if (ivl_expr_value(rval) == IVL_VT_STRING) { /* Special case: string to vector casting */ ivl_lval_t lval = ivl_stmt_lval(net, 0); fprintf(vvp_out, " %%vpi_call %u %u \"$ivl_string_method$to_vec\", v%p_0, v%p_0 {0 0 0};\n", ivl_file_table_index(ivl_stmt_file(net)), ivl_stmt_lineno(net), ivl_expr_signal(rval), ivl_lval_sig(lval)); if (slices) free(slices); return 0; } else if (ivl_expr_value(rval) == IVL_VT_DARRAY) { /* Special case: dynamic array to vector casting */ ivl_lval_t lval = ivl_stmt_lval(net, 0); void*rval_addr = NULL; /* Even more special case: function call returning dynamic array */ if(ivl_expr_type(rval) == IVL_EX_UFUNC) { rval_addr = ivl_scope_port(ivl_expr_def(rval), 0); draw_ufunc_object(rval); /* We do not need to store the result, it is going to be converted to vector quite soon. */ fprintf(vvp_out, " %%pop/obj 1, 0; drop the result\n"); } else { rval_addr = ivl_expr_signal(rval); } fprintf(vvp_out, " %%vpi_call %u %u \"$ivl_darray_method$to_vec\", v%p_0, v%p_0 {0 0 0};\n", ivl_file_table_index(ivl_stmt_file(net)), ivl_stmt_lineno(net), rval_addr, ivl_lval_sig(lval)); if (slices) free(slices); return 0; } else { unsigned wid = ivl_stmt_lwidth(net); draw_eval_vec4(rval); resize_vec4_wid(rval, wid); } switch (ivl_stmt_opcode(net)) { case 0: store_vec4_to_lval(net); break; case '+': fprintf(vvp_out, " %%add;\n"); put_vec_to_lval(net, slices); break; case '-': fprintf(vvp_out, " %%sub;\n"); put_vec_to_lval(net, slices); break; case '*': fprintf(vvp_out, " %%mul;\n"); put_vec_to_lval(net, slices); break; case '/': fprintf(vvp_out, " %%div%s;\n", ivl_expr_signed(rval)? "/s":""); put_vec_to_lval(net, slices); break; case '%': fprintf(vvp_out, " %%mod%s;\n", ivl_expr_signed(rval)? "/s":""); put_vec_to_lval(net, slices); break; case '&': fprintf(vvp_out, " %%and;\n"); put_vec_to_lval(net, slices); break; case '|': fprintf(vvp_out, " %%or;\n"); put_vec_to_lval(net, slices); break; case '^': fprintf(vvp_out, " %%xor;\n"); put_vec_to_lval(net, slices); break; case 'l': /* lval <<= expr */ idx_reg = allocate_word(); fprintf(vvp_out, " %%ix/vec4 %d;\n", idx_reg); fprintf(vvp_out, " %%shiftl %d;\n", idx_reg); clr_word(idx_reg); put_vec_to_lval(net, slices); break; case 'r': /* lval >>= expr */ idx_reg = allocate_word(); fprintf(vvp_out, " %%ix/vec4 %d;\n", idx_reg); fprintf(vvp_out, " %%shiftr %d;\n", idx_reg); clr_word(idx_reg); put_vec_to_lval(net, slices); break; case 'R': /* lval >>>= expr */ idx_reg = allocate_word(); fprintf(vvp_out, " %%ix/vec4 %d;\n", idx_reg); fprintf(vvp_out, " %%shiftr/s %d;\n", idx_reg); clr_word(idx_reg); put_vec_to_lval(net, slices); break; default: fprintf(vvp_out, "; UNSUPPORTED ASSIGNMENT OPCODE: %c\n", ivl_stmt_opcode(net)); assert(0); break; } if (slices) free(slices); return 0; } /* * This function assigns a value to a real variable. This is destined * for /dev/null when typed ivl_signal_t takes over all the real * variable support. */ static int show_stmt_assign_sig_real(ivl_statement_t net) { ivl_lval_t lval; ivl_signal_t var; assert(ivl_stmt_opcode(net) == 0); draw_eval_real(ivl_stmt_rval(net)); assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); var = ivl_lval_sig(lval); assert(var != 0); if (ivl_signal_dimensions(var) == 0) { fprintf(vvp_out, " %%store/real v%p_0;\n", var); return 0; } // For now, only support 1-dimensional arrays. assert(ivl_signal_dimensions(var) == 1); ivl_expr_t word_ex = ivl_lval_idx(lval); int word_ix = allocate_word(); /* If the word index is a constant, then we can write directly to the word and save the index calculation. Out-of-bounds and undefined indices are converted to a canonical index of 'bx during elaboration, and we don't try to optimise that case. */ if (word_ex && number_is_immediate(word_ex, IMM_WID, 0) && !number_is_unknown(word_ex)) { unsigned long use_word = get_number_immediate(word_ex); assert(use_word < ivl_signal_array_count(var)); fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", word_ix, use_word); fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); } else { unsigned do_store = transient_id++; unsigned end_store = transient_id++; draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%jmp/0 t_%u, 4;\n", do_store); fprintf(vvp_out, " %%pop/real 1;\n"); fprintf(vvp_out, " %%jmp t_%u;\n", end_store); fprintf(vvp_out, "t_%u ;\n", do_store); fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); fprintf(vvp_out, "t_%u ;\n", end_store); } clr_word(word_ix); return 0; } static int show_stmt_assign_sig_string(ivl_statement_t net) { ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t part = ivl_lval_part_off(lval); ivl_expr_t aidx = ivl_lval_idx(lval); ivl_signal_t var= ivl_lval_sig(lval); assert(ivl_stmt_lvals(net) == 1); assert(ivl_stmt_opcode(net) == 0); /* Simplest case: no mux. Evaluate the r-value as a string and store the result into the variable. Note that the %store/str opcode pops the string result. */ if (part == 0 && aidx == 0) { draw_eval_string(rval); fprintf(vvp_out, " %%store/str v%p_0;\n", var); return 0; } /* Assign to array. The l-value has an index expression expression so we are assigning to an array word. */ if (aidx != 0) { unsigned ix; assert(part == 0); draw_eval_string(rval); draw_eval_expr_into_integer(aidx, (ix = allocate_word())); fprintf(vvp_out, " %%store/stra v%p, %u;\n", var, ix); clr_word(ix); return 0; } assert(ivl_expr_width(rval)==8); draw_eval_vec4(rval); /* Calculate the character select for the word. */ int mux_word = allocate_word(); draw_eval_expr_into_integer(part, mux_word); fprintf(vvp_out, " %%putc/str/vec4 v%p_0, %d;\n", var, mux_word); clr_word(mux_word); return 0; } unsigned width_of_packed_type(ivl_type_t net) { unsigned idx; unsigned width = 1; for (idx = 0 ; idx < ivl_type_packed_dimensions(net) ; idx += 1) { int lsb = ivl_type_packed_lsb(net,idx); int msb = ivl_type_packed_msb(net,idx); if (lsb <= msb) width *= msb - lsb + 1; else width *= lsb - msb + 1; } return width; } /* * This function handles the special case that we assign an array * pattern to a dynamic array. Handle this by assigning each * element. The array pattern will have a fixed size. */ static int show_stmt_assign_darray_pattern(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_signal_t var= ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); assert(ivl_type_base(var_type) == IVL_VT_DARRAY); ivl_type_t element_type = ivl_type_element(var_type); unsigned idx; #if 0 unsigned element_width = 1; if (ivl_type_base(element_type) == IVL_VT_BOOL) element_width = width_of_packed_type(element_type); else if (ivl_type_base(element_type) == IVL_VT_LOGIC) element_width = width_of_packed_type(element_type); #endif assert(ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN); for (idx = 0 ; idx < ivl_expr_parms(rval) ; idx += 1) { switch (ivl_type_base(element_type)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: draw_eval_vec4(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%store/dar/vec4 v%p_0;\n", var); break; case IVL_VT_REAL: draw_eval_real(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%store/dar/r v%p_0;\n", var); break; case IVL_VT_STRING: draw_eval_string(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%store/dar/str v%p_0;\n", var); break; default: fprintf(vvp_out, "; ERROR: show_stmt_assign_darray_pattern: type_base=%d not implemented\n", ivl_type_base(element_type)); errors += 1; break; } } return errors; } static int show_stmt_assign_sig_darray(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t part = ivl_lval_part_off(lval); ivl_signal_t var= ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); assert(ivl_type_base(var_type) == IVL_VT_DARRAY); ivl_type_t element_type = ivl_type_element(var_type); ivl_expr_t mux = ivl_lval_idx(lval); assert(ivl_stmt_lvals(net) == 1); assert(ivl_stmt_opcode(net) == 0); assert(part == 0); if (mux && (ivl_type_base(element_type)==IVL_VT_REAL)) { draw_eval_real(rval); /* The %set/dar expects the array index to be in index register 3. Calculate the index in place. */ draw_eval_expr_into_integer(mux, 3); fprintf(vvp_out, " %%store/dar/r v%p_0;\n", var); } else if (mux && ivl_type_base(element_type)==IVL_VT_STRING) { /* Evaluate the rval into the top of the string stack. */ draw_eval_string(rval); /* The %store/dar/s expects the array index to me in index register 3. Calculate the index in place. */ draw_eval_expr_into_integer(mux, 3); fprintf(vvp_out, " %%store/dar/str v%p_0;\n", var); } else if (mux) { draw_eval_vec4(rval); /* The %store/dar/vec4 expects the array index to be in index register 3. Calculate the index in place. */ draw_eval_expr_into_integer(mux, 3); fprintf(vvp_out, " %%store/dar/vec4 v%p_0;\n", var); } else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { /* There is no l-value mux, but the r-value is an array pattern. This is a special case of an assignment to elements of the l-value. */ errors += show_stmt_assign_darray_pattern(net); } else { /* There is no l-value mux, so this must be an assignment to the array as a whole. Evaluate the "object", and store the evaluated result. */ errors += draw_eval_object(rval); fprintf(vvp_out, " %%store/obj v%p_0;\n", var); } return errors; } static int show_stmt_assign_sig_queue(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_signal_t var= ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); assert(ivl_type_base(var_type) == IVL_VT_QUEUE); switch (ivl_expr_type(rval)) { case IVL_EX_NULL: errors += draw_eval_object(rval); break; default: fprintf(stderr, "XXXX: I don't know how to handle expr_type=%d here\n", ivl_expr_type(rval)); fprintf(vvp_out, " ; XXXX expr_type=%d\n", ivl_expr_type(rval)); errors += 1; break; } fprintf(vvp_out, " %%store/obj v%p_0;\n", var); return errors; } static int show_stmt_assign_sig_cobject(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_signal_t sig= ivl_lval_sig(lval); unsigned lwid = ivl_lval_width(lval); int prop_idx = ivl_lval_property_idx(lval); if (prop_idx >= 0) { ivl_type_t sig_type = ivl_signal_net_type(sig); ivl_type_t prop_type = ivl_type_prop_type(sig_type, prop_idx); if (ivl_type_base(prop_type) == IVL_VT_BOOL) { assert(ivl_type_packed_dimensions(prop_type) == 1); assert(ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0)); draw_eval_vec4(rval); if (ivl_expr_value(rval)!=IVL_VT_BOOL) fprintf(vvp_out, " %%cast2;\n"); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%store/prop/v %d, %u; Store in bool property %s\n", prop_idx, lwid, ivl_type_prop_name(sig_type, prop_idx)); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_LOGIC) { assert(ivl_type_packed_dimensions(prop_type) == 1); assert(ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0)); draw_eval_vec4(rval); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%store/prop/v %d, %u; Store in logic property %s\n", prop_idx, lwid, ivl_type_prop_name(sig_type, prop_idx)); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_REAL) { /* Calculate the real value into the real value stack. The %store/prop/r will pop the stack value. */ draw_eval_real(rval); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%store/prop/r %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_STRING) { /* Calculate the string value into the string value stack. The %store/prop/r will pop the stack value. */ draw_eval_string(rval); fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%store/prop/str %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY) { int idx = 0; /* The property is a darray, and there is no mux expression to the assignment is of an entire array object. */ fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); errors += draw_eval_object(rval); fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_CLASS) { int idx = 0; ivl_expr_t idx_expr; if ( (idx_expr = ivl_lval_idx(lval)) ) { idx = allocate_word(); } /* The property is a class object. */ fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); errors += draw_eval_object(rval); if (idx_expr) draw_eval_expr_into_integer(idx_expr, idx); fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_CLASS\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); if (idx_expr) clr_word(idx); } else { fprintf(vvp_out, " ; ERROR: ivl_type_base(prop_type) = %d\n", ivl_type_base(prop_type)); assert(0); } } else { /* There is no property select, so evaluate the r-value as an object and assign the entire object to the variable. */ errors += draw_eval_object(rval); if (ivl_signal_array_count(sig) > 1) { unsigned ix; ivl_expr_t aidx = ivl_lval_idx(lval); draw_eval_expr_into_integer(aidx, (ix = allocate_word())); fprintf(vvp_out, " %%store/obja v%p, %u;\n", sig, ix); clr_word(ix); } else { /* Not an array, so no index expression */ fprintf(vvp_out, " %%store/obj v%p_0;\n", sig); } } return errors; } int show_stmt_assign(ivl_statement_t net) { ivl_lval_t lval; ivl_signal_t sig; show_stmt_file_line(net, "Blocking assignment."); lval = ivl_stmt_lval(net, 0); sig = ivl_lval_sig(lval); if (sig && (ivl_signal_data_type(sig) == IVL_VT_REAL)) { return show_stmt_assign_sig_real(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_STRING)) { return show_stmt_assign_sig_string(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_DARRAY)) { return show_stmt_assign_sig_darray(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_QUEUE)) { return show_stmt_assign_sig_queue(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_CLASS)) { return show_stmt_assign_sig_cobject(net); } return show_stmt_assign_vector(net); } iverilog-10_1/tgt-vvp/vector.c000066400000000000000000000016021265551621300164160ustar00rootroot00000000000000/* * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include iverilog-10_1/tgt-vvp/vvp-s.conf.in000066400000000000000000000001371265551621300173010ustar00rootroot00000000000000functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=vvp.tgt iverilog-10_1/tgt-vvp/vvp.c000066400000000000000000000162321265551621300157340ustar00rootroot00000000000000/* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "version_base.h" # include "version_tag.h" # include "vvp_priv.h" # include # include # include # include # include static const char*version_string = "Icarus Verilog VVP Code Generator " VERSION " (" VERSION_TAG ")\n\n" "Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; FILE*vvp_out = 0; int vvp_errors = 0; unsigned show_file_line = 0; int debug_draw = 0; # define FLAGS_COUNT 256 static uint32_t allocate_flag_mask[FLAGS_COUNT / 32] = { 0x000000ff, 0 }; __inline__ static void draw_execute_header(ivl_design_t des) { const char*cp = ivl_design_flag(des, "VVP_EXECUTABLE"); if (cp) { const char *extra_args = ivl_design_flag(des, "VVP_EXTRA_ARGS"); if (!extra_args) extra_args = ""; fprintf(vvp_out, "#! %s%s\n", cp, extra_args); #if !defined(__MINGW32__) fchmod(fileno(vvp_out), 0755); #endif } fprintf(vvp_out, ":ivl_version \"" VERSION "\""); /* I am assuming that a base release will have a blank tag. */ if (*VERSION_TAG != 0) { fprintf(vvp_out, " \"(" VERSION_TAG ")\""); } fprintf(vvp_out, ";\n"); } __inline__ static void draw_module_declarations(ivl_design_t des) { const char*cp = ivl_design_flag(des, "VPI_MODULE_LIST"); while (*cp) { char buffer[128]; const char*comma = strchr(cp, ','); if (comma == 0) comma = cp + strlen(cp); strncpy(buffer, cp, comma-cp); buffer[comma-cp] = 0; fprintf(vvp_out, ":vpi_module \"%s\";\n", buffer); cp = comma; if (*cp) cp += 1; } } int allocate_flag(void) { int idx; for (idx = 0 ; idx < FLAGS_COUNT ; idx += 1) { int word = idx / 32; uint32_t mask = 1 << (idx%32); if (allocate_flag_mask[word] & mask) continue; allocate_flag_mask[word] |= mask; return idx; } return -1; } void clr_flag(int idx) { if (idx < 8) return; assert(idx < FLAGS_COUNT); int word = idx / 32; uint32_t mask = 1 << (idx%32); assert(allocate_flag_mask[word] & mask); allocate_flag_mask[word] &= ~mask; } static void process_debug_string(const char*debug_string) { const char*cp = debug_string; debug_draw = 0; while (*cp) { const char*tail = strchr(cp, ','); if (tail == 0) tail = cp + strlen(cp); size_t len = tail - cp; if (len == 4 && strncmp(cp,"draw", 4)==0) { debug_draw = 1; } while (*tail == ',') tail += 1; cp = tail; } } int target_design(ivl_design_t des) { int rc; ivl_scope_t *roots; unsigned nroots, i; unsigned size; unsigned idx; const char*path = ivl_design_flag(des, "-o"); /* Use -pfileline to determine if file and line information is * printed for procedural statements. (e.g. -pfileline=1). * The default is no file/line information will be included. */ const char*fileline = ivl_design_flag(des, "fileline"); const char*debug_flags = ivl_design_flag(des, "debug_flags"); process_debug_string(debug_flags); assert(path); /* Check to see if file/line information should be included. */ if (strcmp(fileline, "") != 0) { char *eptr; long fl_value = strtol(fileline, &eptr, 0); /* Nothing usable in the file/line string. */ if (fileline == eptr) { fprintf(stderr, "vvp error: Unable to extract file/line " "information from string: %s\n", fileline); return 1; } /* Extra stuff at the end. */ if (*eptr != 0) { fprintf(stderr, "vvp error: Extra characters '%s' " "included at end of file/line string: %s\n", eptr, fileline); return 1; } /* The file/line flag must be positive. */ if (fl_value < 0) { fprintf(stderr, "vvp error: File/line flag (%ld) must " "be positive.\n", fl_value); return 1; } show_file_line = fl_value > 0; } #ifdef HAVE_FOPEN64 vvp_out = fopen64(path, "w"); #else vvp_out = fopen(path, "w"); #endif if (vvp_out == 0) { perror(path); return -1; } vvp_errors = 0; draw_execute_header(des); fprintf(vvp_out, ":ivl_delay_selection \"%s\";\n", ivl_design_delay_sel(des)); { int pre = ivl_design_time_precision(des); char sign = '+'; if (pre < 0) { pre = -pre; sign = '-'; } fprintf(vvp_out, ":vpi_time_precision %c %d;\n", sign, pre); } draw_module_declarations(des); /* This causes all structural records to be drawn. */ ivl_design_roots(des, &roots, &nroots); for (i = 0; i < nroots; i++) draw_scope(roots[i], 0); /* Finish up any modpaths that are not yet emitted. */ cleanup_modpath(); rc = ivl_design_process(des, draw_process, 0); /* Dump the file name table. */ size = ivl_file_table_size(); fprintf(vvp_out, "# The file index is used to find the file name in " "the following table.\n:file_names %u;\n", size); for (idx = 0; idx < size; idx++) { fprintf(vvp_out, " \"%s\";\n", ivl_file_table_item(idx)); } fclose(vvp_out); EOC_cleanup_drivers(); return rc + vvp_errors; } const char* target_query(const char*key) { if (strcmp(key,"version") == 0) return version_string; return 0; } iverilog-10_1/tgt-vvp/vvp.conf.in000066400000000000000000000000601265551621300170340ustar00rootroot00000000000000functor:cprop functor:nodangle flag:DLL=vvp.tgt iverilog-10_1/tgt-vvp/vvp_config.h.in000066400000000000000000000022341265551621300176700ustar00rootroot00000000000000#ifndef IVL_vvp_config_H #define IVL_vvp_config_H /* * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined(__cplusplus) # if !defined(__GNUC__) using namespace std; # elif (__GNUC__ == 3) using namespace std; # endif #endif # undef HAVE_STDINT_H # undef HAVE_INTTYPES_H # undef _LARGEFILE_SOURCE # undef _LARGEFILE64_SOURCE #endif /* IVL_vvp_config_H */ iverilog-10_1/tgt-vvp/vvp_priv.h000066400000000000000000000211351265551621300167770ustar00rootroot00000000000000#ifndef IVL_vvp_priv_H #define IVL_vvp_priv_H /* * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_config.h" # include "ivl_target.h" # include extern int debug_draw; /* * The target_design entry opens the output file that receives the * compiled design, and sets the vvp_out to the descriptor. */ extern FILE* vvp_out; /* * Keep a count of errors that would render the output unusable. */ extern int vvp_errors; extern unsigned transient_id; /* * Set to non-zero when the user wants to display file and line number * information for procedural statements. */ extern unsigned show_file_line; struct vector_info { unsigned base; unsigned wid; }; /* * Convenient constants... */ /* Width limit for typical immediate arguments. */ # define IMM_WID 32 /* The number of words available in a thread. */ # define WORD_COUNT 16 /* * Mangle all non-symbol characters in an identifier, quotes in names */ extern const char *vvp_mangle_id(const char *); extern const char *vvp_mangle_name(const char *); extern char* draw_Cr_to_string(double value); /* * This generates a string from a signal that uniquely identifies * that signal with letters that can be used in a label. * * NOTE: vvp_signal_label should be removed. All it does is a %p of * the pointer, and return a pointer to a static. The code that wants * to reference a signal needs to use the format V_%p, so the presence * of this function is just plain inconsistent. */ extern const char* vvp_signal_label(ivl_signal_t sig); extern unsigned width_of_nexus(ivl_nexus_t nex); extern ivl_variable_type_t data_type_of_nexus(ivl_nexus_t nex); /* * Calculate the width (in bits) of a packed type. */ extern unsigned width_of_packed_type(ivl_type_t net); extern int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr); /* * This function draws a process (initial or always) into the output * file. It normally returns 0, but returns !0 of there is some sort * of error. */ extern int draw_process(ivl_process_t net, void*x); extern int draw_task_definition(ivl_scope_t scope); extern int draw_func_definition(ivl_scope_t scope); extern int draw_scope(ivl_scope_t scope, ivl_scope_t parent); extern void draw_lpm_mux(ivl_lpm_t net); extern void draw_lpm_substitute(ivl_lpm_t net); extern void draw_ufunc_vec4(ivl_expr_t expr); extern void draw_ufunc_real(ivl_expr_t expr); extern void draw_ufunc_string(ivl_expr_t expr); extern void draw_ufunc_object(ivl_expr_t expr); extern char* process_octal_codes(const char*txt, unsigned wid); /* * modpath.c symbols. * * draw_modpath arranges for a .modpath record to be written out. * * cleanup_modpath() cleans up any pending .modpath records that may * have been scheduled by draw_modpath() but not yet written. * * Note: draw_modpath drive_label must be malloc'ed by the * caller. This function will free the string sometime in the future. */ extern void draw_modpath(ivl_signal_t path_sig, char*drive_label); extern void cleanup_modpath(void); /* * This function draws the execution of a vpi_call statement, along * with the tricky handling of arguments. If this is called with a * statement handle, it will generate a %vpi_call * instruction. Otherwise, it will generate a %vpi_func instruction. */ extern void draw_vpi_task_call(ivl_statement_t net); extern void draw_vpi_func_call(ivl_expr_t expr); extern void draw_vpi_rfunc_call(ivl_expr_t expr); extern void draw_class_in_scope(ivl_type_t classtype); /* * Enumeration draw routine. */ void draw_enumeration_in_scope(ivl_enumtype_t enumtype); /* * Switches (tran) */ extern void draw_switch_in_scope(ivl_switch_t sw); /* Draw_net_input and friends uses this. */ struct vvp_nexus_data { /* draw_net_input uses this */ const char*net_input; /* draw_isnald_net_input uses these */ const char*island_input; ivl_island_t island; /* */ unsigned drivers_count; int flags; /* draw_net_in_scope uses these to identify the controlling word. */ ivl_signal_t net; unsigned net_word; }; #define VVP_NEXUS_DATA_STR 0x0001 /* * Given a nexus, draw a string that represents the functor output * that feeds the nexus. This function can be used to get the input to * a functor, event, or even a %load in cases where I have the * ivl_nexus_t object. The draw_net_input function will get the string * cached in the nexus, if there is one, or will generate a string and * cache it. */ extern const char* draw_net_input(ivl_nexus_t nex); void EOC_cleanup_drivers(void); /* * This is different from draw_net_input in that it is intended to be * used within the network of an island. This finds and prepares the * link for a nexus within the scope of the given island, instead of * the net as a whole. */ extern const char* draw_island_net_input(ivl_island_t island, ivl_nexus_t nex); /* * This function is different from draw_net_input in that it will * return a reference to a net as its first choice. This reference * will follow the net value, even if the net is assigned or * forced. The draw_net_input above will return a reference to the * *input* to the net and so will not follow direct assignments to * the net. This function will not return references to local signals, * and will in those cases resort to the net input, or a non-local * signal if one exists for the nexus. */ extern const char*draw_input_from_net(ivl_nexus_t nex); /* * This evaluates an expression and leaves the result in the numbered * integer index register. It also will set bit-4 to 1 if the value is * not fully defined (i.e. contains x or z). */ extern void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix); /* * This evaluates an expression as a condition flag and leaves the * result in a flag that is returned. This result may be used as an * operand for conditional jump instructions. */ extern int draw_eval_condition(ivl_expr_t expr); extern int number_is_unknown(ivl_expr_t ex); extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok); extern long get_number_immediate(ivl_expr_t ex); extern uint64_t get_number_immediate64(ivl_expr_t ex); /* * draw_eval_vec4 evaluates vec4 expressions. The result of the * evaluation is the vec4 result in the top of the vec4 expression stack. */ extern void draw_eval_vec4(ivl_expr_t ex); extern void resize_vec4_wid(ivl_expr_t expr, unsigned wid); /* * draw_eval_real evaluates real value expressions. The result of the * evaluation is the real result in the top of the real expression stack. */ extern void draw_eval_real(ivl_expr_t ex); /* * draw_eval_bool64 evaluates a bool expression. The return code from * the function is the index of the word register that contains the * result. The word is allocated with allocate_word(), so the caller * must arrange for it to be released with clr_word(). The width must * be such that it fits in a 64bit word. */ extern int draw_eval_bool64(ivl_expr_t ex); /* * The draw_eval_string function evaluates the expression as a string, * and pushes the string onto the string stack. */ extern void draw_eval_string(ivl_expr_t ex); /* * The draw_eval_string function evaluates the expression as an object, * and pushes the object onto the object stack. */ extern int draw_eval_object(ivl_expr_t ex); extern int show_stmt_assign(ivl_statement_t net); extern void show_stmt_file_line(ivl_statement_t net, const char*desc); /* */ extern int test_immediate_vec4_ok(ivl_expr_t expr); extern void draw_immediate_vec4(ivl_expr_t expr, const char*opcode); /* * These functions manage word register allocation. */ extern int allocate_word(void); extern void clr_word(int idx); /* * These functions manage flag bit allocation. */ extern int allocate_flag(void); extern void clr_flag(int idx); /* * These are used to count labels as I generate code. */ extern unsigned local_count; extern unsigned thread_count; #endif /* IVL_vvp_priv_H */ iverilog-10_1/tgt-vvp/vvp_process.c000066400000000000000000002210121265551621300174640ustar00rootroot00000000000000/* * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include static int show_statement(ivl_statement_t net, ivl_scope_t sscope); unsigned local_count = 0; unsigned thread_count = 0; unsigned transient_id = 0; /* * This file includes the code needed to generate VVP code for * processes. Scopes are already declared, we generate here the * executable code for the processes. */ /* Support a non-blocking assignment to a real array word. The real value to be written is already in the top of the stack. */ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix, uint64_t delay, ivl_expr_t dexp, unsigned nevents) { unsigned end_assign = transient_id++; int word_ix_reg = 3; /* if we have to evaluate a delay expression, evaluate word index into temp register */ if (dexp != 0) { word_ix_reg = allocate_word(); } /* This code is common to all the different types of array delays. */ if (number_is_immediate(word_ix, IMM_WID, 0) && !number_is_unknown(word_ix)) { fprintf(vvp_out, " %%ix/load %d, %ld, 0; address\n", word_ix_reg, get_number_immediate(word_ix)); } else { /* Calculate array word index into index register 3 */ draw_eval_expr_into_integer(word_ix, word_ix_reg); /* Skip assignment if word expression is not defined. */ unsigned do_assign = transient_id++; fprintf(vvp_out, " %%jmp/0 t_%u, 4;\n", do_assign); fprintf(vvp_out, " %%pop/real 1;\n"); fprintf(vvp_out, " %%jmp t_%u;\n", end_assign); fprintf(vvp_out, "t_%u ;\n", do_assign); } if (dexp != 0) { /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); fprintf(vvp_out, " %%ix/mov 3, %d;\n", word_ix_reg); fprintf(vvp_out, " %%assign/ar/d v%p, %d;\n", lsig, delay_index); clr_word(word_ix_reg); clr_word(delay_index); } else if (nevents != 0) { /* Event control delay... */ fprintf(vvp_out, " %%assign/ar/e v%p;\n", lsig); } else { /* Constant delay... */ unsigned long low_d = delay % UINT64_C(0x100000000); unsigned long hig_d = delay / UINT64_C(0x100000000); /* * The %assign can only take a 32 bit delay. For a larger * delay we need to put it into an index register. */ if (hig_d != 0) { int delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); fprintf(vvp_out, " %%assign/ar/d v%p, %d;\n", lsig, delay_index); clr_word(delay_index); } else { fprintf(vvp_out, " %%assign/ar v%p, %lu;\n", lsig, low_d); } } fprintf(vvp_out, "t_%u ;\n", end_assign); if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n"); } static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, uint64_t delay, ivl_expr_t dexp, ivl_expr_t part_off_ex, unsigned nevents) { int word_ix_reg = 3; int part_off_reg = 0; int delay_index; unsigned long part_off = 0; int error_flag = allocate_flag(); /* Figure the constant part offset, if possible. If we can do so, then forget about the expression and use the calculated value. After this block, if the part_off_ex!=0, then the part offset is used, otherwise, use part_off. */ if (part_off_ex == 0) { part_off = 0; } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } if (dexp != 0) { word_ix_reg = allocate_word(); } else if (part_off_ex) { word_ix_reg = allocate_word(); } /* Calculate array word index into word index register */ draw_eval_expr_into_integer(word_ix, word_ix_reg); if (part_off_ex) { part_off_reg = allocate_word(); /* Save the index calculation error flag to a global. */ fprintf(vvp_out, " %%flag_mov %d, 4;\n", error_flag); draw_eval_expr_into_integer(part_off_ex, part_off_reg); /* Add the error state of the part select to the global. */ fprintf(vvp_out, " %%flag_or %d, 4;\n", error_flag); } else if (part_off != 0) { /* Store word part select into part_off_reg */ part_off_reg = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, 0; part off\n", part_off_reg, part_off); } /* Calculated delay... */ if (dexp != 0) { delay_index = allocate_word(); /* If needed save the index calculation error flag. */ if (! part_off_ex) { fprintf(vvp_out, " %%flag_mov %d, 4;\n", error_flag); } draw_eval_expr_into_integer(dexp, delay_index); if (word_ix_reg != 3) { fprintf(vvp_out, " %%ix/mov 3, %d;\n", word_ix_reg); clr_word(word_ix_reg); } /* Restore the error state since an undefined delay is okay. */ fprintf(vvp_out, " %%flag_mov 4, %d;\n", error_flag); fprintf(vvp_out, " %%assign/vec4/a/d v%p, %d, %d;\n", lsig, part_off_reg, delay_index); clr_word(delay_index); /* Event control delay... */ } else if (nevents != 0) { /* If needed use the global error state. */ if (part_off_ex) { fprintf(vvp_out, " %%flag_mov 4, %d;\n", error_flag); } fprintf(vvp_out, " %%assign/vec4/a/e v%p, %d;\n", lsig, part_off_reg); /* Constant delay... */ } else { unsigned long low_d = delay % UINT64_C(0x100000000); unsigned long hig_d = delay / UINT64_C(0x100000000); delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, %lu; Constant delay\n", delay_index, low_d, hig_d); if (word_ix_reg != 3) { fprintf(vvp_out, " %%ix/mov 3, %d;\n", word_ix_reg); clr_word(word_ix_reg); } /* If needed use the global error state. */ if (part_off_ex) { fprintf(vvp_out, " %%flag_mov 4, %d;\n", error_flag); } fprintf(vvp_out, " %%assign/vec4/a/d v%p, %d, %d;\n", lsig, part_off_reg, delay_index); clr_word(delay_index); } if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n"); clr_flag(error_flag); if (part_off_reg) clr_word(part_off_reg); } /* * The code to generate here assumes that a vec4 vector of the right * width is top of the vec4 stack. Arrange for it to be popped and * assigned to the given l-value. */ static void assign_to_lvector(ivl_lval_t lval, uint64_t delay, ivl_expr_t dexp, unsigned nevents) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned long part_off = 0; const unsigned long use_word = 0; // Detect the case that this is actually a non-blocking assign // to an array word. In that case, run off somewhere else to // deal with it. if (ivl_signal_dimensions(sig) > 0) { ivl_expr_t word_ix = ivl_lval_idx(lval); assert(word_ix); assign_to_array_word(sig, word_ix, delay, dexp, part_off_ex, nevents); return; } if (part_off_ex == 0) { part_off = 0; } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } unsigned long low_d = delay % UINT64_C(0x100000000); unsigned long hig_d = delay / UINT64_C(0x100000000); if (part_off_ex) { // The part select offset is calculated (not constant) // so in these cases we'll need to use // %assign/vec4/off/... variants. if (dexp != 0) { /* Calculated offset... */ int offset_index = allocate_word(); /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); /* Calculated part offset. This will leave flag bit 4 set to 1 if the copy into the index detected xz values. The %assign will use that to know to skip the assign. */ draw_eval_expr_into_integer(part_off_ex, offset_index); /* If the index expression has XZ bits, skip the assign. */ fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } else if (nevents != 0) { int offset_index = allocate_word(); /* Event control delay... */ draw_eval_expr_into_integer(part_off_ex, offset_index); fprintf(vvp_out, " %%assign/vec4/off/e v%p_%lu, %d;\n", sig, use_word, offset_index); fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); } else { int offset_index = allocate_word(); int delay_index = allocate_word(); /* Constant delay... */ fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); /* Calculated part offset. This will leave flag bit 4 set to 1 if the copy into the index detected xz values. The %assign will use that to know to skip the assign. */ draw_eval_expr_into_integer(part_off_ex, offset_index); /* If the index expression has XZ bits, skip the assign. */ fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } } else if (part_off>0 || ivl_lval_width(lval)!=ivl_signal_width(sig)) { if (nevents != 0) { assert(dexp==0); int offset_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", offset_index, part_off); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%assign/vec4/off/e v%p_%lu, %d;\n", sig, use_word, offset_index); fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); } else { // Constant part offset, non-constant (calculated) // assignment delay. Use the %assign/vec4/off/d // instruction to handle this case. int offset_index = allocate_word(); int delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", offset_index, part_off); if (dexp) { draw_eval_expr_into_integer(dexp,delay_index); } else { fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); } fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } } else if (dexp != 0) { /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); fprintf(vvp_out, " %%assign/vec4/d v%p_%lu, %d;\n", sig, use_word, delay_index); clr_word(delay_index); } else if (nevents != 0) { /* Event control delay... */ fprintf(vvp_out, " %%assign/vec4/e v%p_%lu;\n", sig, use_word); fprintf(vvp_out, " %%evctl/c;\n"); } else { /* * The %assign can only take a 32 bit delay. For a larger * delay we need to put it into an index register. */ if (hig_d != 0) { int delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); fprintf(vvp_out, " %%assign/vec4/d v%p_%lu, %d;\n", sig, use_word, delay_index); clr_word(delay_index); } else { fprintf(vvp_out, " %%assign/vec4 v%p_%lu, %lu;\n", sig, use_word, low_d); } } } /* * Routine to insert statement tracing information into the output stream * when requested by the user (compiler). */ void show_stmt_file_line(ivl_statement_t net, const char* desc) { if (show_file_line) { /* If the line number is not zero then the file should also * be set. It's safe to skip the assert during debug, but * the assert represents missing file/line information that * should be reported/fixed. */ unsigned lineno = ivl_stmt_lineno(net); assert(lineno); fprintf(vvp_out, " %%file_line %u %u \"%s\";\n", ivl_file_table_index(ivl_stmt_file(net)), lineno, desc); } } static int show_stmt_alloc(ivl_statement_t net) { ivl_scope_t scope = ivl_stmt_call(net); fprintf(vvp_out, " %%alloc S_%p;\n", scope); return 0; } /* * This function handles the case of non-blocking assign to word * variables such as real, i.e: * * real foo; * foo <= 1.0; * * In this case we know (by Verilog syntax) that there is only exactly * 1 l-value, the target identifier, so it should be relatively easy. */ static int show_stmt_assign_nb_real(ivl_statement_t net) { ivl_lval_t lval; ivl_signal_t sig; ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t del = ivl_stmt_delay_expr(net); /* variables for the selection of word from an array. */ unsigned long use_word = 0; uint64_t delay = 0; unsigned nevents = ivl_stmt_nevent(net); /* Must be exactly 1 l-value. */ assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); sig = ivl_lval_sig(lval); assert(sig); if (del && (ivl_expr_type(del) == IVL_EX_DELAY)) { assert(number_is_immediate(del, 64, 0)); delay = ivl_expr_delay_val(del); del = 0; } /* Evaluate the r-value */ draw_eval_real(rval); if (ivl_signal_dimensions(sig) > 0) { ivl_expr_t word_ix = ivl_lval_idx(lval); assert(word_ix); assign_to_array_r_word(sig, word_ix, delay, del, nevents); return 0; } /* We need to calculate the delay expression. */ if (del) { assert(nevents == 0); int delay_index = allocate_word(); draw_eval_expr_into_integer(del, delay_index); fprintf(vvp_out, " %%assign/wr/d v%p_%lu, %d;\n", sig, use_word, delay_index); clr_word(delay_index); } else if (nevents) { fprintf(vvp_out, " %%assign/wr/e v%p_%lu;\n", sig, use_word); } else { unsigned long low_d = delay % UINT64_C(0x100000000); unsigned long hig_d = delay / UINT64_C(0x100000000); /* * The %assign can only take a 32 bit delay. For a larger * delay we need to put it into an index register. */ if (hig_d != 0) { int delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); fprintf(vvp_out, " %%assign/wr/d v%p_%lu, %d;\n", sig, use_word, delay_index); clr_word(delay_index); } else { fprintf(vvp_out, " %%assign/wr v%p_%lu, %lu;\n", sig, use_word, low_d); } } return 0; } static int show_stmt_assign_nb(ivl_statement_t net) { ivl_lval_t lval; ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t del = ivl_stmt_delay_expr(net); ivl_signal_t sig; unsigned nevents = ivl_stmt_nevent(net); show_stmt_file_line(net, "Nonblocking assignment."); /* If we have an event control build the control structure. */ if (nevents) { assert(del == 0); ivl_expr_t cnt = ivl_stmt_cond_expr(net); unsigned long count = 1; if (cnt && (ivl_expr_type(cnt) == IVL_EX_ULONG)) { assert(number_is_immediate(cnt, IMM_WID, 0)); count = ivl_expr_uvalue(cnt); cnt = 0; } char name[256]; if (nevents == 1) { ivl_event_t ev = ivl_stmt_events(net, 0); snprintf(name, sizeof(name), "E_%p", ev); } else { unsigned idx; static unsigned int cascade_counter = 0; ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, "Eassign_%u .event/or E_%p", cascade_counter, ev); for (idx = 1; idx < nevents; idx += 1) { ev = ivl_stmt_events(net, idx); fprintf(vvp_out, ", E_%p", ev); } snprintf(name, sizeof(name), "Eassign_%u", cascade_counter); cascade_counter += 1; } if (cnt) { int count_index = allocate_word(); const char*type = ivl_expr_signed(cnt) ? "/s" : ""; draw_eval_expr_into_integer(cnt, count_index); fprintf(vvp_out, " %%evctl%s %s, %d;\n", type, name, count_index); clr_word(count_index); } else { fprintf(vvp_out, " %%evctl/i %s, %lu;\n", name, count); } } else { assert(ivl_stmt_cond_expr(net) == 0); } uint64_t delay = 0; /* Detect special cases that are handled elsewhere. */ lval = ivl_stmt_lval(net,0); if ((sig = ivl_lval_sig(lval))) { switch (ivl_signal_data_type(sig)) { case IVL_VT_REAL: return show_stmt_assign_nb_real(net); default: break; } } if (del && (ivl_expr_type(del) == IVL_EX_DELAY)) { assert(number_is_immediate(del, 64, 0)); delay = ivl_expr_delay_val(del); del = 0; } { unsigned wid; unsigned lidx; unsigned cur_rbit = 0; /* Handle the special case that the expression is a real value. Evaluate the real expression, then convert the result to a vector. */ if (ivl_expr_value(rval) == IVL_VT_REAL) { draw_eval_real(rval); /* This is the accumulated with of the l-value of the assignment. */ wid = ivl_stmt_lwidth(net); fprintf(vvp_out, " %%cvt/vr %u;\n", wid); } else { wid = ivl_stmt_lwidth(net); draw_eval_vec4(rval); if (ivl_expr_width(rval) != wid) { if (ivl_expr_signed(rval)) fprintf(vvp_out, " %%pad/s %u;\n", wid); else fprintf(vvp_out, " %%pad/u %u;\n", wid); } } /* Spread the r-value vector over the bits of the l-value. */ for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { unsigned bit_limit = wid - cur_rbit; lval = ivl_stmt_lval(net, lidx); if (bit_limit > ivl_lval_width(lval)) bit_limit = ivl_lval_width(lval); /* If there are more lvals after this, split off from the top of the vec4 stack only the bits (lsb) that we need for the current lval. */ if (lidx+1 < ivl_stmt_lvals(net)) fprintf(vvp_out, " %%split/vec4 %u;\n", bit_limit); assign_to_lvector(lval, delay, del, nevents); cur_rbit += bit_limit; } } return 0; } static int show_stmt_block(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned idx; unsigned cnt = ivl_stmt_block_count(net); for (idx = 0 ; idx < cnt ; idx += 1) { rc += show_statement(ivl_stmt_block_stmt(net, idx), sscope); } return rc; } /* * This draws an invocation of a named block. This is a little * different because a subscope is created. We do that by creating * a thread to deal with this. */ static int show_stmt_block_named(ivl_statement_t net, ivl_scope_t scope) { int rc; unsigned out_id, sub_id; ivl_scope_t subscope = ivl_stmt_block_scope(net); out_id = transient_id++; sub_id = transient_id++; fprintf(vvp_out, " %%fork t_%u, S_%p;\n", sub_id, subscope); fprintf(vvp_out, " %%jmp t_%u;\n", out_id); /* Change the compiling scope to be the named blocks scope. */ fprintf(vvp_out, " .scope S_%p;\n", subscope); fprintf(vvp_out, "t_%u ;\n", sub_id); rc = show_stmt_block(net, subscope); fprintf(vvp_out, " %%end;\n"); /* Return to the previous scope. */ fprintf(vvp_out, " .scope S_%p;\n", scope); fprintf(vvp_out, "t_%u %%join;\n", out_id); return rc; } static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_expr_t expr = ivl_stmt_cond_expr(net); unsigned count = ivl_stmt_case_count(net); unsigned local_base = local_count; unsigned idx, default_case; show_stmt_file_line(net, "Case statement."); local_count += count + 1; /* Evaluate the case condition to the top of the vec4 stack. This expression will be compared multiple times to each case guard. */ draw_eval_vec4(expr); /* First draw the branch table. All the non-default cases generate a branch out of here, to the code that implements the case. The default will fall through all the tests. */ default_case = count; for (idx = 0 ; idx < count ; idx += 1) { ivl_expr_t cex = ivl_stmt_case_expr(net, idx); if (cex == 0) { default_case = idx; continue; } /* Duplicate the case expression so that the cmp instructions below do not completely erase the value. Do this in front of each compare. */ fprintf(vvp_out, " %%dup/vec4;\n"); draw_eval_vec4(cex); switch (ivl_statement_type(net)) { case IVL_ST_CASE: fprintf(vvp_out, " %%cmp/u;\n"); fprintf(vvp_out, " %%jmp/1 T_%u.%u, 6;\n", thread_count, local_base+idx); break; case IVL_ST_CASEX: fprintf(vvp_out, " %%cmp/x;\n"); fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, local_base+idx); break; case IVL_ST_CASEZ: fprintf(vvp_out, " %%cmp/z;\n"); fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, local_base+idx); break; default: assert(0); } } /* Emit code for the default case. */ if (default_case < count) { ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case); rc += show_statement(cst, sscope); } /* Jump to the out of the case. */ fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, local_base+count); for (idx = 0 ; idx < count ; idx += 1) { ivl_statement_t cst = ivl_stmt_case_stmt(net, idx); if (idx == default_case) continue; fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+idx); rc += show_statement(cst, sscope); /* Statement is done, jump to the out of the case. */ fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, local_base+count); } /* The out of the case. */ fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+count); /* The case tests will leave the case expression on the top of the stack, but we are done with it now. Pop it. */ fprintf(vvp_out, " %%pop/vec4 1;\n"); return rc; } static int show_stmt_case_r(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_expr_t expr = ivl_stmt_cond_expr(net); unsigned count = ivl_stmt_case_count(net); unsigned local_base = local_count; unsigned idx, default_case; show_stmt_file_line(net, "Case statement."); /* Build the reference value into the top of the stack. All the case comparisons will make duplicates of this value in order to do their tests. */ draw_eval_real(expr); local_count += count + 1; /* First draw the branch table. All the non-default cases generate a branch out of here, to the code that implements the case. The default will fall through all the tests. */ default_case = count; for (idx = 0 ; idx < count ; idx += 1) { ivl_expr_t cex = ivl_stmt_case_expr(net, idx); if (cex == 0) { default_case = idx; continue; } /* The reference value... */ fprintf(vvp_out, " %%dup/real;\n"); /* The guard value... */ draw_eval_real(cex); /* The comparison. */ fprintf(vvp_out, " %%cmp/wr;\n"); fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, local_base+idx); } /* Emit code for the case default. The above jump table will fall through to this statement. */ if (default_case < count) { ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case); rc += show_statement(cst, sscope); } /* Jump to the out of the case. */ fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, local_base+count); for (idx = 0 ; idx < count ; idx += 1) { ivl_statement_t cst = ivl_stmt_case_stmt(net, idx); if (idx == default_case) continue; fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+idx); rc += show_statement(cst, sscope); fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, local_base+count); } /* The out of the case. */ fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+count); fprintf(vvp_out, " %%pop/real 1;\n"); return rc; } /* * The real value is already pushed to the top of the real value stack. */ static void force_real_to_lval(ivl_statement_t net) { const char*command_name; ivl_lval_t lval; ivl_signal_t lsig; switch (ivl_statement_type(net)) { case IVL_ST_CASSIGN: command_name = "%cassign/wr"; break; case IVL_ST_FORCE: command_name = "%force/wr"; break; default: command_name = "ERROR"; assert(0); break; } assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); lsig = ivl_lval_sig(lval); assert(ivl_lval_width(lval) == 1); assert(ivl_lval_part_off(lval) == 0); ivl_expr_t word_idx = ivl_lval_idx(lval); unsigned long use_word = 0; if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the assignment in this case. */ if (number_is_unknown(word_idx)) { fprintf(vvp_out, " %%pop/real 1;\n"); return; } use_word = get_number_immediate(word_idx); /* We do not currently support using a word from a variable array as the L-value (SystemVerilog / Icarus extension). */ if (ivl_signal_type(lsig) == IVL_SIT_REG) { fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " "word of a variable array (%s[%ld]).\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name, ivl_signal_basename(lsig), ivl_signal_array_base(lsig) + (long)use_word); vvp_errors += 1; } } /* L-Value must be a signal: reg or wire */ assert(lsig != 0); fprintf(vvp_out, " %s v%p_%lu;\n", command_name, lsig, use_word); } static void force_vector_to_lval(ivl_statement_t net) { unsigned lidx; const char*command_name; switch (ivl_statement_type(net)) { case IVL_ST_CASSIGN: command_name = "%cassign/vec4"; break; case IVL_ST_FORCE: command_name = "%force/vec4"; break; default: command_name = "ERROR"; assert(0); break; } for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval = ivl_stmt_lval(net, lidx); ivl_signal_t lsig = ivl_lval_sig(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); ivl_expr_t word_idx = ivl_lval_idx(lval); unsigned long use_word = 0; if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the assignment in this case. */ if (number_is_unknown(word_idx)) { fprintf(vvp_out, " %%pop/vec4 1; force to out of bounds index suppressed.\n"); return; } use_word = get_number_immediate(word_idx); /* We do not currently support using a word from a variable array as the L-value (SystemVerilog / Icarus extension). */ if (ivl_signal_type(lsig) == IVL_SIT_REG) { fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " "word of a variable array (%s[%ld]).\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name, ivl_signal_basename(lsig), ivl_signal_array_base(lsig) + (long)use_word); vvp_errors += 1; } } if (lidx+1 < ivl_stmt_lvals(net)) fprintf(vvp_out, " %%split/vec4 %u;\n", ivl_lval_width(lval)); /* L-Value must be a signal: reg or wire */ assert(lsig != 0); /* Do not support bit or part selects of l-values yet. */ if (part_off_ex) { int off_index = allocate_word(); draw_eval_expr_into_integer(part_off_ex, off_index); fprintf(vvp_out, " %s/off v%p_%lu, %d;\n", command_name, lsig, use_word, off_index); clr_word(off_index); } else { assert(ivl_lval_width(lval) == ivl_signal_width(lsig)); assert(!ivl_lval_part_off(lval)); fprintf(vvp_out, " %s v%p_%lu;\n", command_name, lsig, use_word); } } } static void force_link_rval(ivl_statement_t net, ivl_expr_t rval) { ivl_signal_t rsig; ivl_lval_t lval; ivl_signal_t lsig; const char*command_name; ivl_expr_t part_off_ex; ivl_expr_t lword_idx, rword_idx; unsigned long use_lword = 0, use_rword = 0; if (ivl_expr_type(rval) != IVL_EX_SIGNAL) { if (ivl_expr_type(rval) == IVL_EX_NUMBER || ivl_expr_type(rval) == IVL_EX_REALNUM) return; fprintf(stderr, "%s:%u: tgt-vvp sorry: procedural continuous " "assignments are not yet fully supported. The RHS of " "this assignment will only be evaluated once, at the " "time the assignment statement is executed.\n", ivl_stmt_file(net), ivl_stmt_lineno(net)); return; } switch (ivl_statement_type(net)) { case IVL_ST_CASSIGN: command_name = "%cassign"; break; case IVL_ST_FORCE: command_name = "%force"; break; default: command_name = "ERROR"; assert(0); break; } rsig = ivl_expr_signal(rval); assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); lsig = ivl_lval_sig(lval); /* We do not currently support driving a signal to a bit or * part select (this could give us multiple drivers). */ part_off_ex = ivl_lval_part_off(lval); /* This should be verified in force_vector_to_lval() which is called * before this procedure. */ if (part_off_ex) { assert(number_is_immediate(part_off_ex, IMM_WID, 0)); assert(! number_is_unknown(part_off_ex)); } if (ivl_signal_width(lsig) > ivl_signal_width(rsig) || (part_off_ex && get_number_immediate(part_off_ex) != 0)) { /* Normalize the bit/part select. This also needs to be * reworked to support packed arrays. */ long real_msb = ivl_signal_packed_msb(lsig, 0); long real_lsb = ivl_signal_packed_lsb(lsig, 0); long use_wid = ivl_signal_width(rsig); long use_lsb, use_msb; if (ivl_signal_packed_dimensions(lsig) > 1) { fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s part of a " "packed array.\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name); } if (real_msb >= real_lsb) { use_lsb = get_number_immediate(part_off_ex); use_lsb += real_lsb; use_msb = use_lsb + use_wid - 1; } else { use_lsb = real_lsb; use_lsb -= get_number_immediate(part_off_ex); use_msb = use_lsb; use_msb -= use_wid - 1; } fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s signal to a ", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name); if (use_wid > 1) { fprintf(stderr, "part select (%s[%ld:%ld]).\n", ivl_signal_basename(lsig), use_msb, use_lsb); } else { fprintf(stderr, "bit select (%s[%ld]).\n", ivl_signal_basename(lsig), use_lsb); } vvp_errors += 1; } /* At least for now, only handle force to fixed words of an array. */ if ((lword_idx = ivl_lval_idx(lval)) != 0) { assert(number_is_immediate(lword_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the assignment in this case. */ if (number_is_unknown(lword_idx)) return; use_lword = get_number_immediate(lword_idx); /* We do not currently support using a word from a variable * array as the L-value (SystemVerilog / Icarus extension). */ if (ivl_signal_type(lsig) == IVL_SIT_REG) { /* Normalize the array access. */ long real_word = use_lword; real_word += ivl_signal_array_base(lsig); fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " "word of a variable array (%s[%ld]).\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name, ivl_signal_basename(lsig), real_word); vvp_errors += 1; } } if ((rword_idx = ivl_expr_oper1(rval)) != 0) { assert(ivl_signal_dimensions(rsig) != 0); if (!number_is_immediate(rword_idx, IMM_WID, 0)) { fprintf(stderr, "%s:%u: tgt-vvp sorry: procedural continuous " "assignments are not yet fully supported. The RHS of " "this assignment will only be evaluated once, at the " "time the assignment statement is executed.\n", ivl_stmt_file(net), ivl_stmt_lineno(net)); return; } /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the assignment in this case. */ if (number_is_unknown(rword_idx)) return; use_rword = get_number_immediate(rword_idx); /* We do not currently support using a word from a variable * array as the R-value. */ if (ivl_signal_type(rsig) == IVL_SIT_REG) { /* Normalize the array access. */ long real_word = use_rword; real_word += ivl_signal_array_base(rsig); fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s from the " "word of a variable array (%s[%ld]).\n", ivl_expr_file(rval), ivl_expr_lineno(rval), command_name, ivl_signal_basename(rsig), real_word); vvp_errors += 1; } } else { assert(ivl_signal_dimensions(rsig) == 0); use_rword = 0; } fprintf(vvp_out, " %s/link", command_name); fprintf(vvp_out, " v%p_%lu", lsig, use_lword); fprintf(vvp_out, ", v%p_%lu;\n", rsig, use_rword); } static int show_stmt_cassign(ivl_statement_t net) { ivl_expr_t rval; ivl_signal_t sig; show_stmt_file_line(net, "Assign statement."); rval = ivl_stmt_rval(net); assert(rval); sig = ivl_lval_sig(ivl_stmt_lval(net, 0)); if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) { draw_eval_real(ivl_stmt_rval(net)); force_real_to_lval(net); } else { draw_eval_vec4(rval); resize_vec4_wid(rval, ivl_stmt_lwidth(net)); /* Write out initial continuous assign instructions to assign the expression value to the l-value. */ force_vector_to_lval(net); } force_link_rval(net, rval); return 0; } /* * Handle the deassign similar to cassign. The lvals must all be * vectors without bit or part selects. Simply call %deassign for all * the values. */ static int show_stmt_deassign(ivl_statement_t net) { ivl_signal_t sig = ivl_lval_sig(ivl_stmt_lval(net, 0)); unsigned lidx; show_stmt_file_line(net, "Deassign statement."); if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) { ivl_lval_t lval; assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); assert(ivl_lval_width(lval) == 1); assert(ivl_lval_part_off(lval) == 0); ivl_expr_t word_idx = ivl_lval_idx(lval); unsigned long use_word = 0; if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the deassignment in this case. */ if (number_is_unknown(word_idx)) return 0; use_word = get_number_immediate(word_idx); } fprintf(vvp_out, " %%deassign/wr v%p_%lu;\n", sig, use_word); return 0; } for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval = ivl_stmt_lval(net, lidx); ivl_signal_t lsig = ivl_lval_sig(lval); ivl_expr_t word_idx = ivl_lval_idx(lval); unsigned long use_word = 0; unsigned use_wid; ivl_expr_t part_off_ex; unsigned part_off; assert(lsig != 0); use_wid = ivl_lval_width(lval); part_off_ex = ivl_lval_part_off(lval); part_off = 0; if (part_off_ex != 0) { assert(number_is_immediate(part_off_ex, 64, 0)); if (number_is_unknown(part_off_ex)) return 0; part_off = get_number_immediate(part_off_ex); } if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the deassignment in this case. */ if (number_is_unknown(word_idx)) return 0; use_word = get_number_immediate(word_idx); } fprintf(vvp_out, " %%deassign v%p_%lu, %u, %u;\n", lsig, use_word, part_off, use_wid); } return 0; } static int show_stmt_condit(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned lab_false, lab_out; ivl_expr_t expr = ivl_stmt_cond_expr(net); show_stmt_file_line(net, "If statement."); lab_false = local_count++; lab_out = local_count++; int use_flag = draw_eval_condition(expr); fprintf(vvp_out, " %%jmp/0xz T_%u.%u, %d;\n", thread_count, lab_false, use_flag); clr_flag(use_flag); if (ivl_stmt_cond_true(net)) rc += show_statement(ivl_stmt_cond_true(net), sscope); if (ivl_stmt_cond_false(net)) { fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_out); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_false); rc += show_statement(ivl_stmt_cond_false(net), sscope); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out); } else { fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_false); } return rc; } /* * The delay statement is easy. Simply write a ``%delay '' * instruction to delay the thread, then draw the included statement. * The delay statement comes from Verilog code like this: * * ... * # ; */ static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; uint64_t delay = ivl_stmt_delay_val(net); ivl_statement_t stmt = ivl_stmt_sub_stmt(net); unsigned long low = delay % UINT64_C(0x100000000); unsigned long hig = delay / UINT64_C(0x100000000); show_stmt_file_line(net, "Delay statement."); fprintf(vvp_out, " %%delay %lu, %lu;\n", low, hig); rc += show_statement(stmt, sscope); return rc; } /* * The delayx statement is slightly more complex in that it is * necessary to calculate the delay first. Load the calculated delay * into and index register and use the %delayx instruction to do the * actual delay. */ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_expr_t expr = ivl_stmt_delay_expr(net); ivl_statement_t stmt = ivl_stmt_sub_stmt(net); show_stmt_file_line(net, "Delay statement."); int use_idx = allocate_word(); switch (ivl_expr_value(expr)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: { draw_eval_vec4(expr); fprintf(vvp_out, " %%ix/vec4 %d;\n", use_idx); break; } case IVL_VT_REAL: { draw_eval_real(expr); fprintf(vvp_out, " %%cvt/ur %d;\n", use_idx); break; } default: assert(0); } fprintf(vvp_out, " %%delayx %d;\n", use_idx); clr_word(use_idx); rc += show_statement(stmt, sscope); return rc; } static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_scope_t target = ivl_stmt_call(net); (void)sscope; /* Parameter is not used. */ /* A normal disable statement. */ if (target) { show_stmt_file_line(net, "Disable statement."); fprintf(vvp_out, " %%disable S_%p;\n", target); /* A SystemVerilog disable fork statement. */ } else { show_stmt_file_line(net, "Disable fork statement."); fprintf(vvp_out, " %%disable/fork;\n"); } return rc; } static int show_stmt_do_while(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned top_label = local_count++; show_stmt_file_line(net, "Do/While statement."); /* Start the loop. The top of the loop starts a basic block because it can be entered from above or from the bottom of the loop. */ fprintf(vvp_out, "T_%u.%u ;\n", thread_count, top_label); /* Draw the body of the loop. */ rc += show_statement(ivl_stmt_sub_stmt(net), sscope); /* Draw the evaluation of the condition expression, and test the result. If the expression evaluates to true, then branch to the top label. */ int use_flag = draw_eval_condition(ivl_stmt_cond_expr(net)); fprintf(vvp_out, " %%jmp/1 T_%u.%u, %d;\n", thread_count, top_label, use_flag); clr_flag(use_flag); return rc; } static int show_stmt_force(ivl_statement_t net) { ivl_expr_t rval; ivl_signal_t sig; show_stmt_file_line(net, "Force statement."); rval = ivl_stmt_rval(net); assert(rval); sig = ivl_lval_sig(ivl_stmt_lval(net, 0)); if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) { draw_eval_real(ivl_stmt_rval(net)); force_real_to_lval(net); } else { draw_eval_vec4(rval); /* Write out initial continuous assign instructions to assign the expression value to the l-value. */ force_vector_to_lval(net); } force_link_rval(net, rval); return 0; } static int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_statement_t stmt = ivl_stmt_sub_stmt(net); unsigned lab_top = local_count++; show_stmt_file_line(net, "Forever statement."); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_top); rc += show_statement(stmt, sscope); fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); return rc; } static int show_stmt_fork(ivl_statement_t net, ivl_scope_t sscope) { unsigned idx; int rc = 0; unsigned join_count = ivl_stmt_block_count(net); unsigned join_detach_count = 0; ivl_scope_t scope = ivl_stmt_block_scope(net); int is_named = (scope != 0); unsigned out = transient_id++; unsigned id_base = transient_id; /* Increment the number of IDs needed before the join count is * modified by the join_any or join_none code below. */ transient_id += join_count; switch (ivl_statement_type(net)) { case IVL_ST_FORK: break; case IVL_ST_FORK_JOIN_ANY: if (join_count < 2) break; join_detach_count = join_count - 1; join_count = 1; break; case IVL_ST_FORK_JOIN_NONE: join_detach_count = join_count; join_count = 0; break; default: assert(0); } if (scope==0) scope = sscope; /* Draw a fork statement for all but one of the threads of the fork/join. Send the threads off to a bit of code where they are implemented. */ for (idx = 0 ; idx < (join_count+join_detach_count) ; idx += 1) { fprintf(vvp_out, " %%fork t_%u, S_%p;\n", id_base+idx, scope); } /* Generate enough joins to collect all the sub-threads. */ for (idx = 0 ; idx < join_count ; idx += 1) fprintf(vvp_out, " %%join;\n"); if (join_detach_count > 0) fprintf(vvp_out, " %%join/detach %u;\n", join_detach_count); /* Jump around all the threads that I'm creating. */ fprintf(vvp_out, " %%jmp t_%u;\n", out); /* Change the compiling scope to be the named forks scope. */ if (is_named) fprintf(vvp_out, " .scope S_%p;\n", scope); /* Generate the sub-threads themselves. */ for (idx = 0 ; idx < (join_count + join_detach_count) ; idx += 1) { fprintf(vvp_out, "t_%u ;\n", id_base+idx); rc += show_statement(ivl_stmt_block_stmt(net, idx), scope); fprintf(vvp_out, " %%end;\n"); } /* Return to the previous scope. */ if (sscope) fprintf(vvp_out, " .scope S_%p;\n", sscope); /* This is the label for the out. Use this to branch around the implementations of all the child threads. */ fprintf(vvp_out, "t_%u ;\n", out); return rc; } static int show_stmt_free(ivl_statement_t net) { ivl_scope_t scope = ivl_stmt_call(net); fprintf(vvp_out, " %%free S_%p;\n", scope); return 0; } /* * noop statements are implemented by doing nothing. */ static int show_stmt_noop(ivl_statement_t net) { (void)net; /* Parameter is not used. */ return 0; } static int show_stmt_release(ivl_statement_t net) { ivl_signal_t sig = ivl_lval_sig(ivl_stmt_lval(net, 0)); unsigned lidx; show_stmt_file_line(net, "Release statement."); if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) { unsigned type = 0; ivl_lval_t lval; assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); assert(ivl_lval_width(lval) == 1); assert(ivl_lval_part_off(lval) == 0); ivl_expr_t word_idx = ivl_lval_idx(lval); unsigned long use_word = 0; if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the deassignment in this case. */ if (number_is_unknown(word_idx)) return 0; use_word = get_number_immediate(word_idx); } if (ivl_signal_type(sig) == IVL_SIT_REG) type = 1; fprintf(vvp_out, " %%release/wr v%p_%lu, %u;\n", sig, use_word, type); return 0; } for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval = ivl_stmt_lval(net, lidx); ivl_signal_t lsig = ivl_lval_sig(lval); const char*opcode = 0; ivl_expr_t word_idx = ivl_lval_idx(lval); unsigned long use_word = 0; unsigned use_wid; ivl_expr_t part_off_ex; unsigned part_off; assert(lsig != 0); use_wid = ivl_lval_width(lval); part_off_ex = ivl_lval_part_off(lval); part_off = 0; if (part_off_ex != 0) { assert(number_is_immediate(part_off_ex, 64, 0)); /* An out-of-range or undefined offset will have been converted to a canonical offset of 1'bx. Skip the assignment in this case. */ if (number_is_unknown(part_off_ex)) return 0; part_off = get_number_immediate(part_off_ex); } switch (ivl_signal_type(lsig)) { case IVL_SIT_REG: opcode = "reg"; break; default: opcode = "net"; break; } if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); /* An out-of-range or undefined index will have been converted to a canonical offset of 1'bx. Skip the assignment in this case. */ if (number_is_unknown(word_idx)) return 0; use_word = get_number_immediate(word_idx); } /* Generate the appropriate release statement for this l-value. */ fprintf(vvp_out, " %%release/%s v%p_%lu, %u, %u;\n", opcode, lsig, use_word, part_off, use_wid); } return 0; } static int show_stmt_repeat(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned lab_top = local_count++, lab_out = local_count++; ivl_expr_t expr = ivl_stmt_cond_expr(net); const char *sign = ivl_expr_signed(expr) ? "s" : "u"; show_stmt_file_line(net, "Repeat statement."); /* Calculate the repeat count onto the top of the vec4 stack. */ draw_eval_vec4(expr); /* Test that 0 < expr, escape if expr <= 0. If the expr is unsigned, then we only need to try to escape if expr==0 as it will never be <0. */ fprintf(vvp_out, "T_%u.%u %%dup/vec4;\n", thread_count, lab_top); fprintf(vvp_out, " %%pushi/vec4 0, 0, %u;\n", ivl_expr_width(expr)); fprintf(vvp_out, " %%cmp/%s;\n", sign); if (ivl_expr_signed(expr)) fprintf(vvp_out, " %%jmp/1xz T_%u.%u, 5;\n", thread_count, lab_out); fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, lab_out); /* This adds -1 (all ones in 2's complement) to the count. */ fprintf(vvp_out, " %%pushi/vec4 1, 0, %u;\n", ivl_expr_width(expr)); fprintf(vvp_out, " %%sub;\n"); rc += show_statement(ivl_stmt_sub_stmt(net), sscope); fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out); fprintf(vvp_out, " %%pop/vec4 1;\n"); return rc; } /* * The trigger statement is straight forward. All we have to do is * write a single bit of fake data to the event object. */ static int show_stmt_trigger(ivl_statement_t net) { ivl_event_t ev = ivl_stmt_events(net, 0); assert(ev); show_stmt_file_line(net, "Event trigger statement."); fprintf(vvp_out, " %%event E_%p;\n", ev); return 0; } static int show_stmt_utask(ivl_statement_t net) { ivl_scope_t task = ivl_stmt_call(net); show_stmt_file_line(net, "User task call."); fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(task))); fprintf(vvp_out, ", S_%p;\n", task); fprintf(vvp_out, " %%join;\n"); return 0; } static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) { /* Look to see if this is a SystemVerilog wait fork. */ if ((ivl_stmt_nevent(net) == 1) && (ivl_stmt_events(net, 0) == 0)) { assert(ivl_statement_type(ivl_stmt_sub_stmt(net)) == IVL_ST_NOOP); show_stmt_file_line(net, "Wait fork statement."); fprintf(vvp_out, " %%wait/fork;\n"); return 0; } show_stmt_file_line(net, "Event wait (@) statement."); if (ivl_stmt_nevent(net) == 1) { ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, " %%wait E_%p;\n", ev); } else { unsigned idx; static unsigned int cascade_counter = 0; ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, "Ewait_%u .event/or E_%p", cascade_counter, ev); for (idx = 1 ; idx < ivl_stmt_nevent(net) ; idx += 1) { ev = ivl_stmt_events(net, idx); fprintf(vvp_out, ", E_%p", ev); } fprintf(vvp_out, ";\n %%wait Ewait_%u;\n", cascade_counter); cascade_counter += 1; } return show_statement(ivl_stmt_sub_stmt(net), sscope); } static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned top_label = local_count++; unsigned out_label = local_count++; show_stmt_file_line(net, "While statement."); /* Start the loop. The top of the loop starts a basic block because it can be entered from above or from the bottom of the loop. */ fprintf(vvp_out, "T_%u.%u ;\n", thread_count, top_label); /* Draw the evaluation of the condition expression, and test the result. If the expression evaluates to false, then branch to the out label. */ int use_flag = draw_eval_condition(ivl_stmt_cond_expr(net)); fprintf(vvp_out, " %%jmp/0xz T_%u.%u, %d;\n", thread_count, out_label, use_flag); clr_flag(use_flag); /* Draw the body of the loop. */ rc += show_statement(ivl_stmt_sub_stmt(net), sscope); /* This is the bottom of the loop. branch to the top where the test is repeated, and also draw the out label. */ fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, top_label); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, out_label); return rc; } static int show_delete_method(ivl_statement_t net) { show_stmt_file_line(net, "Delete object"); unsigned parm_count = ivl_stmt_parm_count(net); if (parm_count < 1) return 1; ivl_expr_t parm = ivl_stmt_parm(net, 0); assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm); fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); return 0; } static int show_push_frontback_method(ivl_statement_t net, bool is_front) { const char*type_code; if (is_front) { show_stmt_file_line(net, "queue: push_front"); type_code = "qf"; } else { show_stmt_file_line(net, "queue: push_back"); type_code = "qb"; } unsigned parm_count = ivl_stmt_parm_count(net); if (parm_count != 2) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); assert(ivl_type_base(var_type)== IVL_VT_QUEUE); ivl_type_t element_type = ivl_type_element(var_type); ivl_expr_t parm1 = ivl_stmt_parm(net,1); switch (ivl_type_base(element_type)) { case IVL_VT_REAL: draw_eval_real(parm1); fprintf(vvp_out, " %%store/%s/r v%p_0;\n", type_code, var); break; case IVL_VT_STRING: draw_eval_string(parm1); fprintf(vvp_out, " %%store/%s/str v%p_0;\n", type_code, var); break; default: draw_eval_vec4(parm1); fprintf(vvp_out, " %%store/%s/v v%p_0, %u;\n", type_code, var, width_of_packed_type(element_type)); break; } return 0; } static int show_system_task_call(ivl_statement_t net) { const char*stmt_name = ivl_stmt_name(net); if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0) return show_delete_method(net); if (strcmp(stmt_name,"$ivl_queue_method$push_front") == 0) return show_push_frontback_method(net, true); if (strcmp(stmt_name,"$ivl_queue_method$push_back") == 0) return show_push_frontback_method(net, false); show_stmt_file_line(net, "System task call."); draw_vpi_task_call(net); return 0; } /* * Icarus translated = into * begin * = ; * = ; * end * This routine looks for this pattern so we only emit one %file_line opcode. */ static unsigned is_delayed_or_event_assign(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t assign, delay, delayed_assign; ivl_statement_type_t delay_type; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; (void)scope; /* Parameter is not used. */ /* We must have two block elements. */ if (ivl_stmt_block_count(stmt) != 2) return 0; /* The first must be an assign. */ assign = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a delayx. */ delay = ivl_stmt_block_stmt(stmt, 1); delay_type = ivl_statement_type(delay); if ((delay_type != IVL_ST_DELAYX) && (delay_type != IVL_ST_WAIT)) return 0; /* The statement for the delayx must be an assign. */ delayed_assign = ivl_stmt_sub_stmt(delay); if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(assign) != 1) return 0; lval = ivl_stmt_lval(assign, 0); /* It must not have an array select. */ if (ivl_lval_idx(lval)) return 0; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return 0; lsig = ivl_lval_sig(lval); /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; /* The R-value must be a single signal. */ rval = ivl_stmt_rval(delayed_assign); if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0; /* It must not be an array word. */ if (ivl_expr_oper1(rval)) return 0; rsig = ivl_expr_signal(rval); /* The two signals must be the same. */ if (lsig != rsig) return 0; /* And finally the three statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delay)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delayed_assign))) { return 0; } /* The pattern matched so this block represents a blocking * assignment with an inter-assignment delay or event. */ if (delay_type == IVL_ST_DELAYX) { show_stmt_file_line(stmt, "Blocking assignment (delay)."); } else { show_stmt_file_line(stmt, "Blocking assignment (event)."); } return 1; } /* * Icarus translated = repeat() into * begin * = ; * repeat() ; * = ; * end * This routine looks for this pattern so we only emit one %file_line opcode. */ static unsigned is_repeat_event_assign(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t assign, event, event_assign, repeat; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; (void)scope; /* Parameter is not used. */ /* We must have three block elements. */ if (ivl_stmt_block_count(stmt) != 3) return 0; /* The first must be an assign. */ assign = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a repeat with an event or an event. */ repeat = ivl_stmt_block_stmt(stmt, 1); if (ivl_statement_type(repeat) != IVL_ST_REPEAT) return 0; /* The repeat must have an event statement. */ event = ivl_stmt_sub_stmt(repeat); if (ivl_statement_type(event) != IVL_ST_WAIT) return 0; /* The third must be an assign. */ event_assign = ivl_stmt_block_stmt(stmt, 2); if (ivl_statement_type(event_assign) != IVL_ST_ASSIGN) return 0; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(assign) != 1) return 0; lval = ivl_stmt_lval(assign, 0); /* It must not have an array select. */ if (ivl_lval_idx(lval)) return 0; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return 0; lsig = ivl_lval_sig(lval); /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; /* The R-value must be a single signal. */ rval = ivl_stmt_rval(event_assign); if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0; /* It must not be an array word. */ if (ivl_expr_oper1(rval)) return 0; rsig = ivl_expr_signal(rval); /* The two signals must be the same. */ if (lsig != rsig) return 0; /* And finally the four statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(repeat)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event_assign))) { return 0; } /* The pattern matched so this block represents a blocking * assignment with an inter-assignment repeat event. */ show_stmt_file_line(stmt, "Blocking assignment (repeat event)."); return 1; } /* * Icarus translated wait( into * begin * while ( !== 1'b1) @(); * * end * This routine looks for this pattern and turns it back into a * wait statement. */ static unsigned is_wait(ivl_scope_t scope, ivl_statement_t stmt) { ivl_statement_t while_wait, wait_x, wait_stmt; ivl_expr_t while_expr, expr; const char *bits; (void)scope; /* Parameter is not used. */ /* We must have two block elements. */ if (ivl_stmt_block_count(stmt) != 2) return 0; /* The first must be a while. */ while_wait = ivl_stmt_block_stmt(stmt, 0); if (ivl_statement_type(while_wait) != IVL_ST_WHILE) return 0; /* That has a wait with a NOOP statement. */ wait_x = ivl_stmt_sub_stmt(while_wait); if (ivl_statement_type(wait_x) != IVL_ST_WAIT) return 0; wait_stmt = ivl_stmt_sub_stmt(wait_x); if (ivl_statement_type(wait_stmt) != IVL_ST_NOOP) return 0; /* Check that the while condition has the correct form. */ while_expr = ivl_stmt_cond_expr(while_wait); if (ivl_expr_type(while_expr) != IVL_EX_BINARY) return 0; if (ivl_expr_opcode(while_expr) != 'N') return 0; /* Has a second operator that is a constant 1'b1. */ expr = ivl_expr_oper2(while_expr); if (ivl_expr_type(expr) != IVL_EX_NUMBER) return 0; if (ivl_expr_width(expr) != 1) return 0; bits = ivl_expr_bits(expr); if (*bits != '1') return 0; /* There is no easy way to verify that the @ sensitivity list * matches the first expression so that is not currently checked. */ /* And finally the two statements that represent the wait must * have the same line number as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_wait)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(wait_x))) { return 0; } /* The pattern matched so this block represents a wait statement. */ show_stmt_file_line(stmt, "Wait statement."); return 1; } /* * Check to see if the statement L-value is a port in the given scope. * If it is return the zero based port number. */ static unsigned utask_in_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); ivl_lval_t lval = ivl_stmt_lval(stmt, 0); ivl_signal_t lsig = ivl_lval_sig(lval); const char *sig_name; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(stmt) != 1) return ports; /* It must not have an array select. */ if (ivl_lval_idx(lval)) return ports; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return ports; /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return ports; /* It must have the same scope as the task. */ if (scope != ivl_signal_scope(lsig)) return ports; /* It must be an input or inout port of the task. */ sig_name = ivl_signal_basename(lsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_INPUT) && (port_type != IVL_SIP_INOUT)) continue; if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; } return idx; } /* * Check to see if the statement R-value is a port in the given scope. * If it is return the zero based port number. */ static unsigned utask_out_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); ivl_expr_t rval = ivl_stmt_rval(stmt); ivl_signal_t rsig = 0; ivl_expr_type_t expr_type = ivl_expr_type(rval); const char *sig_name; /* We can have a simple signal. */ if (expr_type == IVL_EX_SIGNAL) { rsig = ivl_expr_signal(rval); /* Or a simple select of a simple signal. */ } else if (expr_type == IVL_EX_SELECT) { ivl_expr_t expr = ivl_expr_oper1(rval); /* We must have a zero select base. */ if (ivl_expr_oper2(rval)) return ports; /* We must be selecting a signal. */ if (ivl_expr_type(expr) != IVL_EX_SIGNAL) return ports; rsig = ivl_expr_signal(expr); } else return ports; /* The R-value must have the same scope as the task. */ if (scope != ivl_signal_scope(rsig)) return ports; /* It must not be an array element. */ if (ivl_signal_dimensions(rsig)) return ports; /* It must be an output or inout port of the task. */ sig_name = ivl_signal_basename(rsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_OUTPUT) && (port_type != IVL_SIP_INOUT)) continue; if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; } return idx; } /* * Structure to hold the port information as we extract it from the block. */ typedef struct port_expr_s { ivl_signal_port_t type; union { ivl_statement_t lval; ivl_expr_t rval; } expr; } *port_expr_t; /* * Icarus encodes a user task call with arguments as: * begin * = * ... * = * * = * ... * = * end * This routine looks for that pattern and translates it into the * appropriate task call. It returns true (1) if it successfully * translated the block to a task call, otherwise it returns false * (0) to indicate the block needs to be emitted. */ static unsigned is_utask_call_with_args(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports, task_idx = 0; unsigned count = ivl_stmt_block_count(stmt); unsigned lineno = ivl_stmt_lineno(stmt); ivl_scope_t task_scope = 0; port_expr_t port_exprs; (void)scope; /* Parameter is not used. */ /* Check to see if the block is of the basic form first. */ for (idx = 0; idx < count; idx += 1) { ivl_statement_t tmp = ivl_stmt_block_stmt(stmt, idx); if (ivl_statement_type(tmp) == IVL_ST_ASSIGN) continue; if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { task_idx = idx; task_scope = ivl_stmt_call(tmp); assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); continue; } return 0; } /* If there is no task call or it takes no argument then return. */ if (!task_scope) return 0; ports = ivl_scope_ports(task_scope); if (ports == 0) return 0; /* Allocate space to save the port information and initialize it. */ port_exprs = (port_expr_t) malloc(sizeof(struct port_expr_s)*ports); for (idx = 0; idx < ports; idx += 1) { port_exprs[idx].type = IVL_SIP_NONE; port_exprs[idx].expr.rval = 0; } /* Check that the input arguments are correct. */ for (idx = 0; idx < task_idx; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_in_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } port_exprs[port].type = IVL_SIP_INPUT; port_exprs[port].expr.rval = ivl_stmt_rval(assign); } /* Check that the output arguments are correct. */ for (idx = task_idx + 1; idx < count; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_out_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } if (port_exprs[port].type == IVL_SIP_INPUT) { /* We probably should verify that the current R-value * matches the new L-value. */ port_exprs[port].type = IVL_SIP_INOUT; } else { port_exprs[port].type = IVL_SIP_OUTPUT; } port_exprs[port].expr.lval = assign; } /* Check that the task call has the correct line number. */ if (lineno != ivl_stmt_lineno(ivl_stmt_block_stmt(stmt, task_idx))) { free(port_exprs); return 0; } /* Verify that all the ports were defined. */ for (idx = 0; idx < ports; idx += 1) { if (port_exprs[idx].type == IVL_SIP_NONE) { free(port_exprs); return 0; } } /* The pattern matched so this block represents a call to a user * defined task with arguments. */ show_stmt_file_line(stmt, "User task call (with arguments)."); free(port_exprs); return 1; } /* * This function draws a statement as vvp assembly. It basically * switches on the statement type and draws code based on the type and * further specifics. */ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) { const ivl_statement_type_t code = ivl_statement_type(net); int rc = 0; switch (code) { case IVL_ST_ALLOC: rc += show_stmt_alloc(net); break; case IVL_ST_ASSIGN: rc += show_stmt_assign(net); break; case IVL_ST_ASSIGN_NB: rc += show_stmt_assign_nb(net); break; case IVL_ST_BLOCK: if (ivl_stmt_block_scope(net)) rc += show_stmt_block_named(net, sscope); else { unsigned saved_file_line = 0; /* This block could really represent a single statement. * If so only emit a single %file_line opcode. */ if (show_file_line) { if (is_delayed_or_event_assign(sscope, net) || is_repeat_event_assign(sscope, net) || is_wait(sscope, net) || is_utask_call_with_args(sscope, net)) { saved_file_line = show_file_line; show_file_line = 0; } } rc += show_stmt_block(net, sscope); if (saved_file_line) show_file_line = 1; } break; case IVL_ST_CASE: case IVL_ST_CASEX: case IVL_ST_CASEZ: rc += show_stmt_case(net, sscope); break; case IVL_ST_CASER: rc += show_stmt_case_r(net, sscope); break; case IVL_ST_CASSIGN: rc += show_stmt_cassign(net); break; case IVL_ST_CONDIT: rc += show_stmt_condit(net, sscope); break; case IVL_ST_DEASSIGN: rc += show_stmt_deassign(net); break; case IVL_ST_DELAY: rc += show_stmt_delay(net, sscope); break; case IVL_ST_DELAYX: rc += show_stmt_delayx(net, sscope); break; case IVL_ST_DISABLE: rc += show_stmt_disable(net, sscope); break; case IVL_ST_DO_WHILE: rc += show_stmt_do_while(net, sscope); break; case IVL_ST_FORCE: rc += show_stmt_force(net); break; case IVL_ST_FOREVER: rc += show_stmt_forever(net, sscope); break; case IVL_ST_FORK: case IVL_ST_FORK_JOIN_ANY: case IVL_ST_FORK_JOIN_NONE: rc += show_stmt_fork(net, sscope); break; case IVL_ST_FREE: rc += show_stmt_free(net); break; case IVL_ST_NOOP: rc += show_stmt_noop(net); break; case IVL_ST_RELEASE: rc += show_stmt_release(net); break; case IVL_ST_REPEAT: rc += show_stmt_repeat(net, sscope); break; case IVL_ST_STASK: rc += show_system_task_call(net); break; case IVL_ST_TRIGGER: rc += show_stmt_trigger(net); break; case IVL_ST_UTASK: rc += show_stmt_utask(net); break; case IVL_ST_WAIT: rc += show_stmt_wait(net, sscope); break; case IVL_ST_WHILE: rc += show_stmt_while(net, sscope); break; default: fprintf(stderr, "vvp.tgt: Unable to draw statement type %d\n", code); rc += 1; break; } return rc; } /* * The process as a whole is surrounded by this code. We generate a * start label that the .thread statement can use, and we generate * code to terminate the thread. */ int draw_process(ivl_process_t net, void*x) { int rc = 0; unsigned idx; ivl_scope_t scope = ivl_process_scope(net); ivl_statement_t stmt = ivl_process_stmt(net); int push_flag = 0; (void)x; /* Parameter is not used. */ for (idx = 0 ; idx < ivl_process_attr_cnt(net) ; idx += 1) { ivl_attribute_t attr = ivl_process_attr_val(net, idx); if (strcmp(attr->key, "_ivl_schedule_push") == 0) { push_flag = 1; } else if (strcmp(attr->key, "ivl_combinational") == 0) { push_flag = 1; } } local_count = 0; fprintf(vvp_out, " .scope S_%p;\n", scope); /* Generate the entry label. Just give the thread a number so that we are certain the label is unique. */ fprintf(vvp_out, "T_%u ;\n", thread_count); /* Draw the contents of the thread. */ rc += show_statement(stmt, scope); /* Terminate the thread with either an %end instruction (initial statements) or a %jmp back to the beginning of the thread. */ switch (ivl_process_type(net)) { case IVL_PR_INITIAL: case IVL_PR_FINAL: fprintf(vvp_out, " %%end;\n"); break; case IVL_PR_ALWAYS: fprintf(vvp_out, " %%jmp T_%u;\n", thread_count); break; } /* Now write out the directive that tells vvp where the thread starts. */ switch (ivl_process_type(net)) { case IVL_PR_INITIAL: case IVL_PR_ALWAYS: if (push_flag) { fprintf(vvp_out, " .thread T_%u, $push;\n", thread_count); } else { fprintf(vvp_out, " .thread T_%u;\n", thread_count); } break; case IVL_PR_FINAL: fprintf(vvp_out, " .thread T_%u, $final;\n", thread_count); break; } thread_count += 1; return rc; } int draw_task_definition(ivl_scope_t scope) { int rc = 0; ivl_statement_t def = ivl_scope_def(scope); fprintf(vvp_out, "TD_%s ;\n", vvp_mangle_id(ivl_scope_name(scope))); assert(def); rc += show_statement(def, scope); fprintf(vvp_out, " %%end;\n"); thread_count += 1; return rc; } int draw_func_definition(ivl_scope_t scope) { int rc = 0; ivl_statement_t def = ivl_scope_def(scope); fprintf(vvp_out, "TD_%s ;\n", vvp_mangle_id(ivl_scope_name(scope))); assert(def); rc += show_statement(def, scope); fprintf(vvp_out, " %%end;\n"); thread_count += 1; return rc; } iverilog-10_1/tgt-vvp/vvp_scope.c000066400000000000000000002002641265551621300171250ustar00rootroot00000000000000/* * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include # include # include "ivl_alloc.h" /* * Escape non-symbol characters in ids, and quotes in strings. */ __inline__ static char hex_digit(unsigned i) { i &= 0xf; return i>=10 ? i-10+'A' : i+'0'; } const char *vvp_mangle_id(const char *id) { static char *out = 0x0; static size_t out_len; int nesc = 0; int iout = 0; const char *inp = id; const char nosym[] = "!\"#%&'()*+,-/:;<=>?@[\\]^`{|}~"; char *se = strpbrk(inp, nosym); if (!se) return id; do { int n = se - inp; unsigned int nlen = strlen(id) + 4*(++nesc) + 1; if (out_len < nlen) { out = realloc(out, nlen); out_len = nlen; } if (n) { strncpy(out+iout, inp, n); iout += n; } inp += n+1; out[iout++] = '\\'; switch (*se) { case '\\': case '/': case '<': case '>': out[iout++] = *se; break; default: out[iout++] = 'x'; out[iout++] = hex_digit(*se >> 4); out[iout++] = hex_digit(*se); break; } se = strpbrk(inp, nosym); } while (se); strcpy(out+iout, inp); return out; } const char *vvp_mangle_name(const char *id) { static char *out = 0x0; static size_t out_len; int nesc = 0; int iout = 0; const char *inp = id; const char nosym[] = "\"\\"; char *se = strpbrk(inp, nosym); if (!se) return id; do { int n = se - inp; unsigned int nlen = strlen(id) + 2*(++nesc) + 1; if (out_len < nlen) { out = realloc(out, nlen); out_len = nlen; } if (n) { strncpy(out+iout, inp, n); iout += n; } inp += n+1; out[iout++] = '\\'; out[iout++] = *se; se = strpbrk(inp, nosym); } while (se); strcpy(out+iout, inp); return out; } /* REMOVE ME: vvp_signal_label should not be used. DEAD CODE * Given a signal, generate a string name that is suitable for use as * a label. The only rule is that the same signal will always have the * same label. The result is stored in static memory, so remember to * copy it out. */ const char* vvp_signal_label(ivl_signal_t sig) { static char buf[32]; sprintf(buf, "%p", sig); return buf; } static ivl_signal_t signal_of_nexus(ivl_nexus_t nex, unsigned*word) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; if (ivl_signal_local(sig)) continue; *word = ivl_nexus_ptr_pin(ptr); return sig; } return 0; } unsigned width_of_nexus(ivl_nexus_t nex) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig != 0) return ivl_signal_width(sig); } return 0; } ivl_variable_type_t data_type_of_nexus(ivl_nexus_t nex) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig != 0) return ivl_signal_data_type(sig); } /* shouldn't happen! */ return IVL_VT_NO_TYPE; } static ivl_nexus_ptr_t ivl_logic_pin_ptr(ivl_net_logic_t net, unsigned pin) { ivl_nexus_t nex = ivl_logic_pin(net, pin); unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_net_logic_t tmp = ivl_nexus_ptr_log(ptr); if (tmp == 0) continue; if (tmp != net) continue; if (ivl_nexus_ptr_pin(ptr) != pin) continue; return ptr; } assert(0); return 0; } #if 0 const char*drive_string(ivl_drive_t drive) { switch (drive) { case IVL_DR_HiZ: return ""; case IVL_DR_SMALL: return "sm"; case IVL_DR_MEDIUM: return "me"; case IVL_DR_WEAK: return "we"; case IVL_DR_LARGE: return "la"; case IVL_DR_PULL: return "pu"; case IVL_DR_STRONG: return ""; case IVL_DR_SUPPLY: return "su"; } return ""; } #endif /* * The draw_scope function draws the major functional items within a * scope. This includes the scopes themselves, of course. All the * other functions in this file are in support of that task. */ /* * NEXUS * ivl builds up the netlist into objects connected together by * ivl_nexus_t objects. The nexus receives all the drivers of the * point in the net and resolves the value. The result is then sent to * all the nets that are connected to the nexus. The nets, then, are * read to get the value of the nexus. * * NETS * Nets are interesting and special, because a nexus may be connected * to several of them at once. This can happen, for example, as an * artifact of module port connects, where the inside and the outside * of the module are connected through an in-out port. (In fact, ivl * will simply connect signals that are bound through a port, because * the input/output/inout properties are enforced as compile time.) * * This case is handled by choosing one to receive the value of the * nexus. This one then feeds to another net at the nexus, and so * on. The last net is selected as the output of the nexus. */ /* * When checking if we can elide a buffer we need to keep the buffer * if both the input and output for the buffer are connected only * to netlist signals. This routine performs this check on the * given nexus. */ static unsigned is_netlist_signal(ivl_net_logic_t net, ivl_nexus_t nex) { unsigned idx, rtn; /* Assume that this is a netlist signal. */ rtn = 1; for (idx = 0; idx < ivl_nexus_ptrs(nex); idx += 1) { ivl_nexus_ptr_t nptr; ivl_signal_t sptr; nptr = ivl_nexus_ptr(nex, idx); /* Skip a pointer to the buffer we're checking. */ if (ivl_nexus_ptr_log(nptr) == net) continue; /* Check to see if this is a netlist signal. */ sptr = ivl_nexus_ptr_sig(nptr); if (sptr && !ivl_signal_local(sptr)) continue; /* If we get here then this is not just a netlist signal. */ rtn = 0; break; } return rtn; } /* * This tests a bufz device against an output receiver, and determines * if the device can be skipped. If this function returns false, then a * gate will be generated for this node. Otherwise, the code generator * will connect its input to its output and skip the gate. */ int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) { ivl_nexus_t in_n, out_n; unsigned idx; /* These are the drives we expect. */ ivl_drive_t dr0 = ivl_nexus_ptr_drive0(nptr); ivl_drive_t dr1 = ivl_nexus_ptr_drive1(nptr); int drive_count = 0; /* If the gate carries a delay, it must remain. */ if (ivl_logic_delay(net, 0) != 0) return 0; /* If the input is connected to the output, then do not elide the gate. This is some sort of cycle. */ if (ivl_logic_pin(net, 0) == ivl_logic_pin(net, 1)) return 0; in_n = ivl_logic_pin(net, 1); for (idx = 0 ; idx < ivl_nexus_ptrs(in_n) ; idx += 1) { ivl_nexus_ptr_t in_np = ivl_nexus_ptr(in_n, idx); if (ivl_nexus_ptr_log(in_np) == net) continue; /* If the driver for the source does not match the expected drive, then we need to keep the bufz. This test also catches the case that the input device is really also an input, as that device will have a drive of HiZ. We need to keep BUFZ devices in that case in order to prevent back-flow of data. */ if (ivl_nexus_ptr_drive0(in_np) != dr0) return 0; if (ivl_nexus_ptr_drive1(in_np) != dr1) return 0; drive_count += 1; } /* If the BUFZ input has multiple drivers on its input, then we need to keep this device in order to hide the resolution. */ if (drive_count != 1) return 0; /* If the BUFZ output is connected to a net that is subject to a force statement, we need to keep the BUFZ to prevent back-flow of the forced value. */ out_n = ivl_logic_pin(net, 0); for (idx = 0 ; idx < ivl_nexus_ptrs(out_n) ; idx += 1) { ivl_nexus_ptr_t out_np = ivl_nexus_ptr(out_n, idx); ivl_signal_t out_sig = ivl_nexus_ptr_sig(out_np); if (out_sig && ivl_signal_forced_net(out_sig)) return 0; } /* If both the input and output are netlist signal then we cannot elide a BUFZ since it represents a continuous assignment. */ if (is_netlist_signal(net, ivl_logic_pin(net, 0)) && is_netlist_signal(net, ivl_logic_pin(net, 1)) && (ivl_logic_type(net) == IVL_LO_BUFZ)) { return 0; } return 1; } char* draw_Cr_to_string(double value) { char tmp[256]; uint64_t mant = 0; int sign, expo, vexp; double fract; if (isinf(value)) { if (value > 0) snprintf(tmp, sizeof(tmp), "Cr"); else snprintf(tmp, sizeof(tmp), "Cr"); return strdup(tmp); } if (value != value) { snprintf(tmp, sizeof(tmp), "Cr"); return strdup(tmp); } sign = 0; if (value < 0.0 || (value == 0.0 && 1.0/value < 0.0)) { sign = 0x4000; value *= -1; } fract = frexp(value, &expo); fract = ldexp(fract, 63); mant = fract; expo -= 63; vexp = expo + 0x1000; assert(vexp >= 0); assert(vexp < 0x2000); vexp += sign; snprintf(tmp, sizeof(tmp), "Cr", mant, vexp); return strdup(tmp); } const char*draw_input_from_net(ivl_nexus_t nex) { static char result[32]; unsigned word; ivl_signal_t sig = signal_of_nexus(nex, &word); if (sig == 0) return draw_net_input(nex); if (ivl_signal_type(sig)==IVL_SIT_REG && ivl_signal_dimensions(sig)>0) return draw_net_input(nex); snprintf(result, sizeof result, "v%p_%u", sig, word); return result; } static const char *local_flag_str( ivl_signal_t sig ) { return ivl_signal_local(sig)? "*" : ""; } /* * This function draws a reg/int/variable in the scope. This is a very * simple device to draw as there are no inputs to connect so no need * to scan the nexus. We do have to account for the possibility that * the device is arrayed, though, by making a node for each array element. */ static void draw_reg_in_scope(ivl_signal_t sig) { int msb; int lsb; switch (ivl_signal_packed_dimensions(sig)) { case 0: msb = 0; lsb = 0; break; case 1: msb = ivl_signal_packed_msb(sig, 0); lsb = ivl_signal_packed_lsb(sig, 0); break; default: msb = ivl_signal_width(sig) - 1; lsb = 0; break; } const char *datatype_flag = ivl_signal_integer(sig) ? "/i" : ivl_signal_signed(sig)? "/s" : ""; const char *local_flag = local_flag_str(sig); int vector_dims = 1; switch (ivl_signal_data_type(sig)) { case IVL_VT_BOOL: if (ivl_signal_signed(sig)) datatype_flag = "/2s"; else datatype_flag = "/2u"; break; case IVL_VT_REAL: datatype_flag = "/real"; vector_dims = 0; break; case IVL_VT_STRING: datatype_flag = "/str"; vector_dims = 0; break; case IVL_VT_CLASS: datatype_flag = "/obj"; vector_dims = 0; break; default: break; } /* If the reg objects are collected into an array, then first write out the .array record to declare the array indices. */ if (ivl_signal_dimensions(sig) > 0 && vector_dims==0) { /* Some types cannot be placed in packed dimensions, so do not include packed dimensions. */ unsigned word_count = ivl_signal_array_count(sig); unsigned swapped = ivl_signal_array_addr_swapped(sig); int last = ivl_signal_array_base(sig)+word_count-1; int first = ivl_signal_array_base(sig); fprintf(vvp_out, "v%p .array%s \"%s\", %d %d;\n", sig, datatype_flag, vvp_mangle_name(ivl_signal_basename(sig)), swapped ? first: last, swapped ? last : first); } else if (ivl_signal_dimensions(sig) > 0) { unsigned word_count = ivl_signal_array_count(sig); unsigned swapped = ivl_signal_array_addr_swapped(sig); int last = ivl_signal_array_base(sig)+word_count-1; int first = ivl_signal_array_base(sig); fprintf(vvp_out, "v%p .array%s \"%s\", %d %d, %d %d;\n", sig, datatype_flag, vvp_mangle_name(ivl_signal_basename(sig)), swapped ? first: last, swapped ? last : first, msb, lsb); } else if (ivl_signal_data_type(sig) == IVL_VT_DARRAY) { fprintf(vvp_out, "v%p_0 .var/darray \"%s\";%s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), ivl_signal_local(sig)? " Local signal" : ""); } else if (ivl_signal_data_type(sig) == IVL_VT_QUEUE) { fprintf(vvp_out, "v%p_0 .var/queue \"%s\";%s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), ivl_signal_local(sig)? " Local signal" : ""); } else if (ivl_signal_data_type(sig) == IVL_VT_STRING) { fprintf(vvp_out, "v%p_0 .var/str \"%s\";%s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), ivl_signal_local(sig)? " Local signal" : ""); } else if (ivl_signal_data_type(sig) == IVL_VT_CLASS) { fprintf(vvp_out, "v%p_0 .var/cobj \"%s\";%s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), ivl_signal_local(sig)? " Local signal" : ""); } else { fprintf(vvp_out, "v%p_0 .var%s %s\"%s\", %d %d;%s\n", sig, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, ivl_signal_local(sig)? " Local signal" : "" ); } } /* * This function draws a net. This is a bit more complicated as we * have to find an appropriate functor to connect to the input. */ static void draw_net_in_scope(ivl_signal_t sig) { int msb; int lsb; switch (ivl_signal_packed_dimensions(sig)) { case 0: msb = 0; lsb = 0; break; case 1: msb = ivl_signal_packed_msb(sig, 0); lsb = ivl_signal_packed_lsb(sig, 0); break; default: msb = ivl_signal_width(sig) - 1; lsb = 0; break; } const char*datatype_flag = ivl_signal_signed(sig)? "/s" : ""; const char *local_flag = local_flag_str(sig); unsigned iword; switch (ivl_signal_data_type(sig)) { case IVL_VT_BOOL: if (ivl_signal_signed(sig)) datatype_flag = "/2s"; else datatype_flag = "/2u"; break; case IVL_VT_REAL: datatype_flag = "/real"; break; default: break; } for (iword = 0 ; iword < ivl_signal_array_count(sig); iword += 1) { unsigned word_count = ivl_signal_array_count(sig); unsigned dimensions = ivl_signal_dimensions(sig); struct vvp_nexus_data*nex_data; /* Connect the pin of the signal to something. */ ivl_nexus_t nex = ivl_signal_nex(sig, iword); const char*driver = draw_net_input(nex); nex_data = (struct vvp_nexus_data*)ivl_nexus_get_private(nex); assert(nex_data); if (nex_data->net == 0) { int strength_aware_flag = 0; const char*vec8 = ""; if (nex_data->flags&VVP_NEXUS_DATA_STR) strength_aware_flag = 1; if (nex_data->drivers_count > 1) vec8 = "8"; if (strength_aware_flag) vec8 = "8"; if (iword == 0 && dimensions > 0) { unsigned swapped = ivl_signal_array_addr_swapped(sig); int last = ivl_signal_array_base(sig) + word_count-1; int first = ivl_signal_array_base(sig); fprintf(vvp_out, "v%p .array \"%s\", %d %d;\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), swapped ? first : last, swapped ? last : first); } if (dimensions > 0) { /* If this is a word of an array, then use an array reference in place of the net name. */ fprintf(vvp_out, "v%p_%u .net%s%s v%p %u, %d %d, %s;" " %u drivers%s\n", sig, iword, vec8, datatype_flag, sig, iword, msb, lsb, driver, nex_data->drivers_count, strength_aware_flag?", strength-aware":"" ); } else if (ivl_signal_local(sig) && ivl_scope_is_auto(ivl_signal_scope(sig))) { assert(word_count == 1); fprintf(vvp_out, "; Elide local/automatic net v%p_%u name=%s\n", sig, iword, ivl_signal_basename(sig)); } else if (ivl_signal_local(sig) && nex_data->drivers_count==0) { assert(word_count == 1); fprintf(vvp_out, "; Elide local net with no drivers, v%p_%u name=%s\n", sig, iword, ivl_signal_basename(sig)); } else { /* If this is an isolated word, it uses its own name. */ assert(word_count == 1); fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s; " " %u drivers%s\n", sig, iword, vec8, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, driver, nex_data->drivers_count, strength_aware_flag?", strength-aware":"" ); } nex_data->net = sig; nex_data->net_word = iword; } else if (dimensions > 0) { /* In this case, we have an alias to an existing signal array. this typically is an instance of port collapsing that the elaborator combined to discover that the entire array can be collapsed, so the word count for the signal and the alias *must* match. */ if (word_count == ivl_signal_array_count(nex_data->net)) { if (iword == 0) { fprintf(vvp_out, "v%p .array \"%s\", v%p; Alias to %s \n", sig, vvp_mangle_name(ivl_signal_basename(sig)), nex_data->net, ivl_signal_basename(nex_data->net)); } /* An alias for an individual word. */ } else { if (iword == 0) { unsigned swapped = ivl_signal_array_addr_swapped(sig); int first = ivl_signal_array_base(sig); int last = first + word_count-1; fprintf(vvp_out, "v%p .array \"%s\", %d %d;\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), swapped ? first : last, swapped ? last : first ); } fprintf(vvp_out, "v%p_%u .alias%s v%p %u, %d %d, " "v%p_%u; Alias to %s\n", sig, iword, datatype_flag, sig, iword, msb, lsb, nex_data->net, nex_data->net_word, ivl_signal_basename(nex_data->net)); } } else { /* Finally, we may have an alias that is a word connected to another word. Again, this is a case of port collapsing. */ int strength_aware_flag = 0; const char*vec8 = ""; if (nex_data->flags&VVP_NEXUS_DATA_STR) strength_aware_flag = 1; if (nex_data->drivers_count > 1) vec8 = "8"; if (strength_aware_flag) vec8 = "8"; fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s; " " alias, %u drivers%s\n", sig, iword, vec8, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, driver, nex_data->drivers_count, strength_aware_flag?", strength-aware":""); } } } /* * Check to see if we need a delay. */ static unsigned need_delay(ivl_net_logic_t lptr) { /* If we have no rising delay then we do not have any delays. */ if (ivl_logic_delay(lptr, 0) == 0) { assert(ivl_logic_delay(lptr, 1) == 0); assert(ivl_logic_delay(lptr, 2) == 0); return 0; } return 1; } /* * Draw the appropriate delay statement. Returns zero if there is not a delay. */ static void draw_delay(ivl_net_logic_t lptr) { ivl_expr_t rise_exp = ivl_logic_delay(lptr, 0); ivl_expr_t fall_exp = ivl_logic_delay(lptr, 1); ivl_expr_t decay_exp = ivl_logic_delay(lptr, 2); /* Calculate the width of the delay. We also use a BUFZ for real * values so we need to resize if the first input is real. */ unsigned delay_wid = width_of_nexus(ivl_logic_pin(lptr, 0)); if (data_type_of_nexus(ivl_logic_pin(lptr, 0)) == IVL_VT_REAL) { delay_wid = 0; } /* If the delays are all constants then process them here. */ if (number_is_immediate(rise_exp, 64, 0) && number_is_immediate(fall_exp, 64, 0) && number_is_immediate(decay_exp, 64, 0)) { assert(! number_is_unknown(rise_exp)); assert(! number_is_unknown(fall_exp)); assert(! number_is_unknown(decay_exp)); fprintf(vvp_out, "L_%p .delay %u " "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", lptr, delay_wid, get_number_immediate64(rise_exp), get_number_immediate64(fall_exp), get_number_immediate64(decay_exp), lptr); /* For a variable delay we indicate only two delays by setting the * decay time to zero. */ } else { ivl_signal_t sig; assert(ivl_expr_type(rise_exp) == IVL_EX_SIGNAL); assert(ivl_expr_type(fall_exp) == IVL_EX_SIGNAL); assert((decay_exp == 0) || (ivl_expr_type(decay_exp) == IVL_EX_SIGNAL)); fprintf(vvp_out, "L_%p .delay %u L_%p/d", lptr, delay_wid, lptr); sig = ivl_expr_signal(rise_exp); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", %s", draw_net_input(ivl_signal_nex(sig,0))); sig = ivl_expr_signal(fall_exp); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", %s", draw_net_input(ivl_signal_nex(sig,0))); if (decay_exp) { sig = ivl_expr_signal(decay_exp); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", %s;\n", draw_net_input(ivl_signal_nex(sig,0))); } else { fprintf(vvp_out, ", 0;\n"); } } } static void draw_udp_def(ivl_udp_t udp) { unsigned init; unsigned i; switch (ivl_udp_init(udp)) { case '0': init = 0; break; case '1': init = 1; break; default: init = 2; break; } if (ivl_udp_sequ(udp)) fprintf(vvp_out, "UDP_%s .udp/sequ \"%s\", %u, %u", vvp_mangle_id(ivl_udp_name(udp)), vvp_mangle_name(ivl_udp_name(udp)), ivl_udp_nin(udp), init ); else fprintf(vvp_out, "UDP_%s .udp/comb \"%s\", %u", vvp_mangle_id(ivl_udp_name(udp)), vvp_mangle_name(ivl_udp_name(udp)), ivl_udp_nin(udp)); for (i=0; i= nudps) { udps = realloc(udps, (nudps+1)*sizeof(ivl_udp_t)); udps[nudps++] = udp; draw_udp_def(udp); } /* * We need to process the arguments first so any evaluation code * (.resolv, etc.) can be built before we build the .udp call. * This matches what is done for the other primitives. */ ninp = ivl_logic_pins(lptr) - 1; input_strings = calloc(ninp, sizeof(char*)); for (pdx = 0 ; pdx < ninp ; pdx += 1) { ivl_nexus_t nex = ivl_logic_pin(lptr, pdx+1); /* Unlike other logic gates, primitives may have unconnected * inputs. The proper behavior is to attach a HiZ to the * port. */ if (nex == 0) { assert(ivl_logic_width(lptr) == 1); input_strings[pdx] = "C4"; } else { input_strings[pdx] = draw_net_input(nex); } } /* Because vvp uses a wide functor for the output of a UDP we need * to define the output delay net when needed, otherwise it will * not be cleaned up correctly (gives a valgrind warning). */ if (need_delay_flag) { fprintf(vvp_out, "v%p_0 .net *\"_d%p\", 0 0, L_%p/d;\n", lptr, lptr, lptr); } /* The same situation exists if a modpath is used to connect the UDP * output to the true output signal. For this case the modpath is * the only thing connected to the UDP output. */ if (udp_has_modpath_output(lptr)) { fprintf(vvp_out, "v%p_0 .net *\"_m%p\", 0 0, L_%p;\n", lptr, lptr, lptr); } /* Generate the UDP call. */ fprintf(vvp_out, "L_%p%s .udp UDP_%s", lptr, need_delay_flag ? "/d" : "", vvp_mangle_id(ivl_udp_name(udp))); for (pdx = 0 ; pdx < ninp ; pdx += 1) { fprintf(vvp_out, ", %s", input_strings[pdx]); } free(input_strings); fprintf(vvp_out, ";\n"); /* Generate a delay when needed. */ if (need_delay_flag) draw_delay(lptr); } static void draw_logic_in_scope(ivl_net_logic_t lptr) { unsigned pdx; const char*ltype = "?"; const char*lcasc = 0; char identity_val = '0'; /* Do we need a delay? */ unsigned need_delay_flag = need_delay(lptr); unsigned vector_width = width_of_nexus(ivl_logic_pin(lptr, 0)); ivl_drive_t str0 = ivl_logic_drive0(lptr); ivl_drive_t str1 = ivl_logic_drive1(lptr); int level; int ninp; const char **input_strings; switch (ivl_logic_type(lptr)) { case IVL_LO_UDP: draw_udp_in_scope(lptr); return; case IVL_LO_BUFZ: { /* Draw bufz objects, but only if the gate cannot be elided. If I can elide it, then the draw_nex_input will take care of it for me. */ ivl_nexus_ptr_t nptr = ivl_logic_pin_ptr(lptr,0); ltype = "BUFZ"; if (can_elide_bufz(lptr, nptr)) return; break; } case IVL_LO_BUFT: { /* Draw bufz objects, but only if the gate cannot be elided. If I can elide it, then the draw_nex_input will take care of it for me. */ ivl_nexus_ptr_t nptr = ivl_logic_pin_ptr(lptr,0); ltype = "BUFT"; if (can_elide_bufz(lptr, nptr)) return; break; } case IVL_LO_PULLDOWN: case IVL_LO_PULLUP: /* Skip pullup and pulldown objects. Things that have pull objects as inputs will instead generate the appropriate C symbol. */ return; case IVL_LO_AND: ltype = "AND"; identity_val = '1'; break; case IVL_LO_BUF: ltype = "BUF"; break; case IVL_LO_BUFIF0: ltype = "BUFIF0"; break; case IVL_LO_BUFIF1: ltype = "BUFIF1"; break; case IVL_LO_NAND: ltype = "NAND"; lcasc = "AND"; identity_val = '1'; break; case IVL_LO_NOR: ltype = "NOR"; lcasc = "OR"; break; case IVL_LO_NOT: ltype = "NOT"; break; case IVL_LO_OR: ltype = "OR"; break; case IVL_LO_XNOR: ltype = "XNOR"; lcasc = "XOR"; break; case IVL_LO_XOR: ltype = "XOR"; break; case IVL_LO_CMOS: ltype = "CMOS"; break; case IVL_LO_PMOS: ltype = "PMOS"; break; case IVL_LO_NMOS: ltype = "NMOS"; break; case IVL_LO_RCMOS: ltype = "RCMOS"; break; case IVL_LO_RPMOS: ltype = "RPMOS"; break; case IVL_LO_RNMOS: ltype = "RNMOS"; break; case IVL_LO_NOTIF0: ltype = "NOTIF0"; break; case IVL_LO_NOTIF1: ltype = "NOTIF1"; break; default: fprintf(stderr, "vvp.tgt: error: Unhandled logic type: %d\n", ivl_logic_type(lptr)); ltype = "?"; break; } if (!lcasc) lcasc = ltype; /* Get all the input label that I will use for parameters to the functor that I create later. */ ninp = ivl_logic_pins(lptr) - 1; assert(ninp >= 0); input_strings = calloc(ninp, sizeof(char*)); for (pdx = 0 ; pdx < (unsigned)ninp ; pdx += 1) input_strings[pdx] = draw_net_input(ivl_logic_pin(lptr, pdx+1)); level = 0; while (ninp) { unsigned inst; for (inst = 0; inst < (unsigned)ninp; inst += 4) { if (ninp > 4) fprintf(vvp_out, "L_%p/%d/%u .functor %s %u", lptr, level, inst, lcasc, vector_width); else { fprintf(vvp_out, "L_%p%s .functor %s %u", lptr, need_delay_flag? "/d" : "", ltype, vector_width); if (str0 != IVL_DR_STRONG || str1 != IVL_DR_STRONG) fprintf(vvp_out, " [%d %d]", str0, str1); } for (pdx = inst; pdx < (unsigned)ninp && pdx < inst+4 ; pdx += 1) { if (level) { fprintf(vvp_out, ", L_%p/%d/%d", lptr, level - 1, pdx*4); } else { fprintf(vvp_out, ", %s", input_strings[pdx]); } } for ( ; pdx < inst+4 ; pdx += 1) { unsigned wdx; fprintf(vvp_out, ", C4<"); for (wdx = 0 ; wdx < vector_width ; wdx += 1) fprintf(vvp_out, "%c", identity_val); fprintf(vvp_out, ">"); } fprintf(vvp_out, ";\n"); } if (ninp > 4) ninp = (ninp+3) / 4; else ninp = 0; level += 1; } /* Free the array of char*. The strings themselves are persistent, held by the ivl_nexus_t objects. */ free(input_strings); /* Generate a delay when needed. */ if (need_delay_flag) draw_delay(lptr); } static void draw_event_in_scope(ivl_event_t obj) { char tmp[4][32]; const unsigned ntmp = sizeof(tmp) / sizeof(tmp[0]); unsigned nany = ivl_event_nany(obj); unsigned nneg = ivl_event_nneg(obj); unsigned npos = ivl_event_npos(obj); unsigned cnt = 0; /* Figure out how many probe functors are needed. */ if (nany > 0) cnt += (nany+ntmp-1) / ntmp; if (nneg > 0) cnt += (nneg+ntmp-1) / ntmp; if (npos > 0) cnt += (npos+ntmp-1) / ntmp; if (cnt == 0) { /* If none are needed, then this is a named event. The code needed is easy. */ fprintf(vvp_out, "E_%p .event \"%s\";\n", obj, vvp_mangle_name(ivl_event_basename(obj))); } else if (cnt > 1) { /* There are a bunch of events that need to be event/or combined. */ unsigned idx; unsigned ecnt = 0; for (idx = 0 ; idx < nany ; idx += ntmp, ecnt += 1) { unsigned sub, top; top = idx + ntmp; if (nany < top) top = nany; for (sub = idx ; sub < top ; sub += 1) { ivl_nexus_t nex = ivl_event_any(obj, sub); strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0])); } fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt); for (sub = idx ; sub < top ; sub += 1) fprintf(vvp_out, ", %s", tmp[sub-idx]); fprintf(vvp_out, ";\n"); } for (idx = 0 ; idx < nneg ; idx += ntmp, ecnt += 1) { unsigned sub, top; top = idx + ntmp; if (nneg < top) top = nneg; for (sub = idx ; sub < top ; sub += 1) { ivl_nexus_t nex = ivl_event_neg(obj, sub); strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0])); } fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt); for (sub = idx ; sub < top ; sub += 1) fprintf(vvp_out, ", %s", tmp[sub-idx]); fprintf(vvp_out, ";\n"); } for (idx = 0 ; idx < npos ; idx += ntmp, ecnt += 1) { unsigned sub, top; top = idx + ntmp; if (npos < top) top = npos; for (sub = idx ; sub < top ; sub += 1) { ivl_nexus_t nex = ivl_event_pos(obj, sub); strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0])); } fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt); for (sub = idx ; sub < top ; sub += 1) fprintf(vvp_out, ", %s", tmp[sub-idx]); fprintf(vvp_out, ";\n"); } assert(ecnt == cnt); fprintf(vvp_out, "E_%p .event/or", obj); fprintf(vvp_out, " E_%p/0", obj); for (idx = 1 ; idx < cnt ; idx += 1) fprintf(vvp_out, ", E_%p/%u", obj, idx); fprintf(vvp_out, ";\n"); } else { unsigned num_input_strings = nany + nneg + npos; unsigned idx; const char*edge = 0; assert(num_input_strings <= ntmp); if (nany > 0) { assert((nneg + npos) == 0); edge = "edge"; for (idx = 0 ; idx < nany ; idx += 1) { ivl_nexus_t nex = ivl_event_any(obj, idx); strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0])); } } else if (nneg > 0) { assert((nany + npos) == 0); edge = "negedge"; for (idx = 0 ; idx < nneg ; idx += 1) { ivl_nexus_t nex = ivl_event_neg(obj, idx); strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0])); } } else { assert((nany + nneg) == 0); edge = "posedge"; for (idx = 0 ; idx < npos ; idx += 1) { ivl_nexus_t nex = ivl_event_pos(obj, idx); strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0])); } } fprintf(vvp_out, "E_%p .event %s", obj, edge); for (idx = 0 ; idx < num_input_strings ; idx += 1) { fprintf(vvp_out, ", %s", tmp[idx]); } fprintf(vvp_out, ";\n"); } } /* * This function draws any functors needed to calculate the input to * this nexus, and leaves in the data array strings that can be used * as functor arguments. The strings are from the draw_net_input * function, which in turn returns nexus names, so the strings are * safe to pass around. */ static void draw_lpm_data_inputs(ivl_lpm_t net, unsigned base, unsigned ndata, const char**src_table) { unsigned idx; for (idx = 0 ; idx < ndata ; idx += 1) { ivl_nexus_t nex = ivl_lpm_data(net, base+idx); src_table[idx] = draw_net_input(nex); } } /* * If needed, draw a .delay node to delay the output from the LPM * device. Return the "/d" string if we drew this .delay node, or the * "" string if the node was not needed. The caller uses that string * to modify labels that are generated. */ static const char* draw_lpm_output_delay(ivl_lpm_t net, ivl_variable_type_t dt) { ivl_expr_t d_rise = ivl_lpm_delay(net, 0); ivl_expr_t d_fall = ivl_lpm_delay(net, 1); ivl_expr_t d_decay = ivl_lpm_delay(net, 2); unsigned width = ivl_lpm_width(net); /* The comparison and reduction operators only have a single output * bit to delay. */ switch (ivl_lpm_type(net)) { case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: width = 1; default: break; } if (dt == IVL_VT_REAL) width = 0; const char*dly = ""; if (d_rise != 0) { assert(number_is_immediate(d_rise, 64, 0)); assert(number_is_immediate(d_fall, 64, 0)); assert(number_is_immediate(d_decay, 64, 0)); assert(! number_is_unknown(d_rise)); assert(! number_is_unknown(d_fall)); assert(! number_is_unknown(d_decay)); dly = "/d"; fprintf(vvp_out, "L_%p .delay %u (%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")" " L_%p/d;\n", net, width, get_number_immediate64(d_rise), get_number_immediate64(d_fall), get_number_immediate64(d_decay), net); } return dly; } static void draw_lpm_abs(ivl_lpm_t net) { const char*src_table[1]; ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_data(net,0)); const char*dly; draw_lpm_data_inputs(net, 0, 1, src_table); dly = draw_lpm_output_delay(net, dt); fprintf(vvp_out, "L_%p%s .abs %s;\n", net, dly, src_table[0]); } static void draw_lpm_cast_int2(ivl_lpm_t net) { const char*src_table[1]; const char*dly; draw_lpm_data_inputs(net, 0, 1, src_table); dly = draw_lpm_output_delay(net, IVL_VT_BOOL); fprintf(vvp_out, "L_%p%s .cast/2 %u, %s;\n", net, dly, ivl_lpm_width(net), src_table[0]); } static void draw_lpm_cast_int(ivl_lpm_t net) { const char*src_table[1]; const char*dly; draw_lpm_data_inputs(net, 0, 1, src_table); dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); fprintf(vvp_out, "L_%p%s .cast/int %u, %s;\n", net, dly, ivl_lpm_width(net), src_table[0]); } static void draw_lpm_cast_real(ivl_lpm_t net) { const char*src_table[1]; const char*dly; const char*is_signed = ""; draw_lpm_data_inputs(net, 0, 1, src_table); dly = draw_lpm_output_delay(net, IVL_VT_REAL); if (ivl_lpm_signed(net)) is_signed = ".s"; fprintf(vvp_out, "L_%p%s .cast/real%s %s;\n", net, dly, is_signed, src_table[0]); } static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; unsigned width; const char*type = ""; ivl_variable_type_t dta = data_type_of_nexus(ivl_lpm_data(net,0)); ivl_variable_type_t dtb = data_type_of_nexus(ivl_lpm_data(net,1)); ivl_variable_type_t dto = IVL_VT_LOGIC; const char*dly; if (dta == IVL_VT_REAL || dtb == IVL_VT_REAL) dto = IVL_VT_REAL; width = ivl_lpm_width(net); switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: if (dto == IVL_VT_REAL) type = "sum.r"; else type = "sum"; break; case IVL_LPM_SUB: if (dto == IVL_VT_REAL) type = "sub.r"; else type = "sub"; break; case IVL_LPM_MULT: if (dto == IVL_VT_REAL) type = "mult.r"; else type = "mult"; break; case IVL_LPM_DIVIDE: if (dto == IVL_VT_REAL) type = "div.r"; else if (ivl_lpm_signed(net)) type = "div.s"; else type = "div"; break; case IVL_LPM_MOD: if (dto == IVL_VT_REAL) type = "mod.r"; else if (ivl_lpm_signed(net)) type = "mod.s"; else type = "mod"; break; case IVL_LPM_POW: if (dto == IVL_VT_REAL) type = "pow.r"; else if (ivl_lpm_signed(net)) type = "pow.s"; else type = "pow"; break; default: assert(0); } draw_lpm_data_inputs(net, 0, 2, src_table); dly = draw_lpm_output_delay(net, dto); fprintf(vvp_out, "L_%p%s .arith/%s %u, %s, %s;\n", net, dly, type, width, src_table[0], src_table[1]); } /* * The read port to an array is generated as a single record that takes * the address as an input. */ static void draw_lpm_array(ivl_lpm_t net) { ivl_nexus_t nex; ivl_signal_t mem = ivl_lpm_array(net); const char*tmp; nex = ivl_lpm_select(net); tmp = draw_net_input(nex); fprintf(vvp_out, "L_%p .array/port v%p, %s;\n", net, mem, tmp); } static void draw_lpm_cmp(ivl_lpm_t net) { const char*src_table[2]; unsigned width; const char*type = ""; const char*signed_string = ivl_lpm_signed(net)? ".s" : ""; ivl_variable_type_t dta = data_type_of_nexus(ivl_lpm_data(net,0)); ivl_variable_type_t dtb = data_type_of_nexus(ivl_lpm_data(net,1)); ivl_variable_type_t dtc = IVL_VT_LOGIC; const char*dly; if (dta == IVL_VT_REAL || dtb == IVL_VT_REAL) dtc = IVL_VT_REAL; width = ivl_lpm_width(net); switch (ivl_lpm_type(net)) { case IVL_LPM_CMP_EEQ: assert(dtc != IVL_VT_REAL); /* Should never get here! */ type = "eeq"; signed_string = ""; break; case IVL_LPM_CMP_EQ: if (dtc == IVL_VT_REAL) type = "eq.r"; else type = "eq"; signed_string = ""; break; case IVL_LPM_CMP_EQX: assert(dtc != IVL_VT_REAL); type = "eqx"; signed_string = ""; break; case IVL_LPM_CMP_EQZ: assert(dtc != IVL_VT_REAL); type = "eqz"; signed_string = ""; break; case IVL_LPM_CMP_GE: if (dtc == IVL_VT_REAL) { type = "ge.r"; signed_string = ""; } else type = "ge"; break; case IVL_LPM_CMP_GT: if (dtc == IVL_VT_REAL) { type = "gt.r"; signed_string = ""; } else type = "gt"; break; case IVL_LPM_CMP_NE: if (dtc == IVL_VT_REAL) type = "ne.r"; else type = "ne"; signed_string = ""; break; case IVL_LPM_CMP_NEE: assert(dtc != IVL_VT_REAL); /* Should never get here! */ type = "nee"; signed_string = ""; break; default: assert(0); } draw_lpm_data_inputs(net, 0, 2, src_table); /* The output of a compare is always logical. */ dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); fprintf(vvp_out, "L_%p%s .cmp/%s%s %u, %s, %s;\n", net, dly, type, signed_string, width, src_table[0], src_table[1]); } /* * This function draws the arguments to a .const node using the * lpm inputs starting at "start" and for "cnt" inputs. This input * count must be <= 4. It is up to the caller to write the header part * of the statement, and to organize the data into multiple * statements. * * Return the width of the final concatenation. */ static unsigned lpm_concat_inputs(ivl_lpm_t net, unsigned start, unsigned cnt, const char*src_table[]) { unsigned idx; unsigned wid = 0; assert(cnt <= 4); /* First, draw the [L M N O] part of the statement, the list of widths for the .concat statement. */ fprintf(vvp_out, "["); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_nexus_t nex = ivl_lpm_data(net, start+idx); unsigned nexus_width = width_of_nexus(nex); fprintf(vvp_out, " %u", nexus_width); wid += nexus_width; } for ( ; idx < 4 ; idx += 1) fprintf(vvp_out, " 0"); fprintf(vvp_out, "]"); for (idx = 0 ; idx < cnt ; idx += 1) { fprintf(vvp_out, ", %s", src_table[idx]); } fprintf(vvp_out, ";\n"); return wid; } /* * Implement the general IVL_LPM_CONCAT using .concat nodes. Use as * many nested nodes as necessary to support the desired number of * input vectors. */ static void draw_lpm_concat(ivl_lpm_t net) { const char*src_table[4]; unsigned icnt = ivl_lpm_size(net); const char*dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); const char*z = ivl_lpm_type(net)==IVL_LPM_CONCATZ? "8" : ""; if (icnt <= 4) { /* This is the easiest case. There are 4 or fewer input vectors, so the entire IVL_LPM_CONCAT can be implemented with a single .concat node. */ draw_lpm_data_inputs(net, 0, icnt, src_table); fprintf(vvp_out, "L_%p%s .concat%s ", net, dly, z); lpm_concat_inputs(net, 0, icnt, src_table); } else { /* If there are more than 4 inputs, things get more complicated. We need to generate a balanced tree of .concat nodes to blend the inputs down to a single root node, that becomes the output from the concatenation. */ unsigned idx, depth; struct concat_tree { unsigned base; unsigned wid; } *tree; tree = malloc((icnt + 3)/4 * sizeof(struct concat_tree)); /* First, fill in all the leaves with the initial inputs to the tree. After this loop, there are (icnt+3)/4 .concat nodes drawn, that together take all the inputs. */ for (idx = 0 ; idx < icnt ; idx += 4) { unsigned wid = 0; unsigned trans = 4; if ((idx + trans) > icnt) trans = icnt - idx; draw_lpm_data_inputs(net, idx, trans, src_table); fprintf(vvp_out, "LS_%p_0_%u .concat%s ", net, idx, z); wid = lpm_concat_inputs(net, idx, trans, src_table); tree[idx/4].base = idx; tree[idx/4].wid = wid; } /* icnt is the input count for the level. It is the number of .concats of the previous level that have to be concatenated at the current level. (This is not the same as the bit width.) */ icnt = (icnt + 3)/4; /* Tree now has icnt nodes that are depth=0 concat nodes which take in the leaf inputs. The while loop below starts and ends with a tree of icnt nodes. Each time through, there are 1/4 the nodes we started with. Thus, we eventually get down to <=4 nodes, and that is when we fall out of the loop. */ depth = 1; while (icnt > 4) { for (idx = 0 ; idx < icnt ; idx += 4) { unsigned tdx; unsigned wid = 0; unsigned trans = 4; if ((idx+trans) > icnt) trans = icnt - idx; fprintf(vvp_out, "LS_%p_%u_%u .concat%s [", net, depth, idx, z); for (tdx = 0 ; tdx < trans ; tdx += 1) { fprintf(vvp_out, " %u", tree[idx+tdx].wid); wid += tree[idx+tdx].wid; } for ( ; tdx < 4 ; tdx += 1) fprintf(vvp_out, " 0"); fprintf(vvp_out, "]"); for (tdx = 0; tdx < trans ; tdx += 1) { fprintf(vvp_out, ", LS_%p_%u_%u", net, depth-1, tree[idx+tdx].base); } fprintf(vvp_out, ";\n"); tree[idx/4].base = idx; tree[idx/4].wid = wid; } depth += 1; icnt = (icnt + 3)/4; } /* Finally, draw the root node that takes in the final row of tree nodes and generates a single output. */ fprintf(vvp_out, "L_%p%s .concat%s [", net, dly, z); for (idx = 0 ; idx < icnt ; idx += 1) fprintf(vvp_out, " %u", tree[idx].wid); for ( ; idx < 4 ; idx += 1) fprintf(vvp_out, " 0"); fprintf(vvp_out, "]"); for (idx = 0 ; idx < icnt ; idx += 1) fprintf(vvp_out, ", LS_%p_%u_%u", net, depth-1, tree[idx].base); fprintf(vvp_out, ";\n"); free(tree); } } /* * Emit a DFF primitive. This uses the following syntax: * * .dff , , [, [, ]]; */ static void draw_lpm_ff(ivl_lpm_t net) { ivl_nexus_t nex; /* Sync set/clear control is not currently supported. This is not * a problem, as synthesis can incorporate this in the D input * expression. All modern synthesis tools do this as a matter of * course, as most cell libraries don't contain flip-flops with * sync set/clear. */ assert(ivl_lpm_sync_clr(net) == 0); assert(ivl_lpm_sync_set(net) == 0); unsigned width = ivl_lpm_width(net); char*edge = ivl_lpm_negedge(net) ? "n" : "p"; if (ivl_lpm_async_clr(net)) { /* Synthesis doesn't currently support both set and clear. If it ever does, it might be better to implement the flip-flop as a UDP. See tgt-vlog95 for an example of how to do this. */ if (ivl_lpm_async_set(net)) { fprintf(stderr, "%s:%u:vvp.tgt: sorry: No support for a DFF " "with both an async. set and clear.\n", ivl_lpm_file(net), ivl_lpm_lineno(net)); vvp_errors += 1; } fprintf(vvp_out, "L_%p .dff/%s/aclr %u ", net, edge, width); } else if (ivl_lpm_async_set(net)) { fprintf(vvp_out, "L_%p .dff/%s/aset %u ", net, edge, width); } else { fprintf(vvp_out, "L_%p .dff/%s %u ", net, edge, width); } nex = ivl_lpm_data(net,0); assert(nex); fprintf(vvp_out, "%s", draw_net_input(nex)); assert(width_of_nexus(nex) == width);; nex = ivl_lpm_clk(net); assert(nex); assert(width_of_nexus(nex) == 1);; fprintf(vvp_out, ", %s", draw_net_input(nex)); nex = ivl_lpm_enable(net); if (nex) { assert(width_of_nexus(nex) == 1);; fprintf(vvp_out, ", %s", draw_net_input(nex)); } else { fprintf(vvp_out, ", C4<1>"); } if ( (nex = ivl_lpm_async_clr(net)) ) { assert(width_of_nexus(nex) == 1);; fprintf(vvp_out, ", %s", draw_net_input(nex)); } if ( (nex = ivl_lpm_async_set(net)) ) { ivl_expr_t val = ivl_lpm_aset_value(net); assert(width_of_nexus(nex) == 1);; fprintf(vvp_out, ", %s", draw_net_input(nex)); if (val) { unsigned nbits = ivl_expr_width(val); const char*bits = ivl_expr_bits(val); unsigned bb; assert(nbits == width); fprintf(vvp_out, ", C4<"); for (bb = 0 ; bb < nbits; bb += 1) fprintf(vvp_out, "%c", bits[nbits-bb-1]); fprintf(vvp_out, ">"); } } fprintf(vvp_out, ";\n"); } static void draw_lpm_shiftl(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); const char* signed_flag = ivl_lpm_signed(net)? "s" : ""; const char*dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); if (ivl_lpm_type(net) == IVL_LPM_SHIFTR) fprintf(vvp_out, "L_%p%s .shift/r%s %u", net, dly, signed_flag, width); else fprintf(vvp_out, "L_%p%s .shift/l %u", net, dly, width); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net, 0))); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net, 1))); fprintf(vvp_out, ";\n"); } static void draw_type_string_of_nex(ivl_nexus_t nex) { switch (data_type_of_nexus(nex)) { case IVL_VT_REAL: fprintf(vvp_out, "r"); break; case IVL_VT_LOGIC: case IVL_VT_BOOL: fprintf(vvp_out, "v%u", width_of_nexus(nex)); break; default: assert(0); break; } } /* Check to see if the output of the system function is connected with * a modpath. */ static int sfunc_has_modpath_output(ivl_lpm_t lptr) { /* The q port is the output connection (nexus). */ ivl_nexus_t nex = ivl_lpm_q(lptr); ivl_scope_t scope = ivl_lpm_scope(lptr); unsigned idx; /* Check to see if there is a signal connected to the output. */ for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; /* The modpath will be connected to the system function * output. */ if ((ivl_signal_scope(sig) == scope) && (ivl_signal_port(sig) == IVL_SIP_OUTPUT) && (ivl_signal_npath(sig))) return 1; } return 0; } /* Emit a definition for a net that has a delay or modpath. */ static void draw_sfunc_output_def(ivl_lpm_t net, char type) { ivl_nexus_t nex = ivl_lpm_q(net); const char *suf = (type == 'd') ? "/d" : ""; switch (data_type_of_nexus(nex)) { case IVL_VT_REAL: fprintf(vvp_out, "v%p_0 .net/real *\"_%c%p\", 0 0, L_%p%s;\n", net, type, net, net, suf); break; case IVL_VT_LOGIC: case IVL_VT_BOOL: fprintf(vvp_out, "v%p_0 .net *\"_%c%p\", %u 0, L_%p%s;\n", net, type, net, width_of_nexus(nex)-1, net, suf); break; default: assert(0); break; } } static void draw_lpm_sfunc(ivl_lpm_t net) { unsigned idx; ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_q(net)); const char*dly = draw_lpm_output_delay(net, dt); /* Because vvp uses a wide functor for the output of a system * function we need to define the output delay net when needed, * otherwise it will not be cleaned up correctly (gives a * valgrind warning). */ if (*dly != 0) { draw_sfunc_output_def(net, 'd'); } /* The same situation exists if a modpath is used to connect the * system function output to the true output signal. For this case * the modpath is the only thing connected to the UDP output. */ if (sfunc_has_modpath_output(net)) { draw_sfunc_output_def(net, 'm'); } if (ivl_lpm_trigger(net)) fprintf(vvp_out, "L_%p%s .sfunc/e %u %u \"%s\", E_%p", net, dly, ivl_file_table_index(ivl_lpm_file(net)), ivl_lpm_lineno(net), ivl_lpm_string(net), ivl_lpm_trigger(net)); else fprintf(vvp_out, "L_%p%s .sfunc %u %u \"%s\"", net, dly, ivl_file_table_index(ivl_lpm_file(net)), ivl_lpm_lineno(net), ivl_lpm_string(net)); /* Print the function type descriptor string. */ fprintf(vvp_out, ", \""); draw_type_string_of_nex(ivl_lpm_q(net)); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) draw_type_string_of_nex(ivl_lpm_data(net,idx)); fprintf(vvp_out, "\""); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) { fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx))); } fprintf(vvp_out, ";\n"); } static void draw_lpm_ufunc(ivl_lpm_t net) { unsigned idx; unsigned ninp; const char**input_strings; ivl_scope_t def = ivl_lpm_define(net); ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_q(net)); const char*dly = draw_lpm_output_delay(net, dt); /* Get all the input labels that I will use for net signals that connect to the inputs of the function. */ ninp = ivl_lpm_size(net); input_strings = calloc(ninp, sizeof(char*)); for (idx = 0 ; idx < ninp ; idx += 1) input_strings[idx] = draw_net_input(ivl_lpm_data(net, idx)); if (ivl_lpm_trigger(net)) fprintf(vvp_out, "L_%p%s .ufunc/e TD_%s, %u, E_%p", net, dly, vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net), ivl_lpm_trigger(net)); else fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net)); /* Print all the net signals that connect to the input of the function. */ for (idx = 0 ; idx < ninp ; idx += 1) { fprintf(vvp_out, ", %s", input_strings[idx]); } free(input_strings); assert((ninp+1) == ivl_scope_ports(def)); /* Now print all the variables in the function scope that receive the input values given in the previous list. */ for (idx = 0 ; idx < ninp ; idx += 1) { ivl_signal_t psig = ivl_scope_port(def, idx+1); if (idx == 0) fprintf(vvp_out, " ("); else fprintf(vvp_out, ", "); assert(ivl_signal_dimensions(psig) == 0); fprintf(vvp_out, "v%p_0", psig); } fprintf(vvp_out, ")"); /* Now print the reference to the signal from which the result is collected. */ { ivl_signal_t psig = ivl_scope_port(def, 0); assert(ivl_lpm_width(net) == ivl_signal_width(psig)); assert(ivl_signal_dimensions(psig) == 0); fprintf(vvp_out, " v%p_0", psig); } /* Finally, print the scope identifier. */ fprintf(vvp_out, " S_%p;\n", def); } /* * Handle a PART SELECT device. This has a single input and output, * plus an optional extra input that is a non-constant base. */ static void draw_lpm_part(ivl_lpm_t net) { unsigned width, base; ivl_nexus_t sel; const char*dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); width = ivl_lpm_width(net); base = ivl_lpm_base(net); sel = ivl_lpm_data(net,1); if (sel == 0) { fprintf(vvp_out, "L_%p%s .part %s", net, dly, draw_net_input(ivl_lpm_data(net, 0))); fprintf(vvp_out, ", %u, %u;\n", base, width); } else { const char*sel_symbol = draw_net_input(sel); fprintf(vvp_out, "L_%p%s .part/v%s %s", net, dly, (ivl_lpm_signed(net) ? ".s" : ""), draw_net_input(ivl_lpm_data(net,0))); fprintf(vvp_out, ", %s", sel_symbol); fprintf(vvp_out, ", %u;\n", width); } } /* * Handle a PART SELECT PV device. Generate a .part/pv node that * includes the part input, and the geometry of the part. */ static void draw_lpm_part_pv(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned base = ivl_lpm_base(net); unsigned signal_width = width_of_nexus(ivl_lpm_q(net)); fprintf(vvp_out, "L_%p .part/pv %s", net, draw_net_input(ivl_lpm_data(net, 0))); fprintf(vvp_out, ", %u, %u, %u;\n", base, width, signal_width); } /* * Draw unary reduction devices. */ static void draw_lpm_re(ivl_lpm_t net, const char*type) { const char*dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); fprintf(vvp_out, "L_%p%s .reduce/%s %s;\n", net, dly, type, draw_net_input(ivl_lpm_data(net,0))); } static void draw_lpm_repeat(ivl_lpm_t net) { const char*dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); fprintf(vvp_out, "L_%p%s .repeat %u, %u, %s;\n", net, dly, ivl_lpm_width(net), ivl_lpm_size(net), draw_net_input(ivl_lpm_data(net,0))); } static void draw_lpm_sign_ext(ivl_lpm_t net) { const char*dly = draw_lpm_output_delay(net, IVL_VT_LOGIC); fprintf(vvp_out, "L_%p%s .extend/s %u, %s;\n", net, dly, ivl_lpm_width(net), draw_net_input(ivl_lpm_data(net,0))); } static void draw_lpm_in_scope(ivl_lpm_t net) { switch (ivl_lpm_type(net)) { case IVL_LPM_ABS: draw_lpm_abs(net); return; case IVL_LPM_CAST_INT: draw_lpm_cast_int(net); return; case IVL_LPM_CAST_INT2: draw_lpm_cast_int2(net); return; case IVL_LPM_CAST_REAL: draw_lpm_cast_real(net); return; case IVL_LPM_ADD: case IVL_LPM_SUB: case IVL_LPM_MULT: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_POW: draw_lpm_add(net); return; case IVL_LPM_ARRAY: draw_lpm_array(net); return; case IVL_LPM_PART_VP: draw_lpm_part(net); return; case IVL_LPM_PART_PV: draw_lpm_part_pv(net); return; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: draw_lpm_concat(net); return; case IVL_LPM_FF: draw_lpm_ff(net); return; case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: draw_lpm_cmp(net); return; case IVL_LPM_MUX: draw_lpm_mux(net); return; case IVL_LPM_RE_AND: draw_lpm_re(net, "and"); return; case IVL_LPM_RE_OR: draw_lpm_re(net, "or"); return; case IVL_LPM_RE_XOR: draw_lpm_re(net, "xor"); return; case IVL_LPM_RE_NAND: draw_lpm_re(net, "nand"); return; case IVL_LPM_RE_NOR: draw_lpm_re(net, "nor"); return; case IVL_LPM_RE_XNOR: draw_lpm_re(net, "xnor"); return; case IVL_LPM_REPEAT: draw_lpm_repeat(net); return; case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: draw_lpm_shiftl(net); return; case IVL_LPM_SIGN_EXT: draw_lpm_sign_ext(net); return; case IVL_LPM_SFUNC: draw_lpm_sfunc(net); return; case IVL_LPM_SUBSTITUTE: draw_lpm_substitute(net); return; case IVL_LPM_UFUNC: draw_lpm_ufunc(net); return; default: fprintf(stderr, "XXXX LPM not supported: %s.%s\n", ivl_scope_name(ivl_lpm_scope(net)), ivl_lpm_basename(net)); } } static const char *vvp_port_info_type_str(ivl_signal_port_t ptype) { switch( ptype ) { case IVL_SIP_INPUT : return "/INPUT"; case IVL_SIP_OUTPUT : return "/OUTPUT"; case IVL_SIP_INOUT : return "/INOUT"; case IVL_SIP_NONE : return "/NODIR"; default : abort(); // NO SUPPORT FOR ANYTHING ELSE YET... } } int draw_scope(ivl_scope_t net, ivl_scope_t parent) { unsigned idx; const char *type; const char*prefix = ivl_scope_is_auto(net) ? "auto" : ""; switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: type = "module"; break; case IVL_SCT_FUNCTION: type = "function"; break; case IVL_SCT_TASK: type = "task"; break; case IVL_SCT_BEGIN: type = "begin"; break; case IVL_SCT_FORK: type = "fork"; break; case IVL_SCT_GENERATE: type = "generate"; break; case IVL_SCT_PACKAGE: type = "package"; break; case IVL_SCT_CLASS: type = "class"; break; default: type = "?"; assert(0); } fprintf(vvp_out, "S_%p .scope %s%s, \"%s\" \"%s\" %u %u", net, prefix, type, vvp_mangle_name(ivl_scope_basename(net)), vvp_mangle_name(ivl_scope_tname(net)), ivl_file_table_index(ivl_scope_file(net)), ivl_scope_lineno(net)); if (parent) { fprintf(vvp_out, ", %u %u %u, S_%p;\n", ivl_file_table_index(ivl_scope_def_file(net)), ivl_scope_def_lineno(net), ivl_scope_is_cell(net), parent); } else { fprintf(vvp_out, ";\n"); } fprintf(vvp_out, " .timescale %d %d;\n", ivl_scope_time_units(net), ivl_scope_time_precision(net)); if( ivl_scope_type(net) == IVL_SCT_MODULE ) { // Port data for VPI: needed for vpiPorts property of vpiModule for( idx = 0; idx < ivl_scope_mod_module_ports(net); ++idx ) { const char *name = ivl_scope_mod_module_port_name(net,idx); ivl_signal_port_t ptype = ivl_scope_mod_module_port_type(net,idx); unsigned width = ivl_scope_mod_module_port_width(net,idx); if( name == 0 ) name = ""; fprintf( vvp_out, " .port_info %u %s %u \"%s\"\n", idx, vvp_port_info_type_str(ptype), width, vvp_mangle_name(name) ); } } for (idx = 0 ; idx < ivl_scope_params(net) ; idx += 1) { ivl_parameter_t par = ivl_scope_param(net, idx); ivl_expr_t pex = ivl_parameter_expr(par); switch (ivl_expr_type(pex)) { case IVL_EX_STRING: fprintf(vvp_out, "P_%p .param/str \"%s\" %d %u %u, \"%s\";\n", par, vvp_mangle_name(ivl_parameter_basename(par)), ivl_parameter_local(par), ivl_file_table_index(ivl_parameter_file(par)), ivl_parameter_lineno(par), ivl_expr_string(pex)); break; case IVL_EX_NUMBER: fprintf(vvp_out, "P_%p .param/l \"%s\" %d %u %u, %sC4<", par, vvp_mangle_name(ivl_parameter_basename(par)), ivl_parameter_local(par), ivl_file_table_index(ivl_parameter_file(par)), ivl_parameter_lineno(par), ivl_expr_signed(pex)? "+":""); { const char*bits = ivl_expr_bits(pex); unsigned nbits = ivl_expr_width(pex); unsigned bb; for (bb = 0 ; bb < nbits; bb += 1) fprintf(vvp_out, "%c", bits[nbits-bb-1]); } fprintf(vvp_out, ">;\n"); break; case IVL_EX_REALNUM: { char *res = draw_Cr_to_string(ivl_expr_dvalue(pex)); fprintf(vvp_out, "P_%p .param/real \"%s\" %d %u %u, %s; " "value=%#g\n", par, vvp_mangle_name(ivl_parameter_basename(par)), ivl_parameter_local(par), ivl_file_table_index(ivl_parameter_file(par)), ivl_parameter_lineno(par), res, ivl_expr_dvalue(pex)); free(res); } break; default: fprintf(vvp_out, "; parameter type %d unsupported\n", ivl_expr_type(pex)); break; } } for (idx = 0 ; idx < ivl_scope_classes(net) ; idx += 1) { ivl_type_t class_type = ivl_scope_class(net,idx); draw_class_in_scope(class_type); } /* Scan the scope for enumeration types, and write out enumeration typespecs. */ for (idx = 0 ; idx < ivl_scope_enumerates(net) ; idx += 1) { ivl_enumtype_t enumtype = ivl_scope_enumerate(net, idx); draw_enumeration_in_scope(enumtype); } /* Scan the scope for logic devices. For each device, draw out a functor that connects pin 0 to the output, and the remaining pins to inputs. */ for (idx = 0 ; idx < ivl_scope_logs(net) ; idx += 1) { ivl_net_logic_t lptr = ivl_scope_log(net, idx); draw_logic_in_scope(lptr); } /* Scan the signals (reg and net) and draw the appropriate statements to make the signal function. */ for (idx = 0 ; idx < ivl_scope_sigs(net) ; idx += 1) { ivl_signal_t sig = ivl_scope_sig(net, idx); switch (ivl_signal_type(sig)) { case IVL_SIT_REG: draw_reg_in_scope(sig); break; default: draw_net_in_scope(sig); break; } } for (idx = 0 ; idx < ivl_scope_events(net) ; idx += 1) { ivl_event_t event = ivl_scope_event(net, idx); draw_event_in_scope(event); } for (idx = 0 ; idx < ivl_scope_lpms(net) ; idx += 1) { ivl_lpm_t lpm = ivl_scope_lpm(net, idx); draw_lpm_in_scope(lpm); } for (idx = 0 ; idx < ivl_scope_switches(net) ; idx += 1) { ivl_switch_t sw = ivl_scope_switch(net, idx); draw_switch_in_scope(sw); } if (ivl_scope_type(net) == IVL_SCT_TASK) draw_task_definition(net); if (ivl_scope_type(net) == IVL_SCT_FUNCTION) vvp_errors += draw_func_definition(net); ivl_scope_children(net, (ivl_scope_f*) draw_scope, net); return 0; } iverilog-10_1/util.h000066400000000000000000000027471265551621300145020ustar00rootroot00000000000000#ifndef IVL_util_H #define IVL_util_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "StringHeap.h" # include "verinum.h" class PExpr; class Design; class NetScope; /* * This file attempts to locate a module in a file. It operates by * looking for a plausible Verilog file to hold the module, and * invoking the parser to bring in that file's contents. */ extern bool load_module(const char*type); struct attrib_list_t { perm_string key; verinum val; }; extern attrib_list_t* evaluate_attributes(const map&att, unsigned&natt, Design*des, NetScope*scope); #endif /* IVL_util_H */ iverilog-10_1/va_math.txt000066400000000000000000000073401265551621300155260ustar00rootroot00000000000000 The following is from the README.va_math that was included with the initial contribution of the va_math module. I've removed the parts that are obviously not applicable, i.e. how to compile the library, to this bundled version of the library. -------- License. -------- Verilog-A math library built for Icarus Verilog http://www.icarus.com/eda/verilog/ Copyright (C) 2007-2010 Cary R. (cygcary@yahoo.com) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ------------------------------------------ Standard Verilog-A Mathematical Functions. ------------------------------------------ The va_math VPI module implements all the standard math functions provided by Verilog-A as Verilog-D system functions. The names are the same except like all Verilog-D system functions the name must be prefixed with a '$'. For reference the functions are: $ln(x) -- Natural logarithm $log10(x) -- Decimal logarithm $exp(x) -- Exponential $sqrt(x) -- Square root $min(x,y) -- Minimum $max(x,y) -- Maximum $abs(x) -- Absolute value $floor(x) -- Floor $ceil(x) -- Ceiling $pow(x,y) -- Power (x**y) $sin(x) -- Sine $cos(x) -- Cosine $tan(x) -- Tangent $asin(x) -- Arc-sine $acos(x) -- Arc-cosine $atan(x) -- Arc-tangent $atan2(y,x) -- Arc-tangent of y/x $hypot(x,y) -- Hypotenuse (sqrt(x**2 + y**2)) $sinh(x) -- Hyperbolic sine $cosh(x) -- Hyperbolic cosine $tanh(x) -- Hyperbolic tangent $asinh(x) -- Arc-hyperbolic sine $acosh(x) -- Arc-hyperbolic cosine $atanh(x) -- Arc-hyperbolic tangent The only limit placed on the x and y arguments by the library is that they must be numbers (not constant strings). The underlying C library controls any other limits placed on the arguments. Most libraries return +-Inf or NaN for results that cannot be represented with real numbers. All functions return a real result. ------------------------------------------ Standard Verilog-A Mathematical Constants. ------------------------------------------ The Verilog-A mathematical constants can be accessed by including the "constants.vams" header file. It is located in the standard include directory. Recent version of Icarus Verilog (0.9.devel) automatically add this directory to the end of the list used to find include files. For reference the mathematical constants are: `M_PI -- Pi `M_TWO_PI -- 2*Pi `M_PI_2 -- Pi/2 `M_PI_4 -- Pi/4 `M_1_PI -- 1/Pi `M_2_PI -- 2/Pi `M_2_SQRTPI -- 2/sqrt(Pi) `M_E -- e `M_LOG2E -- log base 2 of e `M_LOG10E -- log base 10 of e `M_LN2 -- log base e of 2 `M_LN10 -- log base e of 10 `M_SQRT2 -- sqrt(2) `M_SQRT1_2 -- 1/sqrt(2) ------------------ Using the Library. ------------------ Just add "-m va_math" to your iverilog command line/command file and `include the "constants.vams" file as needed. ------ Thanks ------ I would like to thank Larry Doolittle for his suggestions and Stephen Williams for developing Icarus Verilog. -------- The End. -------- iverilog-10_1/verilog.spec000066400000000000000000000116301265551621300156660ustar00rootroot00000000000000#norootforbuild # %define rev_date 20150815 # Normally, the suff-ix is %nil, meaning the suffix is to not be used. # But if the builder wants to make a suffixed package, he may set this # to a value (i.e. -test) to cause suffixes to be put in all the right # places. %define suff %nil # # Summary: Icarus Verilog Name: verilog%{suff} Version: 10.1 Release: 0 License: GPL Group: Productivity/Scientific/Electronics Source: verilog%{suff}-%{version}.tar.gz URL: http://www.icarus.com/eda/verilog/index.html Packager: Stephen Williams BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: gcc-c++, zlib-devel, bison, flex, gperf, readline-devel # This provides tag allows me to use a more specific name for things # that actually depend on me, Icarus Verilog. Provides: iverilog %description Icarus Verilog is a Verilog compiler that generates a variety of engineering formats, including simulation. It strives to be true to the IEEE-1364 standard. %prep %setup -n verilog-%{version} %build if test X%{suff} != X then %{configure} --enable-suffix=%{suff} else %{configure} fi make CXXFLAGS=-O %install %if 0%{?suse_version} %{makeinstall} %else make DESTDIR=$RPM_BUILD_ROOT install %endif %clean rm -rf $RPM_BUILD_ROOT %files %attr(-,root,root) %doc COPYING README.txt BUGS.txt QUICK_START.txt ieee1364-notes.txt mingw.txt swift.txt netlist.txt t-dll.txt vpi.txt cadpli/cadpli.txt %attr(-,root,root) %doc examples/* %attr(-,root,root) %{_mandir}/man1/iverilog%{suff}.1.gz %attr(-,root,root) %{_mandir}/man1/iverilog-vpi%{suff}.1.gz %attr(-,root,root) %{_mandir}/man1/vvp%{suff}.1.gz %attr(-,root,root) %{_bindir}/iverilog%{suff} %attr(-,root,root) %{_bindir}/iverilog-vpi%{suff} %attr(-,root,root) %{_bindir}/vvp%{suff} %attr(-,root,root) %{_libdir}/ivl%{suff}/ivl %attr(-,root,root) %{_libdir}/ivl%{suff}/ivlpp %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdlpp %attr(-,root,root) %{_libdir}/ivl%{suff}/blif.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/blif.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/blif-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/null.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/null.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/null-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/sizer.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/sizer.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/sizer-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/stub.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/stub.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/stub-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/vvp.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/vvp.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/vvp-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/vlog95.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/vlog95.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/vlog95-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/pcb.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/pcb.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/pcb-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/system.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/system.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/va_math.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/va_math.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/v2005_math.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/v2005_math.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/v2009.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/v2009.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/vpi_debug.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/cadpli.vpl %attr(-,root,root) %{_libdir}/libvpi%{suff}.a %attr(-,root,root) %{_libdir}/libveriuser%{suff}.a %attr(-,root,root) %{_libdir}/ivl%{suff}/include/constants.vams %attr(-,root,root) %{_libdir}/ivl%{suff}/include/disciplines.vams %attr(-,root,root) /usr/include/iverilog%{suff}/ivl_target.h %attr(-,root,root) /usr/include/iverilog%{suff}/vpi_user.h %attr(-,root,root) /usr/include/iverilog%{suff}/sv_vpi_user.h %attr(-,root,root) /usr/include/iverilog%{suff}/acc_user.h %attr(-,root,root) /usr/include/iverilog%{suff}/veriuser.h %attr(-,root,root) /usr/include/iverilog%{suff}/_pli_types.h %changelog -n verilog * Thu Jul 25 2013 - steve@icarus.com - Add blif code generator files. * Wed Feb 25 2009 - steve@icarus.com - Handle a package suffix if desired. * Tue Nov 25 2008 - steve@icarus.com - Move header files frim /verilog/ to /iverilog/ * Tue Nov 18 2008 - steve@icarus.com - New snapshot 20080905 * Fri Sep 03 2008 - steve@icarus.com - New snapshot 20080905 * Sat Aug 30 2008 - steve@icarus.com - Add vhdl target files - Add V/AMS header files. * Fri Jan 25 2008 - steve@icarus.com - Removed vvp32 support for x86_64 build. * Sun Feb 28 2007 - steve@icarus.com - Added formatting suitable for openSUSE packaging. iverilog-10_1/verinum.cc000066400000000000000000001241501265551621300153410ustar00rootroot00000000000000/* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "verinum.h" # include # include # include // Needed to get pow for as_double(). # include // Needed to get snprintf for as_string(). # include #if !defined(HAVE_LROUND) /* * If the system doesn't provide the lround function, then we provide * it ourselves here. It is simply the nearest integer, rounded away * from zero. */ extern "C" long int lround(double x) { if (x >= 0.0) return (long)floor(x+0.5); else return (long)ceil(x-0.5); } #endif static verinum::V add_with_carry(verinum::V l, verinum::V r, verinum::V&c); verinum::verinum() : bits_(0), nbits_(0), has_len_(false), has_sign_(false), is_single_(false), string_flag_(false) { } verinum::verinum(const V*bits, unsigned nbits, bool has_len__) : has_len_(has_len__), has_sign_(false), is_single_(false), string_flag_(false) { nbits_ = nbits; bits_ = new V [nbits]; for (unsigned idx = 0 ; idx < nbits ; idx += 1) { bits_[idx] = bits[idx]; } } static string process_verilog_string_quotes(const string&str) { string res; int idx = 0; int str_len = str.length(); while (idx < str_len) { if (str[idx] == '\\') { idx += 1; assert(idx < str_len); switch (str[idx]) { case 'n': res = res + '\n'; idx += 1; break; case 't': res = res + '\t'; idx += 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { char byte_val = 0; int odx = 0; while (odx < 3 && idx+odx < str_len && str[idx+odx] >= '0' && str[idx+odx] <= '7') { byte_val = 8*byte_val + str[idx+odx]-'0'; odx += 1; } idx += odx; res = res + byte_val; break; } default: res = res + str[idx]; idx += 1; break; } } else { res = res + str[idx]; idx += 1; } } return res; } verinum::verinum(const string&s) : has_len_(true), has_sign_(false), is_single_(false), string_flag_(true) { string str = process_verilog_string_quotes(s); nbits_ = str.length() * 8; // Special case: The string "" is 8 bits of 0. if (nbits_ == 0) { nbits_ = 8; bits_ = new V [nbits_]; bits_[0] = V0; bits_[1] = V0; bits_[2] = V0; bits_[3] = V0; bits_[4] = V0; bits_[5] = V0; bits_[6] = V0; bits_[7] = V0; return; } bits_ = new V [nbits_]; unsigned idx, cp; V*bp = bits_+nbits_; for (idx = nbits_, cp = 0 ; idx > 0 ; idx -= 8, cp += 1) { char ch = str[cp]; *(--bp) = (ch&0x80) ? V1 : V0; *(--bp) = (ch&0x40) ? V1 : V0; *(--bp) = (ch&0x20) ? V1 : V0; *(--bp) = (ch&0x10) ? V1 : V0; *(--bp) = (ch&0x08) ? V1 : V0; *(--bp) = (ch&0x04) ? V1 : V0; *(--bp) = (ch&0x02) ? V1 : V0; *(--bp) = (ch&0x01) ? V1 : V0; } } verinum::verinum(verinum::V val, unsigned n, bool h) : has_len_(h), has_sign_(false), is_single_(false), string_flag_(false) { nbits_ = n; bits_ = new V[nbits_]; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) bits_[idx] = val; } verinum::verinum(uint64_t val, unsigned n) : has_len_(true), has_sign_(false), is_single_(false), string_flag_(false) { nbits_ = n; bits_ = new V[nbits_]; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) { bits_[idx] = (val&1) ? V1 : V0; val >>= (uint64_t)1; } } /* The second argument is not used! It is there to make this * constructor unique. */ verinum::verinum(double val, bool) : has_len_(false), has_sign_(true), is_single_(false), string_flag_(false) { bool is_neg = false; double fraction; int exponent; const unsigned BITS_IN_LONG = 8*sizeof(long); /* We return `bx for a NaN or +/- infinity. */ if (val != val || (val && (val == 0.5*val))) { nbits_ = 1; bits_ = new V[nbits_]; bits_[0] = Vx; return; } /* Convert to a positive result. */ if (val < 0.0) { is_neg = true; val = -val; } /* Round to the nearest integer now, as this may increase the number of bits we need to allocate. */ val = round(val); /* Get the exponent and fractional part of the number. */ fraction = frexp(val, &exponent); nbits_ = exponent+1; bits_ = new V[nbits_]; /* If the value is small enough just use lround(). */ if (nbits_ <= BITS_IN_LONG) { long sval = lround(val); if (is_neg) sval = -sval; for (unsigned idx = 0; idx < nbits_; idx += 1) { bits_[idx] = (sval&1) ? V1 : V0; sval >>= 1; } /* Trim the result. */ signed_trim(); return; } unsigned nwords = (exponent-1)/BITS_IN_LONG; fraction = ldexp(fraction, (exponent-1) % BITS_IN_LONG + 1); if (nwords == 0) { unsigned long bits = (unsigned long) fraction; fraction = fraction - (double) bits; for (unsigned idx = 0; idx < nbits_; idx += 1) { bits_[idx] = (bits&1) ? V1 : V0; bits >>= 1; } } else { for (int wd = nwords; wd >= 0; wd -= 1) { unsigned long bits = (unsigned long) fraction; fraction = fraction - (double) bits; unsigned max_idx = (wd+1)*BITS_IN_LONG; if (max_idx > nbits_) max_idx = nbits_; for (unsigned idx = wd*BITS_IN_LONG; idx < max_idx; idx += 1) { bits_[idx] = (bits&1) ? V1 : V0; bits >>= 1; } fraction = ldexp(fraction, BITS_IN_LONG); } } /* Convert a negative number if needed. */ if (is_neg) { *this = -(*this); } /* Trim the result. */ signed_trim(); } /* This is used by the double constructor above. It is needed to remove * extra sign bits that can occur when calculating a negative value. */ void verinum::signed_trim() { /* Do we have any extra digits? */ unsigned tlen = nbits_-1; verinum::V sign = bits_[tlen]; while ((tlen > 0) && (bits_[tlen] == sign)) tlen -= 1; /* tlen now points to the first digit that is not the sign. * or bit 0. Set the length to include this bit and one proper * sign bit if needed. */ if (bits_[tlen] != sign) tlen += 1; tlen += 1; /* Trim the bits if needed. */ if (tlen < nbits_) { V* tbits = new V[tlen]; for (unsigned idx = 0; idx < tlen; idx += 1) tbits[idx] = bits_[idx]; delete[] bits_; bits_ = tbits; nbits_ = tlen; } } verinum::verinum(const verinum&that) { string_flag_ = that.string_flag_; nbits_ = that.nbits_; bits_ = new V[nbits_]; has_len_ = that.has_len_; has_sign_ = that.has_sign_; is_single_ = that.is_single_; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) bits_[idx] = that.bits_[idx]; } verinum::verinum(const verinum&that, unsigned nbits) { string_flag_ = that.string_flag_ && (that.nbits_ == nbits); nbits_ = nbits; bits_ = new V[nbits_]; has_len_ = true; has_sign_ = that.has_sign_; is_single_ = false; unsigned copy = nbits; if (copy > that.nbits_) copy = that.nbits_; for (unsigned idx = 0 ; idx < copy ; idx += 1) bits_[idx] = that.bits_[idx]; if (copy < nbits_) { if (has_sign_ || that.is_single_) { for (unsigned idx = copy ; idx < nbits_ ; idx += 1) bits_[idx] = bits_[idx-1]; } else { for (unsigned idx = copy ; idx < nbits_ ; idx += 1) bits_[idx] = verinum::V0; } } } verinum::verinum(int64_t that) : has_len_(false), has_sign_(true), is_single_(false), string_flag_(false) { int64_t tmp; if (that < 0) tmp = (that+1)/2; else tmp = that/2; nbits_ = 1; while (tmp != 0) { nbits_ += 1; tmp /= 2; } nbits_ += 1; bits_ = new V[nbits_]; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) { bits_[idx] = (that & 1)? V1 : V0; that >>= 1; } } verinum::~verinum() { delete[]bits_; } verinum& verinum::operator= (const verinum&that) { if (this == &that) return *this; if (nbits_ != that.nbits_) { delete[]bits_; nbits_ = that.nbits_; bits_ = new V[that.nbits_]; } for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) bits_[idx] = that.bits_[idx]; has_len_ = that.has_len_; has_sign_ = that.has_sign_; is_single_ = that.is_single_; string_flag_ = that.string_flag_; return *this; } verinum::V verinum::get(unsigned idx) const { assert(idx < nbits_); return bits_[idx]; } verinum::V verinum::set(unsigned idx, verinum::V val) { assert(idx < nbits_); return bits_[idx] = val; } void verinum::set(unsigned off, const verinum&val) { assert(off + val.len() <= nbits_); for (unsigned idx = 0 ; idx < val.len() ; idx += 1) bits_[off+idx] = val[idx]; } unsigned verinum::as_unsigned() const { if (nbits_ == 0) return 0; if (!is_defined()) return 0; unsigned val = 0; unsigned mask = 1; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1) if (bits_[idx] == V1) { if (mask == 0) return ~mask; val |= mask; } return val; } unsigned long verinum::as_ulong() const { if (nbits_ == 0) return 0; if (!is_defined()) return 0; unsigned long val = 0; unsigned long mask = 1; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1) if (bits_[idx] == V1) { if (mask == 0) return ~mask; val |= mask; } return val; } uint64_t verinum::as_ulong64() const { if (nbits_ == 0) return 0; if (!is_defined()) return 0; uint64_t val = 0; uint64_t mask = 1; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1) if (bits_[idx] == V1) { if (mask == 0) return ~mask; val |= mask; } return val; } /* * This function returns the native long integer that represents the * value of this object. It accounts for sign extension if the value * is signed. * * If the value is undefined, return 0. * * This function presumes that the native format is 2s complement * (pretty safe these days) and masks/sets bits accordingly. If the * value is too large for the native form, it truncates the high bits. */ signed long verinum::as_long() const { #define IVLLBITS (8 * sizeof(long) - 1) if (nbits_ == 0) return 0; if (!is_defined()) return 0; signed long val = 0; unsigned diag_top = 0; unsigned top = nbits_; if (top > IVLLBITS) { diag_top = top; top = IVLLBITS; } int lost_bits=0; if (has_sign_ && (bits_[nbits_-1] == V1)) { val = -1; signed long mask = ~1L; for (unsigned idx = 0 ; idx < top ; idx += 1) { if (bits_[idx] == V0) val &= mask; mask = (mask << 1) | 1L; } if (diag_top) { for (unsigned idx = top; idx < diag_top; idx += 1) { if (bits_[idx] == V0) lost_bits=1; } } } else { signed long mask = 1; for (unsigned idx = 0 ; idx < top ; idx += 1, mask <<= 1) { if (bits_[idx] == V1) val |= mask; } if (diag_top) { for (unsigned idx = top; idx < diag_top; idx += 1) { if (bits_[idx] == V1) lost_bits=1; } } } if (lost_bits) cerr << "warning: verinum::as_long() truncated " << diag_top << " bits to " << IVLLBITS << ", returns " << val << endl; return val; #undef IVLLBITS } double verinum::as_double() const { if (nbits_ == 0) return 0.0; double val = 0.0; /* Do we have/want a signed value? */ if (has_sign_ && bits_[nbits_-1] == V1) { V carry = V1; for (unsigned idx = 0; idx < nbits_; idx += 1) { V sum = add_with_carry(~bits_[idx], V0, carry); if (sum == V1) val += pow(2.0, (double)idx); } val *= -1.0; } else { for (unsigned idx = 0; idx < nbits_; idx += 1) { if (bits_[idx] == V1) val += pow(2.0, (double)idx); } } return val; } string verinum::as_string() const { assert( nbits_%8 == 0 ); if (nbits_ == 0) return ""; string res; for (unsigned idx = nbits_ ; idx > 0 ; idx -= 8) { char char_val = 0; V*bp = bits_+idx; if (*(--bp) == V1) char_val |= 0x80; if (*(--bp) == V1) char_val |= 0x40; if (*(--bp) == V1) char_val |= 0x20; if (*(--bp) == V1) char_val |= 0x10; if (*(--bp) == V1) char_val |= 0x08; if (*(--bp) == V1) char_val |= 0x04; if (*(--bp) == V1) char_val |= 0x02; if (*(--bp) == V1) char_val |= 0x01; if (char_val == '"' || char_val == '\\') { char tmp[5]; snprintf(tmp, sizeof tmp, "\\%03o", char_val); res = res + tmp; } else if (isprint(char_val)) { res = res + char_val; } else { char tmp[5]; snprintf(tmp, sizeof tmp, "\\%03o", (unsigned char)char_val); res = res + tmp; } } return res; } bool verinum::is_before(const verinum&that) const { if (that.nbits_ > nbits_) return true; if (that.nbits_ < nbits_) return false; for (unsigned idx = nbits_ ; idx > 0 ; idx -= 1) { if (bits_[idx-1] < that.bits_[idx-1]) return true; if (bits_[idx-1] > that.bits_[idx-1]) return false; } return false; } bool verinum::is_defined() const { for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) { if (bits_[idx] == Vx) return false; if (bits_[idx] == Vz) return false; } return true; } bool verinum::is_zero() const { for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) if (bits_[idx] != V0) return false; return true; } bool verinum::is_negative() const { return (bits_[nbits_-1] == V1) && has_sign(); } unsigned verinum::significant_bits() const { unsigned sbits = nbits_; if (has_sign_) { V sign_bit = bits_[sbits-1]; while ((sbits > 1) && (bits_[sbits-2] == sign_bit)) sbits -= 1; } else { while ((sbits > 1) && (bits_[sbits-1] == verinum::V0)) sbits -= 1; } return sbits; } void verinum::cast_to_int2() { for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) { if (bits_[idx] == Vx || bits_[idx] == Vz) bits_[idx] = V0; } } verinum pad_to_width(const verinum&that, unsigned width) { if (that.len() >= width) return that; if (that.len() == 0) { verinum val (verinum::V0, width, that.has_len()); val.has_sign(that.has_sign()); return val; } verinum::V pad = that[that.len()-1]; if (pad==verinum::V1 && !that.has_sign() && !that.is_single()) pad = verinum::V0; if (that.has_len() && !that.has_sign() && !that.is_single()) { if (pad==verinum::Vx) pad = verinum::V0; if (pad==verinum::Vz) pad = verinum::V0; } verinum val(pad, width, that.has_len()); for (unsigned idx = 0 ; idx < that.len() ; idx += 1) val.set(idx, that[idx]); val.has_sign(that.has_sign()); if (that.is_string() && (width % 8) == 0) { val = verinum(val.as_string()); } return val; } verinum cast_to_width(const verinum&that, unsigned width) { if (that.has_len() && (that.len() == width)) return that; if (that.len() >= width) return verinum(that, width); if (that.len() == 0) { verinum val (verinum::V0, width, true); val.has_sign(that.has_sign()); return val; } verinum::V pad = that[that.len()-1]; if (pad==verinum::V1 && !that.has_sign() && !that.is_single()) pad = verinum::V0; if (that.has_len() && !that.has_sign() && !that.is_single()) { if (pad==verinum::Vx) pad = verinum::V0; if (pad==verinum::Vz) pad = verinum::V0; } verinum val(pad, width, true); for (unsigned idx = 0 ; idx < that.len() ; idx += 1) val.set(idx, that[idx]); val.has_sign(that.has_sign()); return val; } /* * This function returns a version of the verinum that has only as * many bits as are needed to accurately represent the value. It takes * into account the signedness of the value. * * If the input value has a definite length, then that value is just * returned as is. */ verinum trim_vnum(const verinum&that) { unsigned tlen; if (that.has_len()) return that; if (that.len() < 2) return that; if (that.has_sign()) { unsigned top = that.len()-1; verinum::V sign = that.get(top); while ((top > 0) && (that.get(top) == sign)) top -= 1; /* top points to the first digit that is not the sign. Set the length to include this and one proper sign bit. */ if (that.get(top) != sign) top += 1; tlen = top+1; } else { /* If the result is unsigned and has an indefinite length, then trim off all but one leading zero. */ unsigned top = that.len()-1; while ((top > 0) && (that.get(top) == verinum::V0)) top -= 1; /* Now top is the index of the highest non-zero bit. If that turns out to the highest bit in the vector, then there is no trimming possible. */ if (top+1 == that.len()) return that; /* Make tlen wide enough to include the highest non-zero bit, plus one extra 0 bit. */ tlen = top+2; /* This can only happen when the verinum is all zeros, so make it a single bit wide. */ if (that.get(top) == verinum::V0) tlen -= 1; } verinum tmp (verinum::V0, tlen, false); tmp.has_sign(that.has_sign()); for (unsigned idx = 0 ; idx < tmp.len() ; idx += 1) tmp.set(idx, that.get(idx)); return tmp; } ostream& operator<< (ostream&o, verinum::V v) { switch (v) { case verinum::V0: o << "0"; break; case verinum::V1: o << "1"; break; case verinum::Vx: o << "x"; break; case verinum::Vz: o << "z"; break; } return o; } /* * This operator is used by various dumpers to write the Verilog * number in a Verilog format. */ ostream& operator<< (ostream&o, const verinum&v) { if (v.is_string()) { o << "\"" << v.as_string() << "\""; return o; } /* If the verinum number has a fixed length, dump all the bits literally. This is how we express the fixed length in the output. */ if (v.has_len()) { o << v.len(); } /* If the number is fully defined (no x or z) then print it out as a decimal number. */ unsigned dec_len = 8*sizeof(int); /* avoid 32/64 bit differences. */ if (! v.has_sign()) dec_len -= 1; /* an unsigned number. */ if (v.is_defined() && v.len() <= dec_len) { if (v.has_sign()) o << "'sd" << v.as_long(); else o << "'d" << v.as_ulong(); return o; } /* Oh, well. Print the minimum to get the value properly displayed. */ if (v.has_sign()) o << "'sb"; else o << "'b"; if (v.len() == 0) { o << "0"; return o; } verinum::V trim_left = v.get(v.len()-1); unsigned idx; if (v.has_sign()) { for (idx = v.len()-1; idx > 0; idx -= 1) if (trim_left != v.get(idx-1)) break; o << trim_left; } else { idx = v.len(); } while (idx > 0) { o << v.get(idx-1); idx -= 1; } return o; } verinum::V operator == (const verinum&left, const verinum&right) { verinum::V left_pad = verinum::V0; verinum::V right_pad = verinum::V0; if (left.has_sign() && right.has_sign()) { left_pad = left.get(left.len()-1); right_pad = right.get(right.len()-1); if (left_pad == verinum::V1 && right_pad == verinum::V0) return verinum::V0; if (left_pad == verinum::V0 && right_pad == verinum::V1) return verinum::V0; } unsigned max_len = left.len(); if (right.len() > max_len) max_len = right.len(); for (unsigned idx = 0 ; idx < max_len ; idx += 1) { verinum::V left_bit = idx < left.len() ? left[idx] : left_pad; verinum::V right_bit = idx < right.len()? right[idx] : right_pad; if (left_bit != right_bit) return verinum::V0; } return verinum::V1; } verinum::V operator <= (const verinum&left, const verinum&right) { verinum::V left_pad = verinum::V0; verinum::V right_pad = verinum::V0; bool signed_calc = left.has_sign() && right.has_sign(); if (signed_calc) { left_pad = left.get(left.len()-1); right_pad = right.get(right.len()-1); if (left_pad == verinum::V1 && right_pad == verinum::V0) return verinum::V1; if (left_pad == verinum::V0 && right_pad == verinum::V1) return verinum::V0; } unsigned idx; for (idx = left.len() ; idx > right.len() ; idx -= 1) { if (left[idx-1] != right_pad) { // A change of padding for a negative left argument // denotes the left value is less than the right. return (signed_calc && (left_pad == verinum::V1)) ? verinum::V1 : verinum::V0; } } for (idx = right.len() ; idx > left.len() ; idx -= 1) { if (right[idx-1] != left_pad) { // A change of padding for a negative right argument // denotes the left value is not less than the right. return (signed_calc && (right_pad == verinum::V1)) ? verinum::V0 : verinum::V1; } } idx = right.len(); if (left.len() < idx) idx = left.len(); while (idx > 0) { if (left[idx-1] == verinum::Vx) return verinum::Vx; if (left[idx-1] == verinum::Vz) return verinum::Vx; if (right[idx-1] == verinum::Vx) return verinum::Vx; if (right[idx-1] == verinum::Vz) return verinum::Vx; if (left[idx-1] > right[idx-1]) return verinum::V0; if (left[idx-1] < right[idx-1]) return verinum::V1; idx -= 1; } return verinum::V1; } verinum::V operator < (const verinum&left, const verinum&right) { verinum::V left_pad = verinum::V0; verinum::V right_pad = verinum::V0; bool signed_calc = left.has_sign() && right.has_sign(); if (signed_calc) { left_pad = left.get(left.len()-1); right_pad = right.get(right.len()-1); if (left_pad == verinum::V1 && right_pad == verinum::V0) return verinum::V1; if (left_pad == verinum::V0 && right_pad == verinum::V1) return verinum::V0; } unsigned idx; for (idx = left.len() ; idx > right.len() ; idx -= 1) { if (left[idx-1] != right_pad) { // A change of padding for a negative left argument // denotes the left value is less than the right. return (signed_calc && (left_pad == verinum::V1)) ? verinum::V1 : verinum::V0; } } for (idx = right.len() ; idx > left.len() ; idx -= 1) { if (right[idx-1] != left_pad) { // A change of padding for a negative right argument // denotes the left value is not less than the right. return (signed_calc && (right_pad == verinum::V1)) ? verinum::V0 : verinum::V1; } } while (idx > 0) { if (left[idx-1] == verinum::Vx) return verinum::Vx; if (left[idx-1] == verinum::Vz) return verinum::Vx; if (right[idx-1] == verinum::Vx) return verinum::Vx; if (right[idx-1] == verinum::Vz) return verinum::Vx; if (left[idx-1] > right[idx-1]) return verinum::V0; if (left[idx-1] < right[idx-1]) return verinum::V1; idx -= 1; } return verinum::V0; } static verinum::V add_with_carry(verinum::V l, verinum::V r, verinum::V&c) { unsigned sum = 0; switch (c) { case verinum::Vx: case verinum::Vz: c = verinum::Vx; return verinum::Vx; case verinum::V0: break; case verinum::V1: sum += 1; } switch (l) { case verinum::Vx: case verinum::Vz: c = verinum::Vx; return verinum::Vx; case verinum::V0: break; case verinum::V1: sum += 1; break; } switch (r) { case verinum::Vx: case verinum::Vz: c = verinum::Vx; return verinum::Vx; case verinum::V0: break; case verinum::V1: sum += 1; break; } if (sum & 2) c = verinum::V1; else c = verinum::V0; if (sum & 1) return verinum::V1; else return verinum::V0; } verinum operator ~ (const verinum&left) { verinum val = left; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) switch (val[idx]) { case verinum::V0: val.set(idx, verinum::V1); break; case verinum::V1: val.set(idx, verinum::V0); break; default: val.set(idx, verinum::Vx); break; } return val; } /* * Addition and subtraction works a bit at a time, from the least * significant up to the most significant. The result is signed only * if both of the operands are signed. If either operand is unsized, * the result is expanded as needed to prevent overflow. */ verinum operator + (const verinum&left, const verinum&right) { const bool has_len_flag = left.has_len() && right.has_len(); const bool signed_flag = left.has_sign() && right.has_sign(); unsigned min_len = min(left.len(), right.len()); unsigned max_len = max(left.len(), right.len()); // If either the left or right values are undefined, the // entire result is undefined. if (!left.is_defined() || !right.is_defined()) { unsigned len = has_len_flag ? max_len : 1; verinum result (verinum::Vx, len, has_len_flag); result.has_sign(signed_flag); return result; } verinum::V*val_bits = new verinum::V[max_len+1]; verinum::V carry = verinum::V0; for (unsigned idx = 0 ; idx < min_len ; idx += 1) val_bits[idx] = add_with_carry(left[idx], right[idx], carry); verinum::V rpad = sign_bit(right); verinum::V lpad = sign_bit(left); if (left.len() > right.len()) { for (unsigned idx = min_len ; idx < max_len ; idx += 1) val_bits[idx] = add_with_carry(left[idx], rpad, carry); } else { for (unsigned idx = min_len ; idx < max_len ; idx += 1) val_bits[idx] = add_with_carry(lpad, right[idx], carry); } unsigned len = max_len; if (!has_len_flag) { val_bits[max_len] = add_with_carry(lpad, rpad, carry); if (signed_flag) { if (val_bits[max_len] != val_bits[max_len-1]) len += 1; } else { if (val_bits[max_len] != verinum::V0) len += 1; } } verinum result (val_bits, len, has_len_flag); result.has_sign(signed_flag); delete[]val_bits; return result; } verinum operator - (const verinum&left, const verinum&right) { const bool has_len_flag = left.has_len() && right.has_len(); const bool signed_flag = left.has_sign() && right.has_sign(); unsigned min_len = min(left.len(), right.len()); unsigned max_len = max(left.len(), right.len()); // If either the left or right values are undefined, the // entire result is undefined. if (!left.is_defined() || !right.is_defined()) { unsigned len = has_len_flag ? max_len : 1; verinum result (verinum::Vx, len, has_len_flag); result.has_sign(signed_flag); return result; } verinum::V*val_bits = new verinum::V[max_len+1]; verinum::V carry = verinum::V1; for (unsigned idx = 0 ; idx < min_len ; idx += 1) val_bits[idx] = add_with_carry(left[idx], ~right[idx], carry); verinum::V rpad = sign_bit(right); verinum::V lpad = sign_bit(left); if (left.len() > right.len()) { for (unsigned idx = min_len ; idx < max_len ; idx += 1) val_bits[idx] = add_with_carry(left[idx], ~rpad, carry); } else { for (unsigned idx = min_len ; idx < max_len ; idx += 1) val_bits[idx] = add_with_carry(lpad, ~right[idx], carry); } unsigned len = max_len; if (signed_flag && !has_len_flag) { val_bits[max_len] = add_with_carry(lpad, ~rpad, carry); if (val_bits[max_len] != val_bits[max_len-1]) len += 1; } verinum result (val_bits, len, has_len_flag); result.has_sign(signed_flag); delete[]val_bits; return result; } verinum operator - (const verinum&right) { const bool has_len_flag = right.has_len(); const bool signed_flag = right.has_sign(); unsigned len = right.len(); // If either the left or right values are undefined, the // entire result is undefined. if (!right.is_defined()) { verinum result (verinum::Vx, has_len_flag ? len : 1, has_len_flag); result.has_sign(signed_flag); return result; } verinum::V*val_bits = new verinum::V[len+1]; verinum::V carry = verinum::V1; for (unsigned idx = 0 ; idx < len ; idx += 1) val_bits[idx] = add_with_carry(verinum::V0, ~right[idx], carry); if (signed_flag && !has_len_flag) { val_bits[len] = add_with_carry(verinum::V0, ~sign_bit(right), carry); if (val_bits[len] != val_bits[len-1]) len += 1; } verinum result (val_bits, len, has_len_flag); result.has_sign(signed_flag); delete[]val_bits; return result; } /* * This operator multiplies the left number by the right number. The * result is signed only if both of the operands are signed. If either * operand is unsized, the resulting number is as large as the sum of * the sizes of the operands. * * The algorithm used is successive shift and add operations, * implemented as the nested loops. */ verinum operator * (const verinum&left, const verinum&right) { const bool has_len_flag = left.has_len() && right.has_len(); const bool signed_flag = left.has_sign() && right.has_sign(); const unsigned l_len = left.len(); const unsigned r_len = right.len(); unsigned len = has_len_flag ? max(l_len, r_len) : l_len + r_len; // If either the left or right values are undefined, the // entire result is undefined. if (!left.is_defined() || !right.is_defined()) { verinum result (verinum::Vx, has_len_flag ? len : 1, has_len_flag); result.has_sign(signed_flag); return result; } verinum result(verinum::V0, len, has_len_flag); result.has_sign(signed_flag); verinum::V r_sign = sign_bit(right); for (unsigned rdx = 0 ; rdx < len ; rdx += 1) { verinum::V r_bit = rdx < r_len ? right.get(rdx) : r_sign; if (r_bit == verinum::V0) continue; verinum::V l_sign = sign_bit(left); verinum::V carry = verinum::V0; for (unsigned ldx = 0 ; ldx < (len - rdx) ; ldx += 1) { verinum::V l_bit = ldx < l_len ? left[ldx] : l_sign; result.set(ldx+rdx, add_with_carry(l_bit, result[rdx+ldx], carry)); } } return trim_vnum(result); } static verinum make_p_one(unsigned len, bool has_len, bool has_sign) { verinum tmp (verinum::V0, has_len ? len : 2, has_len); tmp.set(0, verinum::V1); tmp.has_sign(has_sign); return tmp; } static verinum make_m_one(unsigned len, bool has_len, bool has_sign) { verinum tmp (verinum::V1, has_len ? len : 1, has_len); tmp.has_sign(has_sign); return tmp; } static verinum recursive_pow(const verinum&left, verinum&right) { // If the exponent is zero, return a value of 1 if (right.is_zero()) { return make_p_one(left.len(), left.has_len(), left.has_sign()); } verinum result; if (right.get(0) == 1) { // The exponent is odd, so subtract 1 from it and recurse. // We know it's odd, so the subtraction is easy. right.set(0, verinum::V0); result = pow(left, right); result = left * result; } else { // The exponent is even, so divide it by 2 and recurse right = right >> 1; result = pow(left, right); result = result * result; } return result; } verinum pow(const verinum&left, const verinum&right) { verinum result; // We need positive and negative one in a few places. verinum p_one = make_p_one(left.len(), left.has_len(), left.has_sign()); verinum m_one = make_m_one(left.len(), left.has_len(), left.has_sign()); // If either the left or right values are undefined, the // entire result is undefined. if (!left.is_defined() || !right.is_defined()) { result = verinum(verinum::Vx, left.len(), left.has_len()); result.has_sign(left.has_sign()); // If the right value is zero we need to set the result to 1. } else if (right.is_zero()) { result = p_one; } else if (right.is_negative()) { // 0 ** is 'bx. if (left.is_zero()) { result = verinum(verinum::Vx, left.len(), left.has_len()); result.has_sign(left.has_sign()); // -1 ** is 1 or -1. Note that this must be done // before testing for +1 in case 'left' has a width of 1. } else if (left.has_sign() && left == m_one) { if (right.get(0) == verinum::V0) { result = p_one; } else { result = m_one; } // 1 ** is 1. } else if (left == p_one) { result = p_one; // Everything else is 0. } else { result = verinum(verinum::V0, left.len(), left.has_len()); result.has_sign(left.has_sign()); } } else { verinum exponent = right; result = recursive_pow(left, exponent); } return trim_vnum(result); } verinum operator << (const verinum&that, unsigned shift) { bool has_len_flag = that.has_len(); unsigned len = that.len(); if (!has_len_flag) len += shift; verinum result(verinum::V0, len, has_len_flag); result.has_sign(that.has_sign()); for (unsigned idx = shift ; idx < len ; idx += 1) result.set(idx, that.get(idx - shift)); return trim_vnum(result); } verinum operator >> (const verinum&that, unsigned shift) { bool has_len_flag = that.has_len(); unsigned len = that.len(); verinum::V sign_bit = that.has_sign() ? that.get(len-1) : verinum::V0; if (shift >= len) { if (!has_len_flag) len = 1; verinum result(sign_bit, len, has_len_flag); result.has_sign(that.has_sign()); return result; } if (!has_len_flag) len -= shift; verinum result(sign_bit, len, has_len_flag); result.has_sign(that.has_sign()); for (unsigned idx = shift ; idx < that.len() ; idx += 1) result.set(idx-shift, that.get(idx)); return trim_vnum(result); } static verinum unsigned_divide(verinum num, verinum den, bool signed_result) { // We need the following calculations to be lossless. The // result will be cast to the required width by the caller. num.has_len(false); den.has_len(false); unsigned nwid = num.len(); while (nwid > 0 && (num.get(nwid-1) == verinum::V0)) nwid -= 1; unsigned dwid = den.len(); while (dwid > 0 && (den.get(dwid-1) == verinum::V0)) dwid -= 1; if (dwid > nwid) return verinum(verinum::V0, 1); den = den << (nwid-dwid); unsigned idx = nwid - dwid + 1; verinum result (verinum::V0, signed_result ? idx + 1 : idx); if (signed_result) { result.set(idx, verinum::V0); result.has_sign(true); } while (idx > 0) { if (den <= num) { verinum dif = num - den; num = dif; result.set(idx-1, verinum::V1); } den = den >> 1; idx -= 1; } return result; } static verinum unsigned_modulus(verinum num, verinum den) { // We need the following calculations to be lossless. The // result will be cast to the required width by the caller. num.has_len(false); den.has_len(false); unsigned nwid = num.len(); while (nwid > 0 && (num.get(nwid-1) == verinum::V0)) nwid -= 1; unsigned dwid = den.len(); while (dwid > 0 && (den.get(dwid-1) == verinum::V0)) dwid -= 1; if (dwid > nwid) return num; den = den << (nwid-dwid); unsigned idx = nwid - dwid + 1; while (idx > 0) { if (den <= num) { verinum dif = num - den; num = dif; } den = den >> 1; idx -= 1; } return num; } /* * This operator divides the left number by the right number. The result * is signed only if both of the operands are signed. */ verinum operator / (const verinum&left, const verinum&right) { const bool has_len_flag = left.has_len() && right.has_len(); const bool signed_flag = left.has_sign() && right.has_sign(); unsigned use_len = left.len(); // If either the left or right values are undefined, or the // right value is zero, the entire result is undefined. if (!left.is_defined() || !right.is_defined() || right.is_zero()) { verinum result (verinum::Vx, use_len, has_len_flag); result.has_sign(signed_flag); return result; } verinum result(verinum::Vz, use_len, has_len_flag); /* do the operation differently, depending on whether the result is signed or not. */ if (signed_flag) { if (use_len <= (8*sizeof(long) - 1)) { long l = left.as_long(); long r = right.as_long(); long v = l / r; for (unsigned idx = 0 ; idx < use_len ; idx += 1) { result.set(idx, (v & 1)? verinum::V1 : verinum::V0); v >>= 1; } } else { verinum use_left, use_right; bool negative = false; if (left.is_negative()) { use_left = -left; negative = !negative; } else { use_left = left; } use_left.has_sign(false); if (right.is_negative()) { use_right = -right; negative = !negative; } else { use_right = right; } use_right.has_sign(false); result = unsigned_divide(use_left, use_right, true); if (negative) result = -result; } } else { if (use_len <= 8 * sizeof(unsigned long)) { /* Use native unsigned division to do the work. */ unsigned long l = left.as_ulong(); unsigned long r = right.as_ulong(); unsigned long v = l / r; for (unsigned idx = 0 ; idx < use_len ; idx += 1) { result.set(idx, (v & 1)? verinum::V1 : verinum::V0); v >>= 1; } } else { result = unsigned_divide(left, right, false); } } if (has_len_flag) result = cast_to_width(result, use_len); result.has_sign(signed_flag); return trim_vnum(result); } verinum operator % (const verinum&left, const verinum&right) { const bool has_len_flag = left.has_len() && right.has_len(); const bool signed_flag = left.has_sign() && right.has_sign(); unsigned use_len = left.len(); // If either the left or right values are undefined, or the // right value is zero, the entire result is undefined. if (!left.is_defined() || !right.is_defined() || right.is_zero()) { verinum result (verinum::Vx, use_len, has_len_flag); result.has_sign(signed_flag); return result; } verinum result(verinum::Vz, use_len, has_len_flag); if (signed_flag) { if (use_len <= 8*sizeof(long)) { /* Use native signed modulus to do the work. */ long l = left.as_long(); long r = right.as_long(); long v = l % r; for (unsigned idx = 0 ; idx < use_len ; idx += 1) { result.set(idx, (v & 1)? verinum::V1 : verinum::V0); v >>= 1; } } else { verinum use_left, use_right; bool negative = false; if (left.is_negative()) { use_left = -left; negative = true; } else { use_left = left; } use_left.has_sign(false); if (right.is_negative()) { use_right = -right; } else { use_right = right; } use_right.has_sign(false); result = unsigned_modulus(use_left, use_right); if (negative) result = -result; } } else { if (use_len <= 8*sizeof(unsigned long)) { /* Use native unsigned modulus to do the work. */ unsigned long l = left.as_ulong(); unsigned long r = right.as_ulong(); unsigned long v = l % r; for (unsigned idx = 0 ; idx < use_len ; idx += 1) { result.set(idx, (v & 1)? verinum::V1 : verinum::V0); v >>= 1; } } else { result = unsigned_modulus(left, right); } } if (has_len_flag) result = cast_to_width(result, use_len); result.has_sign(signed_flag); return trim_vnum(result); } verinum concat(const verinum&left, const verinum&right) { if (left.is_string() && right.is_string()) { std::string tmp = left.as_string() + right.as_string(); verinum res (tmp); return res; } verinum res (verinum::V0, left.len() + right.len()); for (unsigned idx = 0 ; idx < right.len() ; idx += 1) res.set(idx, right.get(idx)); for (unsigned idx = 0 ; idx < left.len() ; idx += 1) res.set(idx+right.len(), left.get(idx)); return res; } verinum::V operator ~ (verinum::V l) { switch (l) { case verinum::V0: return verinum::V1; case verinum::V1: return verinum::V0; default: return verinum::Vx; } } verinum::V operator | (verinum::V l, verinum::V r) { if (l == verinum::V1) return verinum::V1; if (r == verinum::V1) return verinum::V1; if (l != verinum::V0) return verinum::Vx; if (r != verinum::V0) return verinum::Vx; return verinum::V0; } verinum::V operator & (verinum::V l, verinum::V r) { if (l == verinum::V0) return verinum::V0; if (r == verinum::V0) return verinum::V0; if (l != verinum::V1) return verinum::Vx; if (r != verinum::V1) return verinum::Vx; return verinum::V1; } verinum::V operator ^ (verinum::V l, verinum::V r) { if (l == verinum::V0) return bit4_z2x(r); if (r == verinum::V0) return bit4_z2x(l); if ((l == verinum::V1) && (r == verinum::V1)) return verinum::V0; return verinum::Vx; } iverilog-10_1/verinum.h000066400000000000000000000161341265551621300152050ustar00rootroot00000000000000#ifndef IVL_verinum_H #define IVL_verinum_H /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "config.h" #ifdef HAVE_IOSFWD # include #else class ostream; #endif using namespace std; /* * Numbers in Verilog are multibit strings, where each bit has 4 * possible values: 0, 1, x or z. The verinum number is store in * little-endian format. This means that if the long value is 2b'10, * get(0) is 0 and get(1) is 1. */ class verinum { public: enum V { V0 = 0, V1, Vx, Vz }; verinum(); verinum(const string&str); verinum(const V*v, unsigned nbits, bool has_len =true); verinum(V, unsigned nbits =1, bool has_len =true); verinum(uint64_t val, unsigned bits); verinum(double val, bool); verinum(const verinum&); // Create a signed number, with an unspecified number of bits. explicit verinum(int64_t val); // Copy only the specified number of bits from the // source. Also mark this number as has_len. verinum(const verinum&, unsigned bits); ~verinum(); verinum& operator= (const verinum&); // Number of stored bits in this number. unsigned len() const { return nbits_; } // A number "has a length" if the length was specified fixed // in some way. bool has_len(bool flag) { has_len_ = flag; return has_len_; } bool has_len() const { return has_len_; } bool has_sign(bool flag) { has_sign_ = flag; return has_sign_; } bool has_sign() const { return has_sign_; } // A number "is single" if it comes from a SystemVerilog 'N bit vector bool is_single(bool flag) { is_single_ = flag; return is_single_; } bool is_single() const { return is_single_; } // A number is "defined" if there are no x or z bits in its value. bool is_defined() const; bool is_zero() const; bool is_negative() const; // A number is "a string" if its value came directly from // an ASCII description instead of a number value. bool is_string() const { return string_flag_; } // Comparison for use in sorting algorithms. bool is_before(const verinum&that) const; // Number of significant bits in this number. unsigned significant_bits() const; // Convert 4-state to 2-state void cast_to_int2(); // Individual bits can be accessed with the get and set // methods. V get(unsigned idx) const; V set(unsigned idx, V val); void set(unsigned idx, const verinum&val); V operator[] (unsigned idx) const { return get(idx); } // Return the value as a native unsigned integer. If the value is // larger than can be represented by the returned type, return // the maximum value of that type. If the value has any x or z // bits or has zero width, return the value 0. uint64_t as_ulong64() const; unsigned as_unsigned() const; unsigned long as_ulong() const; signed long as_long() const; double as_double() const; string as_string() const; private: void signed_trim(); private: V* bits_; unsigned nbits_; bool has_len_; bool has_sign_; bool is_single_; // These are some convenience flags that help us do a better // job of pretty-printing values. bool string_flag_; }; /* * This returns the sign bit of the verinum value. If the value is * unsigned, then return an implicit sign bit of 0. Otherwise, return * the high bit. */ inline verinum::V sign_bit(const verinum&val) { if (val.has_sign()) return val.get(val.len()-1); else return verinum::V0; } /* Return a verinum that has the same value as the input, but is at least as wide as the requested width. This may involve sign extension, if the value is signed. */ extern verinum pad_to_width(const verinum&, unsigned width); /* Return a verinum that has the same value as the input, but is exactly the requested width. This may involve sign extension, if the value is signed. The returned verinum will have fixed width. */ extern verinum cast_to_width(const verinum&, unsigned width); /* Return a verinum that is minimal. That is, it has only the length needed to accurately represent the contained value, signed or not. */ extern verinum trim_vnum(const verinum&); extern ostream& operator<< (ostream&, const verinum&); extern ostream& operator<< (ostream&, verinum::V); inline verinum::V bit4_z2x(verinum::V bit) { return bit<2? bit : verinum::Vx; /* Relies on V0 and V1 being <2 */} extern verinum::V operator ~ (verinum::V l); extern verinum::V operator | (verinum::V l, verinum::V r); extern verinum::V operator & (verinum::V l, verinum::V r); extern verinum::V operator ^ (verinum::V l, verinum::V r); extern verinum::V operator == (const verinum&left, const verinum&right); extern verinum::V operator <= (const verinum&left, const verinum&right); extern verinum::V operator < (const verinum&left, const verinum&right); inline verinum::V operator > (const verinum&left, const verinum&right) { return right < left; } inline verinum::V operator >= (const verinum&left, const verinum&right) { return right <= left; } inline verinum::V operator != (const verinum&left, const verinum&right) { return (left == right)? verinum::V0 : verinum::V1; } /* These are arithmetic operators. If any operand is unsized, they generally work to produce results that do not overflow. That means the result may expand or contract to hold the bits needed to hold the operation results accurately. It is up to the caller to truncate or pad if a specific width is expected. If all operands are sized, the normal Verilog rules for result size are used. */ extern verinum operator - (const verinum&right); extern verinum operator + (const verinum&left, const verinum&right); extern verinum operator - (const verinum&left, const verinum&right); extern verinum operator * (const verinum&left, const verinum&right); extern verinum operator / (const verinum&left, const verinum&right); extern verinum operator % (const verinum&left, const verinum&right); extern verinum pow(const verinum&left, const verinum&right); extern verinum operator<< (const verinum&left, unsigned shift); extern verinum operator>> (const verinum&left, unsigned shift); extern verinum concat(const verinum&left, const verinum&right); /* Bitwise not returns the ones complement. */ extern verinum operator ~ (const verinum&left); #endif /* IVL_verinum_H */ iverilog-10_1/verireal.cc000066400000000000000000000074721265551621300154740ustar00rootroot00000000000000/* * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" # include "compiler.h" # include "verireal.h" # include "verinum.h" # include # include # include # include # include # include verireal::verireal() { value_ = 0.0; } verireal::verireal(const char*txt) { char*tmp = new char[strlen(txt)+1]; char*cp = tmp; for (unsigned idx = 0 ; txt[idx] ; idx += 1) { if (txt[idx] == '_') continue; *cp++ = txt[idx]; } cp[0] = 0; value_ = strtod(tmp, 0); delete[]tmp; } verireal::verireal(long val) { value_ = (double)val; } verireal::verireal(double val) { value_ = val; } verireal::~verireal() { } long verireal::as_long(int shift) const { double out = value_ * pow(10.0,shift); double outf; if (out >= 0.0) { outf = floor(out); if (out >= (outf + 0.5)) outf += 1.0; } else { outf = ceil(out); if (out <= (outf - 0.5)) outf -= 1.0; } return (long) outf; } int64_t verireal::as_long64(int shift) const { double out = value_ * pow(10.0,shift); double outf; if (out >= 0.0) { outf = floor(out); if (out >= (outf + 0.5)) outf += 1.0; } else { outf = ceil(out); if (out <= (outf - 0.5)) outf -= 1.0; } return (int64_t) outf; } double verireal::as_double() const { return value_; } verireal operator+ (const verireal&l, const verireal&r) { verireal res; res.value_ = l.value_ + r.value_; return res; } verireal operator- (const verireal&l, const verireal&r) { verireal res; res.value_ = l.value_ - r.value_; return res; } verireal operator* (const verireal&l, const verireal&r) { verireal res; res.value_ = l.value_ * r.value_; return res; } verireal operator/ (const verireal&l, const verireal&r) { verireal res; res.value_ = l.value_ / r.value_; return res; } verireal operator/ (const verireal&l, const verinum&r) { verireal res; res.value_ = l.value_ / (double)r.as_long(); return res; } verireal operator% (const verireal&l, const verireal&r) { verireal res; // Modulus of a real value is not supported by the standard, // but we support it as an extension. Assert that we are in // the correct state before doing the operation. assert(gn_icarus_misc_flag); res.value_ = fmod(l.value_, r.value_); return res; } verireal operator% (const verireal&l, const verinum&r) { verireal res; // See above. assert(gn_icarus_misc_flag); res.value_ = fmod(l.value_, (double)r.as_long()); return res; } verireal pow (const verireal&l, const verireal&r) { verireal res; res.value_ = pow(l.value_, r.value_); return res; } verireal operator- (const verireal&l) { verireal res; res.value_ = - l.value_; return res; } ostream& operator<< (ostream&out, const verireal&v) { out << showpoint << v.value_; return out; } iverilog-10_1/verireal.h000066400000000000000000000057301265551621300153310ustar00rootroot00000000000000#ifndef IVL_verireal_H #define IVL_verireal_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "config.h" #ifdef HAVE_IOSFWD # include #else class ostream; #endif using namespace std; class verinum; /* * This class holds a floating point decimal number. The number is * stored as an integer mantissa and a power of 10. The mantissa is an * integer so that decimal numbers in the source (which are decimal) * can be stored exactly. */ class verireal { friend ostream& operator<< (ostream&, const verireal&); friend verireal operator+ (const verireal&, const verireal&); friend verireal operator- (const verireal&, const verireal&); friend verireal operator* (const verireal&, const verireal&); friend verireal operator/ (const verireal&, const verireal&); friend verireal operator/ (const verireal&, const verinum&); friend verireal operator% (const verireal&, const verireal&); friend verireal operator% (const verireal&, const verinum&); friend verireal pow(const verireal&, const verireal&); // Unary minus. friend verireal operator- (const verireal&); public: explicit verireal(); explicit verireal(const char*text); explicit verireal(long val); explicit verireal(double val); ~verireal(); /* Return the value of the floating point number as an integer, rounded as needed. The shift is the power of 10 to multiply the value before calculating the result. So for example if the value is 2.5 and shift == 1, the result is 25. */ long as_long(int shift =0) const; int64_t as_long64(int shift =0) const; double as_double() const; private: double value_; }; extern ostream& operator<< (ostream&, const verireal&); extern verireal operator* (const verireal&, const verireal&); extern verireal operator/ (const verireal&, const verireal&); extern verireal operator/ (const verireal&, const verinum&); extern verireal operator% (const verireal&, const verireal&); extern verireal operator% (const verireal&, const verinum&); extern verireal pow(const verireal&, const verireal&); extern verireal operator- (const verireal&); #endif /* IVL_verireal_H */ iverilog-10_1/veriuser.h000066400000000000000000000264671265551621300153760ustar00rootroot00000000000000#ifndef VERIUSER_H #define VERIUSER_H /* * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This header file contains the definitions and declarations needed * by an Icarus Verilog user using tf_ routines. * * NOTE 1: Icarus Verilog does not directly support tf_ routines. This * header file defines a tf_ compatibility later. The functions that * are implemented here are actually implemented using VPI routines. * * NOTE 2: The routines and definitions of the tf_ library were * clearly not designed to account for C++, or even ANSI-C. This * header file attempts to fix these problems in a source code * compatible way. In the end, though, it is not completely * possible. Instead, users should not use this or the acc_user.h * header files or functions in new applications, and instead use the * more modern vpi_user.h and VPI functions. * * This API is provided by Icarus Verilog only to support legacy software. */ #ifdef __cplusplus # define EXTERN_C_START extern "C" { # define EXTERN_C_END } #else # define EXTERN_C_START # define EXTERN_C_END #endif #ifndef __GNUC__ # undef __attribute__ # define __attribute__(x) #endif EXTERN_C_START # include "_pli_types.h" /* * defines for tf_message */ #define ERR_MESSAGE 1 #define ERR_WARNING 2 #define ERR_ERROR 3 #define ERR_INTERNAL 4 #define ERR_SYSTEM 5 /* * These are some defines for backwards compatibility. They should not * be used in new code. */ #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #ifndef __cplusplus # ifndef true # define true 1 # endif # ifndef false # define false 0 # endif # ifndef bool # define bool int # endif #endif /* * structure for veriusertfs array */ typedef struct t_tfcell { short type; /* usertask|userfunction|userrealfunction */ short data; /* data passed to user routine */ int (*checktf)(int user_data, int reason); int (*sizetf)(int user_data, int reason); int (*calltf)(int user_data, int reason); int (*misctf)(int user_data, int reason, int paramvc); char *tfname; /* name of the system task/function */ int forwref; /* usually set to 1 */ char *tfveritool; /* usually ignored */ char *tferrmessage; /* usually ignored */ char reserved[20]; /* reserved */ } s_tfcell, *p_tfcell; /* * Normal PLI1.0 modules export a veriusertfs array that contains all * the function definitions for use by the simulator. The user code is * expected to supply that array. The method of export varies amongst * Verilog simulators, but at least one vendor gets a pointer to the * veriusertfs array by calling a boot function that takes no * arguments and returns a pointer to the table. * * The Icarus Verilog bootstrap process is a little bit different, and * is described in the vpi_user.h header file. However, a fairly * simple adaptation to your application fixes it so that it * automatically boots with Icarus Verilog. * * The trick is to write a vlog_startup_routine that calls the * veriusertfs_register_table function. A simple example: * * static struct s_tfcell my_veriusertfs_table[] = { * [... table entries, null terminated ...] * }; * * // Cadence compatibility * struct s_tfcell* my_bootstrap(void) * { return my_veriusertfs_table; } * * // Icarus Verilog compatibility * static void veriusertfs_register(void) * { * veriusertfs_register_table(my_veriusertfs_table); * } * void (*vlog_startup_routines[])() = { &veriusertfs_register, 0 }; * * The veriusertfs_register function and vlog_startup_routines table * are specific to Icarus Verilog, and arrange for automatic loading * of the PLI1 application. The vvp program will treat this as any * other VPI module. * * By structuring the bootstrap process in this manner, it is * plausible to make source code compatibility with a variety of * Verilog simulators. * * NOTE: The cadpli module of Icarus Verilog also makes it possible to * be *binary* compatible with other simulators. Create the * my_bootstrap function and leave out the vlog_startup_routines, then * use the "-mcadpli" module to load it in compatibility mode. */ extern s_tfcell veriusertfs[]; extern void veriusertfs_register_table(p_tfcell vtable); #define usertask 1 #define userfunction 2 #define userrealfunction 3 /* callback reasons */ #define reason_checktf 1 #define reason_calltf 3 #define reason_paramvc 7 #define reason_synch 8 #define REASON_SYNCH reason_synch #define reason_finish 9 #define reason_reactivate 10 #define REASON_REACTIVATE reason_reactivate #define reason_rosynch 11 #define REASON_ROSYNCH reason_rosynch #define reason_endofcompile 16 /* These are values returned by tf_typep */ #define tf_nullparam 0 #define TF_NULLPARAM tf_nullparam #define tf_string 1 #define TF_STRING tf_string #define tf_readonly 10 #define TF_READONLY tf_readonly #define tf_readwrite 11 #define TF_READWRITE tf_readwrite #define tf_rwbitselect 12 #define TF_RWBITSELECT tf_rwbitselect #define tf_rwpartselect 13 #define TF_RWPARTSELECT tf_rwpartselect #define tf_rwmemselect 14 #define TF_RWMEMSELECT tf_rwmemselect #define tf_readonlyreal 15 #define TF_READONLYREAL tf_readonlyreal #define tf_readwritereal 16 #define TF_READWRITEREAL tf_readwritereal typedef struct t_tfnodeinfo { PLI_INT16 node_type; PLI_INT16 padding; union { struct t_vecval *vecval_p; struct t_strengthval *strengthval_p; PLI_BYTE8 *memoryval_p; double *read_value_p; } node_value; char* node_symbol; PLI_INT32 node_ngroups; PLI_INT32 node_vec_size; PLI_INT32 node_sign; PLI_INT32 node_ms_index; PLI_INT32 node_ls_index; PLI_INT32 node_mem_size; PLI_INT32 node_lhs_element; PLI_INT32 node_rhs_element; PLI_INT32*node_handle; } s_tfnodeinfo, *p_tfnodeinfo; /* values used in the node_type of the tfnodeinfo structure. */ #define tf_null_node 100 #define TF_NULL_NODE tf_null_node #define tf_reg_node 101 #define TF_REG_NODE tf_reg_node #define tf_integer_node 102 #define TF_INTEGER_NODE tf_integer_node #define tf_time_node 103 #define TF_TIME_NODE tf_time_node #define tf_netvector_node 104 #define TF_NETVECTOR_NODE tf_netvector_node #define tf_netscalar_node 105 #define TF_NETSCALAR_NODE tf_netscalar_node #define tf_memory_node 106 #define TF_MEMORY_NODE tf_memory_node #define tf_real_node 107 #define TF_REAL_NODE tf_real_node /* Structure used by the tf_exprinfo function. */ typedef struct t_tfexprinfo { PLI_INT16 expr_type; PLI_INT16 padding; struct t_vecval*expr_value_p; double real_value; char*expr_string; PLI_INT32 expr_ngroups; PLI_INT32 expr_vec_size; PLI_INT32 expr_sign; PLI_INT32 expr_lhs_select; PLI_INT32 expr_rhs_select; } s_tfexprinfo, *p_tfexprinfo; /* Extern functions from the library. */ extern void io_printf (const char *, ...) __attribute__((format (printf,1,2))); extern char* mc_scan_plusargs(char*plusarg); extern int tf_asynchoff(void); extern int tf_asynchon(void); extern int tf_dofinish(void); extern int tf_dostop(void); extern void tf_error(const char*, ...) __attribute__((format (printf,1,2))); extern struct t_tfexprinfo* tf_exprinfo(PLI_INT32 a, struct t_tfexprinfo*ip); extern char* tf_getcstringp(int nparam); extern PLI_BYTE8* tf_getinstance(void); extern int tf_getlongp(int*aof_highvalue, int pnum); extern PLI_INT32 tf_getp(PLI_INT32); extern PLI_INT32 tf_igetp(PLI_INT32, void*); extern double tf_getrealp(PLI_INT32); extern double tf_igetrealp(PLI_INT32, void*); extern char *tf_strgetp(PLI_INT32, PLI_INT32); extern char *tf_istrgetp(PLI_INT32, PLI_INT32, void*); extern char *tf_strgettime(void); extern PLI_INT32 tf_gettime(void); extern PLI_INT32 tf_getlongtime(PLI_INT32*); extern PLI_INT32 tf_igetlongtime(PLI_INT32*, void*); extern void tf_scale_longdelay(void*,PLI_INT32,PLI_INT32,PLI_INT32*,PLI_INT32*); extern void tf_unscale_longdelay(void*,PLI_INT32,PLI_INT32,PLI_INT32*,PLI_INT32*); extern void tf_scale_realdelay(void*,double,double*); extern void tf_unscale_realdelay(void*,double,double*); extern PLI_INT32 tf_gettimeprecision(void); extern PLI_INT32 tf_igettimeprecision(void*); extern PLI_INT32 tf_gettimeunit(void); extern PLI_INT32 tf_igettimeunit(void*); extern PLI_BYTE8* tf_getworkarea(void); extern PLI_INT32 tf_message(PLI_INT32 level, char*facility, char*messno, char*fmt, ...) __attribute__((format (printf,4,5))); extern void tf_multiply_long(PLI_INT32*aof_low1, PLI_INT32*aof_high1, PLI_INT32 aof_low2, PLI_INT32 aof_high2); extern void tf_real_to_long(double real, PLI_INT32*low, PLI_INT32*high); extern void tf_long_to_real(PLI_INT32 low, PLI_INT32 high, double *real); extern PLI_INT32 tf_nump(void); extern PLI_INT32 tf_inump(void*); /* IEEE1364 NOTE: tf_putlongp is listed as returning in in the header file shown in the standard, but as returning void in the detailed description of the function. So I call it void. */ extern void tf_putlongp(int pnum, int lowvalue, int highvalue); extern PLI_INT32 tf_putp(PLI_INT32, PLI_INT32); extern PLI_INT32 tf_iputp(PLI_INT32, PLI_INT32, void*); extern PLI_INT32 tf_putrealp(PLI_INT32, double); extern PLI_INT32 tf_iputrealp(PLI_INT32, double, void*); /* Activate the misctf function after a delay. The units are of the current scope. The tf_isetdelay variant specifies a particular system task instance to use as the context for the units. tf_getinstance gets that value. */ extern int tf_setdelay(PLI_INT32 delay); extern int tf_isetdelay(PLI_INT32 delay, void* sys); /* IEEE1364 NOTE: tf_setworkarea is listed as taking a PLI_BYTE8*, but that is silly, it really takes any kind of pointer. Taking void* is compatible with those who pass a PLI_BYTE8*. */ extern PLI_INT32 tf_setworkarea(void*workarea); /* Return the complete, hierarchical name of the current scope. The current scope is the scope containing the currently running system task call. */ extern char* tf_spname(void); extern char* tf_mipname(void); extern char* tf_imipname(void*); extern PLI_INT32 tf_synchronize(void); extern PLI_INT32 tf_isynchronize(void* sys); extern PLI_INT32 tf_rosynchronize(void); extern PLI_INT32 tf_irosynchronize(void* sys); extern PLI_INT32 tf_setrealdelay(double realdelay); extern PLI_INT32 tf_isetrealdelay(double realdelay, void*inst); extern PLI_INT32 tf_typep(PLI_INT32 narg); extern void tf_warning(const char*, ...) __attribute__((format (printf,1,2))); EXTERN_C_END #endif /* VERIUSER_H */ iverilog-10_1/version.c000066400000000000000000000037211265551621300151760ustar00rootroot00000000000000/* * Copyright (c) 2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "version_base.h" # include "version_tag.h" # include # include static void run_string(const char*txt) { const char*cp = txt; while (*cp) { if (cp[0] == '%' && cp[1] != 0) { switch (cp[1]) { case 'M': fprintf(stdout, "%u", VERSION_MAJOR); break; case 'n': fprintf(stdout, "%u", VERSION_MINOR); break; case 'E': fprintf(stdout, "%s", VERSION_EXTRA); break; case 'T': fprintf(stdout, "%s", VERSION_TAG); break; case '%': putc('%', stdout); break; default: break; } cp += 2; } else if (cp[0] == '\\' && cp[1] != 0) { switch (cp[1]) { case 'n': putc('\n', stdout); break; default: putc(cp[1], stdout); break; } cp += 2; } else { putc(cp[0], stdout); cp += 1; } } } int main(int argc, char*argv[]) { int idx; if (argc == 1) { printf("%s\n", VERSION); return 0; } run_string(argv[1]); for (idx = 2 ; idx < argc ; idx += 1) { printf(" "); run_string(argv[idx]); } return 0; } iverilog-10_1/version_base.h000066400000000000000000000007611265551621300161760ustar00rootroot00000000000000#ifndef VERSION /* * Edit this definition in version_base.in to define the base version * number for the compiled result. */ # define VERSION_MAJOR 10 # define VERSION_MINOR 1 /* * This will be appended to the version. Use this to mark development * versions and the like. */ # define VERSION_EXTRA " (stable)" # define VERSION_STRINGIFY(x) #x # define VERSION_STR(a,b,extra) VERSION_STRINGIFY(a.b) extra #define VERSION VERSION_STR(VERSION_MAJOR,VERSION_MINOR,VERSION_EXTRA) #endif iverilog-10_1/vhdlpp/000077500000000000000000000000001265551621300146375ustar00rootroot00000000000000iverilog-10_1/vhdlpp/Makefile.in000066400000000000000000000105761265551621300167150ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ # Look in the appropriate source directory for the C++ files. The one # exception to this is if we need to rebuild the lexor_keyword.cc file. # If we do, then we want to use the local version instead of the one is # $(srcdir). vpath lexor_keyword.cc . vpath %.cc $(srcdir)/../libmisc vpath %.cc $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include CC = @CC@ CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ YACC = @YACC@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. -I../libmisc else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../libmisc endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @EXTRALIBS@ M = StringHeap.o LineInfo.o O = main.o architec.o compiler.o entity.o std_funcs.o std_types.o \ expression.o package.o scope.o sequential.o subprogram.o vsignal.o vtype.o \ vtype_match.o \ architec_elaborate.o entity_elaborate.o expression_elaborate.o \ expression_evaluate.o \ sequential_elaborate.o \ vtype_elaborate.o \ entity_stream.o expression_stream.o vtype_stream.o \ lexor.o lexor_keyword.o parse.o \ parse_misc.o library.o vhdlreal.o vhdlint.o \ architec_emit.o entity_emit.o expression_emit.o package_emit.o \ sequential_emit.o subprogram_emit.o vtype_emit.o \ debug.o architec_debug.o expression_debug.o sequential_debug.o \ $M all: dep vhdlpp@EXEEXT@ check: all clean: rm -f *.o *~ parse.cc parse.h lexor.cc parse.output lexor_keyword.cc rm -rf dep vhdlpp@EXEEXT@ distclean: clean rm -f Makefile config.log rm -f stamp-vhdlpp_config-h vhdlpp_config.h cppcheck: $(O:.o=.cc) cppcheck --enable=all -f \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in cd ..; ./config.status --file=vhdlpp/$@ dep: mkdir dep vhdlpp@EXEEXT@: $O $(CXX) -o vhdlpp@EXEEXT@ $(LDFLAGS) $O $(LIBS) %.o: $(srcdir)/../libmisc/%.cc $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d %.o: %.cc vhdlpp_config.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d lexor.o: lexor.cc parse.h parse.o: parse.cc lexor.cc: $(srcdir)/lexor.lex $(LEX) -s -olexor.cc $(srcdir)/lexor.lex # Build this in two steps to avoid parallel build issues (see pr3462585) parse.cc: $(srcdir)/parse.y $(YACC) --verbose -t -d -o $@ $< parse.h: parse.cc mv parse.cc.h $@ 2>/dev/null || mv parse.hh $@ lexor_keyword.o: lexor_keyword.cc parse.h lexor_keyword.cc: $(srcdir)/lexor_keyword.gperf gperf -o -i 7 --ignore-case -C -k 1-4,6,9,$$ -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false) install: all installdirs $(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@ $(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@: vhdlpp@EXEEXT@ $(INSTALL_PROGRAM) ./vhdlpp@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@" stamp-vhdlpp_config-h: $(srcdir)/vhdlpp_config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=vhdlpp/vhdlpp_config.h vhplpp_config.h: stamp-vhdlpp_config-h -include $(patsubst %.o, dep/%.d, $O $M) iverilog-10_1/vhdlpp/README.txt000066400000000000000000000025331265551621300163400ustar00rootroot00000000000000 vhdlpp COMMAND LINE FLAGS: -D Debug flags. The token can be: * yydebug | no-yydebug * entities= -L Library path. Add the directory name to the front of the library search path. The library search path is initially empty. -V Display version on stdout -v Verbose: Display version on stderr, and enable verbose messages to stderr. -w Work path. This is the directory where the working directory is. LIBRARY FORMAT: The vhdlpp program stores libraries as directory that contain packages. The name of the directory (in lower case) is the name of the library as used on the "import" statement. Within that library, there are packages in files named .pkg. For example: /... sample/... test1.pkg test2.pkg bar/... test3.pkg Use the "+vhdl-libdir+" record in a config file to tell Icarus Verilog that is a place to look for libraries. Then in your VHDL code, access packages like this: library sample; library bar; use sample.test1.all; use bar.test3.all; The *.pkg files are just VHDL code containing only the package with the same name. When Icarus Verilog encounters the "use ..*;" statement, it looks for the .pkg file in the library and parses that file to get the package header declared therein. iverilog-10_1/vhdlpp/architec.cc000066400000000000000000000150001265551621300167240ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "architec.h" # include "expression.h" # include "parse_types.h" // Need this for parse_errors? # include "parse_api.h" # include using namespace std; Architecture::Architecture(perm_string name, const ActiveScope&ref, list&s) : Scope(ref), name_(name), cur_component_(NULL) { statements_.splice(statements_.end(), s); } Architecture::~Architecture() { delete_all(statements_); ScopeBase::cleanup(); } bool Architecture::find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const { if(Scope::find_constant(by_name, typ, exp)) return true; // Check generics in components if(cur_component_) { std::map::const_iterator c = new_components_.find(cur_component_->component_name()); if(c == new_components_.end()) c = old_components_.find(cur_component_->component_name()); assert(c != old_components_.end()); ComponentBase*base = c->second; const InterfacePort*generic = base->find_generic(by_name); if(!generic) return false; // apparently there is no such generic in the component Expression*e = cur_component_->find_generic_map(by_name); typ = generic->type; exp = e ? e : generic->expr; return true; } return false; } void Architecture::push_genvar_type(perm_string gname, const VType*gtype) { genvar_type_t tmp; tmp.name = gname; tmp.vtype = gtype; genvar_type_stack_.push_back(tmp); } void Architecture::pop_genvar_type(void) { assert(! genvar_type_stack_.empty()); genvar_type_stack_.pop_back(); } const VType* Architecture::probe_genvar_type(perm_string gname) { for (std::list::reverse_iterator cur = genvar_type_stack_.rbegin() ; cur != genvar_type_stack_.rend() ; ++cur) { if (cur->name == gname) return cur->vtype; } return 0; } void Architecture::push_genvar_emit(perm_string gname, const GenerateStatement*gen) { genvar_emit_t tmp; tmp.name = gname; tmp.gen = gen; genvar_emit_stack_.push_back(tmp); } void Architecture::pop_genvar_emit(void) { assert(! genvar_emit_stack_.empty()); genvar_emit_stack_.pop_back(); } const GenerateStatement* Architecture::probe_genvar_emit(perm_string gname) { for (std::list::reverse_iterator cur = genvar_emit_stack_.rbegin() ; cur != genvar_emit_stack_.rend() ; ++cur) { if (cur->name == gname) return cur->gen; } return 0; } Architecture::Statement::Statement() { } Architecture::Statement::~Statement() { } GenerateStatement::GenerateStatement(perm_string gname, std::list&s) : name_(gname) { statements_.splice(statements_.end(), s); } GenerateStatement::~GenerateStatement() { for_each(statements_.begin(), statements_.end(), ::delete_object()); } ForGenerate::ForGenerate(perm_string gname, perm_string genvar, prange_t*rang, std::list&s) : GenerateStatement(gname, s), genvar_(genvar), lsb_(rang->lsb()), msb_(rang->msb()) { } ForGenerate::~ForGenerate() { } IfGenerate::IfGenerate(perm_string gname, Expression*cond, std::list&s) : GenerateStatement(gname, s), cond_(cond) { } IfGenerate::~IfGenerate() { } SignalAssignment::SignalAssignment(ExpName*name, list&rv) : lval_(name) { rval_.splice(rval_.end(), rv); } SignalAssignment::SignalAssignment(ExpName*name, Expression*rv) : lval_(name) { rval_.push_back(rv); } SignalAssignment::~SignalAssignment() { for (list::iterator cur = rval_.begin() ; cur != rval_.end() ; ++cur) { delete *cur; } delete lval_; } ComponentInstantiation::ComponentInstantiation(perm_string i, perm_string c, list*parms, list*ports) : iname_(i), cname_(c) { typedef pair::iterator,bool> insert_rc; while (parms && ! parms->empty()) { named_expr_t*cur = parms->front(); parms->pop_front(); insert_rc rc = generic_map_.insert(make_pair(cur->name(), cur->expr())); if (! rc.second) { cerr << "?:?: error: Duplicate map of generic " << cur->name() << " ignored." << endl; parse_errors += 1; } } while (ports && ! ports->empty()) { named_expr_t*cur = ports->front(); ports->pop_front(); insert_rc rc = port_map_.insert(make_pair(cur->name(), cur->expr())); if (! rc.second) { cerr << "?:?: error: Duplicate map of port " << cur->name() << " ignored." << endl; parse_errors += 1; } } } ComponentInstantiation::~ComponentInstantiation() { for (map::iterator it = generic_map_.begin() ; it != generic_map_.end() ; ++it) { delete it->second; } for (map::iterator it = port_map_.begin() ; it != port_map_.end(); ++it) { delete it->second; } } Expression*ComponentInstantiation::find_generic_map(perm_string by_name) const { map::const_iterator p = generic_map_.find(by_name); if(p == generic_map_.end()) return NULL; return p->second; } ProcessStatement::ProcessStatement(perm_string iname, std::list*sensitivity_list, std::list*statements_list) : iname_(iname) { if (sensitivity_list) sensitivity_list_.splice(sensitivity_list_.end(), *sensitivity_list); if (statements_list) statements_list_.splice(statements_list_.end(), *statements_list); } ProcessStatement::~ProcessStatement() { } iverilog-10_1/vhdlpp/architec.h000066400000000000000000000167761265551621300166130ustar00rootroot00000000000000#ifndef IVL_architec_H #define IVL_architec_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include "LineInfo.h" # include "scope.h" # include # include class ComponentInstantiation; class Entity; class Expression; class ExpName; class GenerateStatement; class SequentialStmt; class Signal; class named_expr_t; class prange_t; /* * The Architecture class carries the contents (name, statements, * etc.) of a parsed VHDL architecture. These objects are ultimately * put into entities. */ class Architecture : public Scope, public LineInfo { public: // Architectures contain concurrent statements, that are // derived from this nested class. class Statement : public LineInfo { public: Statement(); virtual ~Statement() =0; virtual int elaborate(Entity*ent, Architecture*arc); virtual int emit(ostream&out, Entity*ent, Architecture*arc); virtual void dump(ostream&out, int indent = 0) const; }; public: // Create an architecture from its name and its statements. // NOTE: The statement list passed in is emptied. Architecture(perm_string name, const ActiveScope&ref, std::list&s); ~Architecture(); perm_string get_name() const { return name_; } // Sets the currently processed component (to be able to reach its parameters). void set_cur_component(ComponentInstantiation*component) { cur_component_ = component; } bool find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const; // Elaborate this architecture in the context of the given entity. int elaborate(Entity*entity); // These methods are used while in the scope of a generate // block to mark that a name is a genvar at this point. const VType* probe_genvar_type(perm_string); void push_genvar_type(perm_string gname, const VType*gtype); void pop_genvar_type(void); // These methods are used during EMIT to check for names that // are genvar names. const GenerateStatement* probe_genvar_emit(perm_string); void push_genvar_emit(perm_string gname, const GenerateStatement*); void pop_genvar_emit(void); // Emit this architecture to the given out file in the context // of the specified entity. This method is used by the // elaborate code to display generated code to the specified // output. int emit(ostream&out, Entity*entity); // The dump method writes a debug display to the given output. void dump(ostream&out, perm_string of_entity, int indent = 0) const; private: perm_string name_; // Concurrent statements local to this architecture std::list statements_; struct genvar_type_t { perm_string name; const VType*vtype; }; std::list genvar_type_stack_; struct genvar_emit_t { perm_string name; const GenerateStatement*gen; }; std::list genvar_emit_stack_; // Currently processed component (or NULL if none). ComponentInstantiation*cur_component_; private: // Not implemented }; /* * This is a base class for various generate statement types. It holds * the generate statement name, and a list of substatements. */ class GenerateStatement : public Architecture::Statement { public: GenerateStatement(perm_string gname, std::list&s); ~GenerateStatement(); inline perm_string get_name() const { return name_; } protected: int elaborate_statements(Entity*ent, Architecture*arc); int emit_statements(ostream&out, Entity*ent, Architecture*arc); void dump_statements(ostream&out, int indent) const; private: perm_string name_; std::list statements_; }; class ForGenerate : public GenerateStatement { public: ForGenerate(perm_string gname, perm_string genvar, prange_t*rang, std::list&s); ~ForGenerate(); int elaborate(Entity*ent, Architecture*arc); int emit(ostream&out, Entity*entity, Architecture*arc); void dump(ostream&out, int ident =0) const; private: perm_string genvar_; Expression*lsb_; Expression*msb_; }; class IfGenerate : public GenerateStatement { public: IfGenerate(perm_string gname, Expression*cond, std::list&s); ~IfGenerate(); int elaborate(Entity*ent, Architecture*arc); int emit(ostream&out, Entity*entity, Architecture*arc); private: Expression*cond_; }; /* * The SignalAssignment class represents the * concurrent_signal_assignment that is placed in an architecture. */ class SignalAssignment : public Architecture::Statement { public: SignalAssignment(ExpName*target, std::list&rval); SignalAssignment(ExpName*target, Expression*rval); ~SignalAssignment(); virtual int elaborate(Entity*ent, Architecture*arc); virtual int emit(ostream&out, Entity*entity, Architecture*arc); virtual void dump(ostream&out, int ident =0) const; private: ExpName*lval_; std::list rval_; }; class ComponentInstantiation : public Architecture::Statement { public: ComponentInstantiation(perm_string iname, perm_string cname, std::list*parms, std::list*ports); ~ComponentInstantiation(); virtual int elaborate(Entity*ent, Architecture*arc); virtual int emit(ostream&out, Entity*entity, Architecture*arc); virtual void dump(ostream&out, int indent =0) const; // Returns the expression that initializes a generic (or NULL if not found). Expression*find_generic_map(perm_string by_name) const; inline perm_string instance_name() const { return iname_; } inline perm_string component_name() const { return cname_; } private: perm_string iname_; perm_string cname_; std::map generic_map_; std::map port_map_; }; class ProcessStatement : public Architecture::Statement { public: ProcessStatement(perm_string iname, std::list*sensitivity_list, std::list*statement_list); ~ProcessStatement(); virtual int elaborate(Entity*ent, Architecture*arc); virtual int emit(ostream&out, Entity*entity, Architecture*arc); virtual void dump(ostream&out, int indent =0) const; private: int rewrite_as_always_edge_(Entity*ent, Architecture*arc); int extract_anyedge_(Entity*ent, Architecture*arc); private: perm_string iname_; std::list sensitivity_list_; std::list statements_list_; }; #endif /* IVL_architec_H */ iverilog-10_1/vhdlpp/architec_debug.cc000066400000000000000000000073311265551621300201020ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "architec.h" # include "expression.h" # include "sequential.h" # include # include # include using namespace std; void Architecture::dump(ostream&out, perm_string of_entity, int indent) const { out << setw(indent) << "" << "architecture " << name_ << " of entity " << of_entity << " file=" << get_fileline() << endl; dump_scope(out); for (list::const_iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { (*cur)->dump(out, indent+3); } } void Architecture::Statement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Architecture::Statement at file=" << get_fileline() << endl; } void ComponentInstantiation::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Component Instantiation " << "instance=" << iname_ << " of component=" << cname_ << ", file=" << get_fileline() << endl; for (map::const_iterator cur = port_map_.begin() ; cur != port_map_.end() ; ++cur) { out << setw(indent+2) <<""<< cur->first << " => ..." << endl; if (cur->second) cur->second->dump(out, indent+6); else out << setw(indent+6) <<""<< "OPEN" << endl; } } void GenerateStatement::dump_statements(ostream&out, int indent) const { for (list::const_iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { Statement*curp = *cur; curp->dump(out, indent); } } void ForGenerate::dump(ostream&out, int indent) const { out << setw(indent) << "" << "for " << genvar_ << " in" << endl; msb_->dump(out, indent+4); lsb_->dump(out, indent+4); out << setw(indent) << "" << "generate" << endl; dump_statements(out, indent+4); out << setw(indent) << "" << "end generate" << endl; } void SignalAssignment::dump(ostream&out, int indent) const { out << setw(indent) << "" << "SignalAssignment file=" << get_fileline() << endl; lval_->dump(out, indent+1); out << setw(indent+2) << "" << "<= ..." << endl; for (list::const_iterator cur = rval_.begin() ; cur != rval_.end() ; ++cur) { (*cur)->dump(out, indent+2); } } void ProcessStatement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ProcessStatement name_=" << iname_ << " file=" << get_fileline() << endl; out << setw(indent+3) << "" << "Sensitivity_list:" << endl; for (list::const_iterator cur = sensitivity_list_.begin() ; cur != sensitivity_list_.end() ; ++cur) { (*cur)->dump(out, indent+4); } out << setw(indent+3) << "" << "sequence of statements:" << endl; for (list::const_iterator cur = statements_list_.begin() ; cur != statements_list_.end() ; ++cur) { (*cur)->dump(out, indent+4); } } iverilog-10_1/vhdlpp/architec_elaborate.cc000066400000000000000000000243121265551621300207500ustar00rootroot00000000000000/* * Copyright (c) 2011-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "architec.h" # include "entity.h" # include "expression.h" # include "sequential.h" # include # include int Architecture::elaborate(Entity*entity) { int errors = 0; // Constant assignments in the architecture get their types // from the constant declaration itself. Elaborate the value // expression with the declared type. for (map::iterator cur = use_constants_.begin() ; cur != use_constants_.end() ; ++cur) { cur->second->val->elaborate_expr(entity, this, cur->second->typ); } for (map::iterator cur = cur_constants_.begin() ; cur != cur_constants_.end() ; ++cur) { cur->second->val->elaborate_expr(entity, this, cur->second->typ); } // Elaborate initializer expressions for signals & variables for (map::iterator cur = old_signals_.begin() ; cur != old_signals_.end() ; ++cur) { cur->second->elaborate_init_expr(entity, this); } for (map::iterator cur = new_signals_.begin() ; cur != new_signals_.end() ; ++cur) { cur->second->elaborate_init_expr(entity, this); } for (map::iterator cur = old_variables_.begin() ; cur != old_variables_.end() ; ++cur) { cur->second->elaborate_init_expr(entity, this); } for (map::iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { cur->second->elaborate_init_expr(entity, this); } for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { int cur_errors = (*cur)->elaborate(entity, this); errors += cur_errors; } if (errors > 0) { cerr << errors << " errors in " << name_ << " architecture of " << entity->get_name() << "." << endl; } return errors; } int Architecture::Statement::elaborate(Entity*, Architecture*) { return 0; } int ComponentInstantiation::elaborate(Entity*ent, Architecture*arc) { int errors = 0; ComponentBase*base = arc->find_component(cname_); if (base == 0) { cerr << get_fileline() << ": error: No component declaration" << " for instance " << iname_ << " of " << cname_ << "." << endl; return 1; } arc->set_cur_component(this); for (map::const_iterator cur = generic_map_.begin() ; cur != generic_map_.end() ; ++cur) { // check if generic from component instantiation // exists in the component declaration const InterfacePort*iparm = base->find_generic(cur->first); if (iparm == 0) { cerr << get_fileline() << ": warning: No generic " << cur->first << " in component " << cname_ << "." << endl; continue; } ExpName* tmp; if (cur->second && (tmp = dynamic_cast(cur->second))) errors += tmp->elaborate_rval(ent, arc, iparm); if (cur->second) errors += cur->second->elaborate_expr(ent, arc, iparm->type); } for (map::const_iterator cur = port_map_.begin() ; cur != port_map_.end() ; ++cur) { // check if a port from component instantiation // exists in the component declaration const InterfacePort*iport = base->find_port(cur->first); if (iport == 0) { cerr << get_fileline() << ": error: No port " << cur->first << " in component " << cname_ << "." << endl; errors += 1; continue; } ExpName* tmp; if (cur->second && (tmp = dynamic_cast(cur->second))) errors += tmp->elaborate_rval(ent, arc, iport); /* It is possible for the port to be explicitly unconnected. In that case, the Expression will be nil */ if (cur->second) cur->second->elaborate_expr(ent, arc, iport->type); } arc->set_cur_component(NULL); return errors; } int GenerateStatement::elaborate_statements(Entity*ent, Architecture*arc) { int errors = 0; for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { Architecture::Statement*curp = *cur; errors += curp->elaborate(ent, arc); } return errors; } int ForGenerate::elaborate(Entity*ent, Architecture*arc) { int errors = 0; arc->push_genvar_type(genvar_, lsb_->probe_type(ent, arc)); errors += elaborate_statements(ent, arc); arc->pop_genvar_type(); return errors; } int IfGenerate::elaborate(Entity*ent, Architecture*arc) { int errors = 0; errors += elaborate_statements(ent, arc); return errors; } /* * This method attempts to rewrite the process content as an * always-@(n-edge ) version of the same statement. This makes * for a more natural translation to Verilog, if it comes to that. */ int ProcessStatement::rewrite_as_always_edge_(Entity*, Architecture*) { // If there are multiple sensitivity expressions, I give up. if (sensitivity_list_.size() != 1) return -1; // If there are multiple statements, I give up. if (statements_list_.size() != 1) return -1; Expression*se = sensitivity_list_.front(); SequentialStmt*stmt_raw = statements_list_.front(); // If the statement is not an if-statement, I give up. IfSequential*stmt = dynamic_cast (stmt_raw); if (stmt == 0) return -1; // If the "if" statement has a false clause, then give up. if (stmt->false_size() != 0) return -1; const Expression*ce_raw = stmt->peek_condition(); // Here we expect the condition to be // 'event AND ='1'. // So if ce_raw is not a logical AND, I give up. const ExpLogical*ce = dynamic_cast (ce_raw); if (ce == 0) return -1; if (ce->logic_fun() != ExpLogical::AND) return -1; const Expression*op1_raw = ce->peek_operand1(); const Expression*op2_raw = ce->peek_operand2(); if (dynamic_cast(op2_raw)) { const Expression*tmp = op1_raw; op1_raw = op2_raw; op2_raw = tmp; } // If operand1 is not an 'event attribute, I give up. const ExpAttribute*op1 = dynamic_cast(op1_raw); if (op1 == 0) return -1; if (op1->peek_attribute() != "event") return -1; const ExpRelation*op2 = dynamic_cast(op2_raw); if (op2 == 0) return -1; if (op2->relation_fun() != ExpRelation::EQ) return -1; const Expression*op2a_raw = op2->peek_operand1(); const Expression*op2b_raw = op2->peek_operand2(); if (dynamic_cast(op2a_raw)) { const Expression*tmp = op2b_raw; op2b_raw = op2a_raw; op2a_raw = tmp; } if (! se->symbolic_compare(op1->peek_base())) return -1; const ExpCharacter*op2b = dynamic_cast(op2b_raw); if (op2b->value() != '1' && op2b->value() != '0') return -1; // We've matched this pattern: // process () if ('event and = ) then ... // And we can convert it to: // always @(edge ) ... // Replace the sensitivity expression with an edge // expression. The ExpEdge expression signals that this is an // always-@(edge) statement. ExpEdge*edge = new ExpEdge(op2b->value()=='1'? ExpEdge::POSEDGE : ExpEdge::NEGEDGE, se); assert(sensitivity_list_.size() == 1); sensitivity_list_.pop_front(); sensitivity_list_.push_front(edge); // Replace the statement with the body of the always // statement, which is the true clause of the top "if" // statement. There should be no "else" clause. assert(statements_list_.size() == 1); statements_list_.pop_front(); stmt->extract_true(statements_list_); delete stmt; return 0; } /* * Change the "process () " into "always @() ..." */ int ProcessStatement::extract_anyedge_(Entity*, Architecture*) { vector se; while (! sensitivity_list_.empty()) { se.push_back(sensitivity_list_.front()); sensitivity_list_.pop_front(); } for (size_t idx = 0 ; idx < se.size() ; idx += 1) { ExpEdge*edge = new ExpEdge(ExpEdge::ANYEDGE, se[idx]); FILE_NAME(edge, se[idx]); sensitivity_list_.push_back(edge); } return 0; } int ProcessStatement::elaborate(Entity*ent, Architecture*arc) { int errors = 0; if (rewrite_as_always_edge_(ent, arc) >= 0) { extract_anyedge_(ent, arc); } for (list::iterator cur = statements_list_.begin() ; cur != statements_list_.end() ; ++cur) { errors += (*cur)->elaborate(ent, arc); } return errors; } int SignalAssignment::elaborate(Entity*ent, Architecture*arc) { int errors = 0; // Elaborate the l-value expression. errors += lval_->elaborate_lval(ent, arc, false); // The elaborate_lval should have resolved the type of the // l-value expression. We'll use that type to elaborate the // r-value. const VType*lval_type = lval_->peek_type(); if (lval_type == 0) { if (errors == 0) { errors += 1; cerr << get_fileline() << ": error: Unable to calculate type for l-value expression." << endl; } return errors; } for (list::iterator cur = rval_.begin() ; cur != rval_.end() ; ++cur) { errors += (*cur)->elaborate_expr(ent, arc, lval_type); } return errors; } iverilog-10_1/vhdlpp/architec_emit.cc000066400000000000000000000210341265551621300177460ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "architec.h" # include "entity.h" # include "expression.h" # include "sequential.h" # include "subprogram.h" # include "vsignal.h" # include # include # include int Scope::emit_signals(ostream&out, Entity*entity, Architecture*arc) { int errors = 0; for (map::iterator cur = old_signals_.begin() ; cur != old_signals_.end() ; ++cur) { errors += cur->second->emit(out, entity, arc); } for (map::iterator cur = new_signals_.begin() ; cur != new_signals_.end() ; ++cur) { errors += cur->second->emit(out, entity, arc); } return errors; } int Scope::emit_variables(ostream&out, Entity*entity, Architecture*arc) { int errors = 0; for (map::iterator cur = old_variables_.begin() ; cur != old_variables_.end() ; ++cur) { errors += cur->second->emit(out, entity, arc); } for (map::iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { errors += cur->second->emit(out, entity, arc); } return errors; } int Architecture::emit(ostream&out, Entity*entity) { int errors = 0; // Find typedefs that are present in the architecture body and // emit them, so that following code can use the name instead // of the full definition. typedef_context_t typedef_ctx; //for (map::iterator cur = use_types_.begin() //; cur != use_types_.end() ; ++cur) { //if(const VTypeDef*def = dynamic_cast(cur->second)) //errors += def->emit_typedef(out, typedef_ctx); //} for (map::iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { if(const VTypeDef*def = dynamic_cast(cur->second)) errors += def->emit_typedef(out, typedef_ctx); } for (map::iterator cur = use_constants_.begin() ; cur != use_constants_.end() ; ++cur) { out << "localparam " << cur->first << " = "; errors += cur->second->val->emit(out, entity, this); out << ";" << endl; } for (map::iterator cur = cur_constants_.begin() ; cur != cur_constants_.end() ; ++cur) { out << "localparam " << cur->first << " = "; errors += cur->second->val->emit(out, entity, this); out << ";" << endl; } errors += emit_signals(out, entity, this); errors += emit_variables(out, entity, this); for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++ cur) { // Do not emit unbounded functions, we will just need fixed instances later if(!cur->second->unbounded()) errors += cur->second->emit_package(out); } for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { errors += (*cur)->emit(out, entity, this); } return errors; } int Architecture::Statement::emit(ostream&out, Entity*, Architecture*) { out << " // " << get_fileline() << ": internal error: " << "I don't know how to emit this statement! " << "type=" << typeid(*this).name() << endl; return 1; } int SignalAssignment::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; ivl_assert(*this, rval_.size() == 1); Expression*rval = rval_.front(); out << "// " << get_fileline() << endl; out << "assign "; errors += lval_->emit(out, ent, arc); out << " = "; errors += rval->emit(out, ent, arc); out << ";" << endl; return errors; } int ComponentInstantiation::emit(ostream&out, Entity*ent, Architecture*arc) { const char*comma = ""; int errors = 0; arc->set_cur_component(this); if(ComponentBase*comp = arc->find_component(cname_)) { const std::vector& generics = comp->get_generics(); if(generics.size() != generic_map_.size()) // Display an error for generics that do not have neither // default nor component specific value defined for(vector::const_iterator it = generics.begin(); it != generics.end(); ++it) { if(!(*it)->expr && generic_map_.count((*it)->name) == 0) { cerr << get_fileline() << ": generic " << (*it)->name << "value is not defined" << endl; ++errors; } } } out << cname_; if (! generic_map_.empty()) { out << " #("; comma = ""; for (map::iterator cur = generic_map_.begin() ; cur != generic_map_.end() ; ++cur) { ivl_assert(*this, cur->second); out << comma << ".\\" << cur->first << " ("; errors += cur->second->emit(out, ent, arc); out << ")"; comma = ", "; } out << ")"; } out << " \\" << iname_ << " ("; comma = ""; for (map::iterator cur = port_map_.begin() ; cur != port_map_.end() ; ++cur) { // Skip unconnected ports if (cur->second == 0) continue; out << comma << ".\\" << cur->first << " ("; errors += cur->second->emit(out, ent, arc); out << ")"; comma = ", "; } out << ");" << endl; arc->set_cur_component(NULL); return errors; } int GenerateStatement::emit_statements(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { Architecture::Statement*curp = *cur; errors += curp->emit(out, ent, arc); } return errors; } int ForGenerate::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; out << "genvar \\" << get_name() << ":" << genvar_ << " ;" << endl; out << "for (\\" << get_name() << ":" << genvar_ << " = "; errors += lsb_->emit(out, ent, arc); out << "; \\" << get_name() << ":" << genvar_ << " <= "; errors += msb_->emit(out, ent, arc); out << "; \\" << get_name() << ":" << genvar_ << " = \\" << get_name() << ":" << genvar_ << " + 1)" << " begin : \\" << get_name() << endl; arc->push_genvar_emit(genvar_, this); errors += emit_statements(out, ent, arc); arc->pop_genvar_emit(); out << "end" << endl; return errors; } int IfGenerate::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; out << "if ("; cond_->emit(out, ent, arc); out << ") begin : \\" << get_name() << endl; errors += emit_statements(out, ent, arc); out << "end" << endl; return errors; } /* * Emit a process statement using "always" syntax. * * Note that VHDL is different from Verilog, in that the sensitivity * list goes at the END of the statement list, not at the * beginning. In VHDL, all the statements are initially executed once * before blocking in the first wait on the sensitivity list. */ int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; out << "always begin" << endl; for (list::iterator cur = statements_list_.begin() ; cur != statements_list_.end() ; ++cur) { errors += (*cur)->emit(out, ent, arc); } if (! sensitivity_list_.empty()) { out << "@("; const char*comma = 0; for (list::iterator cur = sensitivity_list_.begin() ; cur != sensitivity_list_.end() ; ++cur) { if (comma) out << comma; errors += (*cur)->emit(out, ent, arc); comma = ", "; } out << ") /* sensitivity list for process */;" << endl; } out << "end" << endl; return errors; } iverilog-10_1/vhdlpp/compiler.cc000066400000000000000000000016461265551621300167670ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "compiler.h" StringHeapLex lex_strings; StringHeapLex filename_strings; iverilog-10_1/vhdlpp/compiler.h000066400000000000000000000023401265551621300166210ustar00rootroot00000000000000#ifndef IVL_compiler_H #define IVL_compiler_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include const int GN_KEYWORD_2008 = 0x0001; // TRUE if processing is supposed to dump progress to stderr. extern bool verbose_flag; extern bool debug_elaboration; extern std::ofstream debug_log_file; extern StringHeapLex lex_strings; extern StringHeapLex filename_strings; #endif /* IVL_compiler_H */ iverilog-10_1/vhdlpp/debug.cc000066400000000000000000000334131265551621300162400ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * Copyright (c) 2014 CERN * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Picture Elements, Inc., 777 Panoramic Way, Berkeley, CA 94704. */ # include "entity.h" # include "architec.h" # include "expression.h" # include "parse_types.h" # include "subprogram.h" # include "sequential.h" # include "vsignal.h" # include "vtype.h" # include # include # include static ostream& operator << (ostream&out, port_mode_t that) { switch (that) { case PORT_NONE: out << "NO-PORT"; break; case PORT_IN: out << "IN"; break; case PORT_OUT: out << "OUT"; break; default: out << "PORT-????"; break; } return out; } void dump_design_entities(ostream&file) { for (map::iterator cur = design_entities.begin() ; cur != design_entities.end() ; ++cur) { cur->second->dump(file); } } void ComponentBase::dump_generics(ostream&out, int indent) const { if (parms_.empty()) { out << setw(indent) << "" << "No generics" << endl; } else { out << setw(indent) << "" << "GENERICS:" << endl; for (vector::const_iterator cur = parms_.begin() ; cur != parms_.end() ; ++cur) { InterfacePort*item = *cur; out << setw(indent+2) << "" << item->name << " : " << item->mode << ", type="; if (item->type) item->type->show(out); else out << ""; out << ", file=" << item->get_fileline() << endl; } } } void ComponentBase::dump_ports(ostream&out, int indent) const { if (ports_.empty()) { out << setw(indent) << "" << "No ports" << endl; } else { out << setw(indent) << "" << "PORTS:" << endl; for (vector::const_iterator cur = ports_.begin() ; cur != ports_.end() ; ++cur) { InterfacePort*item = *cur; out << setw(indent+2) << "" << item->name << " : " << item->mode << ", type="; if (item->type) item->type->show(out); else out << ""; out << ", file=" << item->get_fileline() << endl; } } } void Scope::dump_scope(ostream&out) const { // Dump types out << " -- imported types" << endl; for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { out << " " << cur->first << ": "; cur->second->show(out); out << endl; } out << " -- Types from this scope" << endl; for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { out << " " << cur->first << ": "; cur->second->show(out); out << endl; } // Dump constants out << " -- imported constants" << endl; for (map::const_iterator cur = use_constants_.begin() ; cur != use_constants_.end() ; ++cur) { out << " constant " << cur->first << " = "; out << endl; } out << " -- Constants from this scope" << endl; for (map::const_iterator cur = cur_constants_.begin() ; cur != cur_constants_.end() ; ++cur) { out << " constant " << cur->first << " = "; out << endl; } // Dump signal declarations out << " -- Signals" << endl; for (map::const_iterator cur = old_signals_.begin() ; cur != old_signals_.end() ; ++cur) { if (cur->second) cur->second->dump(out, 3); else out << " signal " << cur->first.str() << ": ???" << endl; } for (map::const_iterator cur = new_signals_.begin() ; cur != new_signals_.end() ; ++cur) { if (cur->second) cur->second->dump(out, 3); else out << " signal " << cur->first.str() << ": ???" << endl; } // Dump subprograms out << " -- Imported Subprograms" << endl; for (map::const_iterator cur = use_subprograms_.begin() ; cur != use_subprograms_.end() ; ++cur) { out << " subprogram " << cur->first << " is" << endl; cur->second->dump(out); if(cur->second->body()) cur->second->body()->dump(out); out << " end subprogram " << cur->first << endl; } out << " -- Subprograms from this scope" << endl; for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { out << " subprogram " << cur->first << " is" << endl; cur->second->dump(out); if(cur->second->body()) cur->second->body()->dump(out); out << " end subprogram " << cur->first << endl; } // Dump component declarations out << " -- Components" << endl; for (map::const_iterator cur = old_components_.begin() ; cur != old_components_.end() ; ++cur) { out << " component " << cur->first << " is" << endl; cur->second->dump_generics(out); cur->second->dump_ports(out); out << " end component " << cur->first << endl; } for (map::const_iterator cur = new_components_.begin() ; cur != new_components_.end() ; ++cur) { out << " component " << cur->first << " is" << endl; cur->second->dump_generics(out); cur->second->dump_ports(out); out << " end component " << cur->first << endl; } } void Entity::dump(ostream&out, int indent) const { out << setw(indent) << "" << "entity " << get_name() << " file=" << get_fileline() << endl; dump_ports(out, indent+2); for (map::const_iterator cur = arch_.begin() ; cur != arch_.end() ; ++cur) { cur->second->dump(out, get_name(), indent); } } void SigVarBase::dump(ostream&out, int indent) const { out << setw(indent) << "" << "signal/variable " << name_ << " is "; if (type_) out << *type_; else out << "?NO TYPE?"; out << endl; } void Expression::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Expression [" << typeid(*this).name() << "]" << " at " << get_fileline()<< endl; } void ExpAggregate::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Aggregate expression at " << get_fileline() << endl; for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) elements_[idx]->dump(out, indent+2); } void ExpAggregate::element_t::dump(ostream&out, int indent) const { out << setw(indent) << "" << "choices:" << endl; for (size_t idx = 0 ; idx < fields_.size() ; idx += 1) fields_[idx]->dump(out, indent+4); out << setw(indent) << "" << "expression:" << endl; val_->dump(out, indent+4); } void ExpAggregate::choice_t::dump(ostream&out, int indent) const { if (others()) { out << setw(indent) << "" << "=> others" << endl; return; } if (expr_.get()) { expr_->dump(out, indent); return; } if (range_.get()) { range_->dump(out, indent); return; } out << setw(indent) << "" << "?choice_t?" << endl; } void ExpAttribute::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Attribute " << name_ << " at " << get_fileline() << endl; base_->dump(out, indent+4); } void ExpBinary::dump_operands(ostream&out, int indent) const { operand1_->dump(out, indent); operand2_->dump(out, indent); } void ExpBitstring::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Bit string " << value_.size() << "b\""; for (size_t idx = value_.size() ; idx > 0 ; idx -= 1) { out << value_[idx-1]; } out << "\"" << endl; } void ExpCharacter::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Character '" << value_ << "'" << " at " << get_fileline() << endl; } void ExpConditional::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Conditional expression at "<< get_fileline() << endl; for (list::const_iterator cur = options_.begin() ; cur != options_.end() ; ++cur) { (*cur)->dump(out, indent); } } void ExpConditional::case_t::dump(ostream&out, int indent) const { out << setw(indent) << "" << "when:" << endl; if (cond_) cond_->dump(out, indent+4); out << setw(indent) << "" << "do:" << endl; for (list::const_iterator cur = true_clause_.begin() ; cur != true_clause_.end() ; ++cur) { (*cur)->dump(out, indent+4); } } void ExpEdge::dump(ostream&out, int indent) const { out << setw(indent) << ""; switch (fun_) { case NEGEDGE: out << "negedge "; break; case ANYEDGE: out << "ANYedge "; break; case POSEDGE: out << "posedge "; } out << "at " << get_fileline() << endl; dump_operand1(out, indent+3); } void ExpFunc::dump(ostream&out, int indent) const { out << setw(indent) << "" << "function " << name_ << " has " << argv_.size() << " arguments:" << endl; for (size_t idx = 0 ; idx < argv_.size() ; idx += 1) { argv_[idx]->dump(out, indent+2); } } void ExpInteger::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Integer " << value_ << " at " << get_fileline() << endl; } void ExpReal::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Integer " << value_ << " at " << get_fileline() << endl; } void ExpLogical::dump(ostream&out, int indent) const { const char*fun_name = "?"; switch (fun_) { case AND: fun_name = "AND"; break; case OR: fun_name = "OR"; break; case NAND: fun_name = "NAND"; break; case NOR: fun_name = "NOR"; break; case XOR: fun_name = "XOR"; break; case XNOR: fun_name = "XNOR"; break; } out << setw(indent) << "" << "Logical " << fun_name << " at " << get_fileline() << endl; dump_operands(out, indent+4); } void ExpName::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ExpName(\"" << name_ << "\")" << " at " << get_fileline() << endl; if (prefix_.get()) prefix_->dump(out, indent+8); if (index_) index_->dump(out, indent+6); if (lsb_) lsb_->dump(out, indent+6); } void ExpNameALL::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ExpNameALL at " << get_fileline() << endl; } void ExpRelation::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Relation "; switch (fun_) { case EQ: out << "="; break; case LT: out << "<"; break; case GT: out << ">"; break; case NEQ: out << "/="; break; case LE: out << "<="; break; case GE: out << ">="; break; } out << endl; dump_operands(out, indent+4); } void ExpString::dump(ostream&out, int indent) const { out << setw(indent) << "" << "String \""; for(vector::const_iterator it = value_.begin(); it != value_.end(); ++it) out << *it; out << "\"" << " at " << get_fileline() << endl; } void ExpUAbs::dump(ostream&out, int indent) const { out << setw(indent) << "" << "abs() at " << get_fileline() << endl; dump_operand1(out, indent+4); } void ExpUnary::dump_operand1(ostream&out, int indent) const { operand1_->dump(out, indent); } void ExpUNot::dump(ostream&out, int indent) const { out << setw(indent) << "" << "not() at " << get_fileline() << endl; dump_operand1(out, indent+4); } void named_expr_t::dump(ostream&out, int indent) const { out << setw(indent) << "" << name_ << "=>"; expr_->dump(out, indent); } void prange_t::dump(ostream&out, int indent) const { left_->dump(out, indent); out << setw(indent) << "" << (direction_ ? "downto" : "to"); right_->dump(out, indent); } ostream& Expression::dump_inline(ostream&out) const { out << typeid(*this).name(); return out; } ostream& ExpInteger::dump_inline(ostream&out) const { out << value_; return out; } ostream& ExpReal::dump_inline(ostream&out) const { out << value_; return out; } void SubprogramBody::dump(ostream&fd) const { if (statements_== 0 || statements_->empty()) { fd << " " << endl; } else { for (list::const_iterator cur = statements_->begin() ; cur != statements_->end() ; ++cur) { SequentialStmt*curp = *cur; curp->dump(fd, 8); } } } void SubprogramHeader::dump(ostream&fd) const { fd << " " << name_; if (ports_->empty()) { fd << "()"; } else { fd << "("; list::const_iterator cur = ports_->begin(); InterfacePort*curp = *cur; fd << curp->name << ":"; curp->type->show(fd); for (++ cur ; cur != ports_->end() ; ++ cur) { curp = *cur; fd << "; " << curp->name << ":"; curp->type->show(fd); } fd << ")"; } fd << " return "; return_type_->show(fd); fd << endl; } iverilog-10_1/vhdlpp/entity.cc000066400000000000000000000052361265551621300164700ustar00rootroot00000000000000 /* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "entity.h" # include "architec.h" # include using namespace std; std::map design_entities; ComponentBase::ComponentBase(perm_string name) : name_(name) { } ComponentBase::~ComponentBase() { for(std::vector::iterator it = ports_.begin() ; it != ports_.end(); ++it) delete *it; } void ComponentBase::set_interface(std::list*parms, std::list*ports) { if (parms) { while (! parms->empty()) { parms_.push_back(parms->front()); parms->pop_front(); } } if (ports) { while (! ports->empty()) { ports_.push_back(ports->front()); ports->pop_front(); } } } const InterfacePort* ComponentBase::find_port(perm_string my_name) const { for (size_t idx = 0 ; idx < ports_.size() ; idx += 1) { if (ports_[idx]->name == my_name) return ports_[idx]; } return 0; } const InterfacePort* ComponentBase::find_generic(perm_string my_name) const { for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]->name == my_name) return parms_[idx]; } return 0; } Entity::Entity(perm_string name) : ComponentBase(name) { bind_arch_ = 0; } Entity::~Entity() { for(map::reverse_iterator it = arch_.rbegin() ; it != arch_.rend(); ++it) delete it->second; } Architecture* Entity::add_architecture(Architecture*that) { if (Architecture*tmp = arch_ [that->get_name()]) { return tmp; } return arch_[that->get_name()] = that; } void Entity::set_declaration_l_value(perm_string nam, bool flag) { map::iterator cur = declarations_.find(nam); assert(cur != declarations_.end()); cur->second.reg_flag = flag; } iverilog-10_1/vhdlpp/entity.h000066400000000000000000000117111265551621300163250ustar00rootroot00000000000000#ifndef IVL_entity_H #define IVL_entity_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include "vtype.h" # include "StringHeap.h" # include "LineInfo.h" typedef enum { PORT_NONE=0, PORT_IN, PORT_OUT, PORT_INOUT } port_mode_t; class Architecture; class Expression; class InterfacePort : public LineInfo { public: InterfacePort(port_mode_t mod = PORT_NONE, perm_string nam = empty_perm_string, const VType*typ = NULL, Expression*exp = NULL) : mode(mod), name(nam), type(typ), expr(exp) {} InterfacePort(const VType*typ) : mode(PORT_NONE), type(typ), expr(NULL) {} // Port direction from the source code. port_mode_t mode; // Name of the port from the source code perm_string name; // Name of interface type as given in the source code. const VType*type; // Default value expression (or nil) Expression*expr; }; /* * The ComponentBase class represents the base entity * declaration. When used as is, then this represents a forward * declaration of an entity. Elaboration will match it to a proper * entity. Or this can be the base class for a full-out Entity. */ class ComponentBase : public LineInfo { public: ComponentBase(perm_string name); ~ComponentBase(); // Entities have names. perm_string get_name() const { return name_; } const InterfacePort* find_port(perm_string by_name) const; const InterfacePort* find_generic(perm_string by_name) const; const std::vector& get_generics() const { return parms_; } // Declare the ports for the entity. The parser calls this // method with a list of interface elements that were parsed // for the entity. This method collects those entities, and // empties the list in the process. void set_interface(std::list*parms, std::list*ports); void write_to_stream(std::ostream&fd) const; public: void dump_generics(std::ostream&out, int indent =0) const; void dump_ports(std::ostream&out, int indent = 0) const; private: perm_string name_; protected: std::vector parms_; std::vector ports_; }; /* * Entities are fully declared components. */ class Entity : public ComponentBase { public: Entity(perm_string name); ~Entity(); // bind an architecture to the entity, and return the // Architecture that was bound. If there was a previous // architecture with the same name bound, then do not replace // the architecture and instead return the old // value. The caller can tell that the bind worked if the // returned pointer is the same as the passed pointer. Architecture* add_architecture(Architecture*); // After the architecture is bound, elaboration calls this // method to elaborate this entity. This method arranges for // elaboration to happen all the way through the architecture // that is bound to this entity. int elaborate(); // During elaboration, it may be discovered that a port is // used as an l-value in an assignment. This method tweaks the // declaration to allow for that case. void set_declaration_l_value(perm_string by_name, bool flag); int emit(ostream&out); void dump(ostream&out, int indent = 0) const; private: std::maparch_; Architecture*bind_arch_; map declarations_; int elaborate_generic_exprs_(void); int elaborate_ports_(void); }; /* * As the parser parses entities, it puts them into this map. It uses * a map because sometimes it needs to look back at an entity by name. */ extern std::map design_entities; /* * Elaborate the collected entities, and return the number of * elaboration errors. */ extern int elaborate_entities(void); extern int emit_entities(void); /* * Use this function to dump a description of the design entities to a * file. This is for debug, not for any useful purpose. */ extern void dump_design_entities(ostream&file); #endif /* IVL_entity_H */ iverilog-10_1/vhdlpp/entity_elaborate.cc000066400000000000000000000073361265551621300205110ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "entity.h" # include "compiler.h" # include "architec.h" # include "vtype.h" # include # include # include # include # include # include using namespace std; int elaborate_entities(void) { int errors = 0; for (map::iterator cur = design_entities.begin() ; cur != design_entities.end() ; ++cur) { errors += cur->second->elaborate(); } return errors; } int Entity::elaborate() { int errors = 0; if (verbose_flag) cerr << "Elaborate entity " << get_name() << "..." << endl; if (arch_.empty()) { cerr << get_fileline() << ": error: " << "No architectures to choose from for entity " << get_name() << "." << endl; return 1; } /* FIXME: the architecture for the entity should be chosen in configuration block (not yet implemented). Multiple architectures are allowed in general */ if (arch_.size() > 1) { cerr << get_fileline() << ": sorry: " << "Multiple architectures for an entity are not yet supported" << ". Architectures for entity " << get_name() << " are:" << endl; for (map::const_iterator cur = arch_.begin() ; cur != arch_.end() ; ++cur) { cerr << get_fileline() << ": : " << cur->first << " at " << cur->second->get_fileline() << endl; } //errors += 1; } /* FIXME: here we should look at configuration block */ bind_arch_ = arch_.begin()->second; if (verbose_flag) cerr << "For entity " << get_name() << ", choosing architecture " << bind_arch_->get_name() << "." << endl; errors += elaborate_generic_exprs_(); errors += elaborate_ports_(); errors += bind_arch_->elaborate(this); return errors; } int Entity::elaborate_generic_exprs_() { int errors = 0; for (vector::const_iterator cur = parms_.begin() ; cur != parms_.end() ; ++cur) { InterfacePort*curp = *cur; if(curp->expr) curp->expr->elaborate_expr(this, 0, curp->type); } return errors; } int Entity::elaborate_ports_(void) { int errors = 0; for (std::vector::const_iterator cur = ports_.begin() ; cur != ports_.end() ; ++cur) { InterfacePort*cur_port = *cur; const VType*type = cur_port->type; if (type == 0) { cerr << get_fileline() << ": error: " << "Giving up on unknown type for port " << cur_port->name << "." << endl; errors += 1; continue; } // Elaborate the type to elaborate any expressions that // are used by the types. errors += type->elaborate(this, bind_arch_); VType::decl_t cur_decl; cur_decl.type = type; declarations_[cur_port->name] = cur_decl; } return errors; } iverilog-10_1/vhdlpp/entity_emit.cc000066400000000000000000000054501265551621300175040ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "entity.h" # include "architec.h" # include # include # include # include int emit_entities(void) { int errors = 0; for (map::iterator cur = design_entities.begin() ; cur != design_entities.end() ; ++cur) { errors += cur->second->emit(cout); } return errors; } int Entity::emit(ostream&out) { int errors = 0; out << "module \\" << get_name() << " "; // If there are generics, emit them if (parms_.size() > 0) { out << "#("; for (vector::const_iterator cur = parms_.begin() ; cur != parms_.end() ; ++cur) { const InterfacePort*curp = *cur; if (cur != parms_.begin()) out << ", "; out << "parameter \\" << curp->name << " = "; if(curp->expr) { errors += curp->expr->emit(out, this, 0); } else { // Unlike VHDL, Verilog module parameter port list // elements are always assignments. Fill in the blank. out << "1'bx"; } } out << ") "; } // If there are ports, emit them. if (ports_.size() > 0) { out << "("; const char*sep = 0; for (vector::const_iterator cur = ports_.begin() ; cur != ports_.end() ; ++cur) { InterfacePort*port = *cur; VType::decl_t&decl = declarations_[port->name]; if (sep) out << sep << endl; else sep = ", "; switch (port->mode) { case PORT_NONE: // Should not happen cerr << get_fileline() << ": error: Undefined port direction." << endl; out << "NO_PORT " << port->name; break; case PORT_IN: out << "input "; break; case PORT_OUT: out << "output "; break; case PORT_INOUT: out << "inout "; break; } errors += decl.emit(out, port->name); } cout << ")"; } out << ";" << endl; errors += bind_arch_->emit(out, this); out << "endmodule" << endl; return errors; } iverilog-10_1/vhdlpp/entity_stream.cc000066400000000000000000000051051265551621300200360ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "entity.h" # include "expression.h" using namespace std; void ComponentBase::write_to_stream(ostream&fd) const { fd << " component " << name_ << " is" << endl; if(!parms_.empty()) { fd << " generic(" << endl; for(vector::const_iterator it = parms_.begin(); it != parms_.end(); ++it) { const InterfacePort*parm = *it; if(it != parms_.begin()) fd << ";"; fd << " " << parm->name << " : "; parm->type->write_to_stream(fd); if(parm->expr) { fd << " := "; parm->expr->write_to_stream(fd); } fd << endl; } fd << " );" << endl; } if(!ports_.empty()) { fd << " port(" << endl; vector::const_iterator cur = ports_.begin(); while (cur != ports_.end()) { InterfacePort*item = *cur; ++cur; fd << " " << item->name << " : "; switch (item->mode) { case PORT_NONE: fd << "???? "; break; case PORT_IN: fd << "in "; break; case PORT_OUT: fd << "out "; break; case PORT_INOUT: fd << "inout "; break; } item->type->write_to_stream(fd); if (cur != ports_.end()) fd << ";" << endl; else fd << endl; } fd << " );" << endl; } fd << " end component;" << endl; } iverilog-10_1/vhdlpp/expression.cc000066400000000000000000000353351265551621300173560ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2015 / Stephen Williams (steve@icarus.com), * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "expression.h" # include "subprogram.h" # include "parse_types.h" # include "scope.h" # include # include # include # include # include using namespace std; Expression::Expression() : type_(0) { } Expression::~Expression() { } void Expression::set_type(const VType*typ) { assert(type_==0 || type_==typ); type_ = typ; } bool Expression::symbolic_compare(const Expression*) const { cerr << get_fileline() << ": internal error: " << "symbolic_compare() method not implemented " << "for " << typeid(*this).name() << endl; return false; } ExpAttribute::ExpAttribute(ExpName*bas, perm_string nam) : base_(bas), name_(nam) { } ExpAttribute::~ExpAttribute() { /* Different attributes can point to the same base so we cannot delete this here. * Look at the vhdl_range test with valgrind to see this issue. */ // delete base_; } Expression*ExpAttribute::clone() const { return new ExpAttribute(static_cast(base_->clone()), name_); } void ExpAttribute::visit(ExprVisitor& func) { base_->visit(func); func(this); } ExpBinary::ExpBinary(Expression*op1, Expression*op2) : operand1_(op1), operand2_(op2) { } ExpBinary::~ExpBinary() { delete operand1_; delete operand2_; } bool ExpBinary::eval_operand1(ScopeBase*scope, int64_t&val) const { return operand1_->evaluate(scope, val); } bool ExpBinary::eval_operand2(ScopeBase*scope, int64_t&val) const { return operand2_->evaluate(scope, val); } void ExpBinary::visit(ExprVisitor& func) { operand1_->visit(func); operand2_->visit(func); func(this); } ExpUnary::ExpUnary(Expression*op1) : operand1_(op1) { } ExpUnary::~ExpUnary() { delete operand1_; } void ExpUnary::visit(ExprVisitor& func) { operand1_->visit(func); func(this); } ExpAggregate::ExpAggregate(std::list*el) : elements_(el? el->size() : 0) { assert(el); size_t idx = 0; while (! el->empty()) { assert(idx < elements_.size()); elements_[idx++] = el->front(); el->pop_front(); } delete el; } ExpAggregate::~ExpAggregate() { for(std::vector::iterator it = elements_.begin(); it != elements_.end(); ++it) { delete *it; } for(std::vector::iterator it = aggregate_.begin(); it != aggregate_.end(); ++it) { delete it->choice; if(!it->alias_flag) delete it->expr; } } Expression* ExpAggregate::clone() const { std::list*new_elements = NULL; if(!elements_.empty()) { new_elements = new std::list(); for(std::vector::const_iterator it = elements_.begin(); it != elements_.end(); ++it) { new_elements->push_back(new element_t(**it)); } } assert(aggregate_.empty()); // cloning should not happen after elab return new ExpAggregate(new_elements); } void ExpAggregate::visit(ExprVisitor& func) { for(std::vector::iterator it = elements_.begin(); it != elements_.end(); ++it) { (*it)->extract_expression()->visit(func); } for(std::vector::iterator it = aggregate_.begin(); it != aggregate_.end(); ++it) { if(Expression*choice_expr = it->choice->simple_expression(false)) choice_expr->visit(func); it->expr->visit(func); } func(this); } ExpAggregate::choice_t::choice_t(Expression*exp) : expr_(exp) { } ExpAggregate::choice_t::choice_t() { } ExpAggregate::choice_t::choice_t(prange_t*rang) : range_(rang) { } ExpAggregate::choice_t::choice_t(const choice_t&other) { if(Expression*e = other.expr_.get()) expr_.reset(e->clone()); if(other.range_.get()) range_.reset(new prange_t(*other.range_.get())); } ExpAggregate::choice_t::~choice_t() { } bool ExpAggregate::choice_t::others() const { return expr_.get() == 0 && range_.get() == 0; } Expression*ExpAggregate::choice_t::simple_expression(bool detach_flag) { Expression*res = detach_flag? expr_.release() : expr_.get(); return res; } prange_t*ExpAggregate::choice_t::range_expressions(void) { return range_.get(); } ExpAggregate::element_t::element_t(list*fields, Expression*val) : fields_(fields? fields->size() : 0), val_(val) { if (fields) { size_t idx = 0; while (! fields->empty()) { assert(idx < fields_.size()); fields_[idx++] = fields->front(); fields->pop_front(); } } } ExpAggregate::element_t::element_t(const ExpAggregate::element_t&other) { fields_.reserve(other.fields_.size()); for(std::vector::const_iterator it = other.fields_.begin(); it != other.fields_.end(); ++it) { fields_.push_back(*it); } val_ = other.val_->clone(); } ExpAggregate::element_t::~element_t() { for (size_t idx = 0 ; idx < fields_.size() ; idx += 1) delete fields_[idx]; delete val_; } ExpArithmetic::ExpArithmetic(ExpArithmetic::fun_t op, Expression*op1, Expression*op2) : ExpBinary(op1, op2), fun_(op) { // The xCONCAT type is not actually used. assert(op != xCONCAT); } ExpArithmetic::~ExpArithmetic() { } /* * Store bitstrings in little-endian order. */ ExpBitstring::ExpBitstring(const char*val) : value_(strlen(val)) { for (size_t idx = value_.size() ; idx > 0 ; idx -= 1) value_[idx-1] = *val++; } ExpBitstring::~ExpBitstring() { } ExpCharacter::ExpCharacter(char val) : value_(val) { } ExpCharacter::~ExpCharacter() { } ExpConcat::ExpConcat(Expression*op1, Expression*op2) : operand1_(op1), operand2_(op2) { } ExpConcat::~ExpConcat() { delete operand1_; delete operand2_; } void ExpConcat::visit(ExprVisitor& func) { operand1_->visit(func); operand2_->visit(func); func(this); } ExpConditional::ExpConditional(Expression*co, list*tru, list*options) { if(co && tru) options_.push_back(new case_t(co, tru)); if(options) options_.splice(options_.end(), *options); } ExpConditional::~ExpConditional() { while (!options_.empty()) { case_t*tmp = options_.front(); options_.pop_front(); delete tmp; } } Expression*ExpConditional::clone() const { std::list*new_options = NULL; if(!options_.empty()) { new_options = new std::list(); for(std::list::const_iterator it = options_.begin(); it != options_.end(); ++it) { new_options->push_back(new case_t(**it)); } } return new ExpConditional(NULL, NULL, new_options); } void ExpConditional::visit(ExprVisitor& func) { for(std::list::iterator it = options_.begin(); it != options_.end(); ++it) { (*it)->visit(func); } func(this); } ExpConditional::case_t::case_t(Expression*cond, std::list*tru) : cond_(cond) { if (tru) true_clause_.splice(true_clause_.end(), *tru); } ExpConditional::case_t::case_t(const case_t&other) : LineInfo(other) { cond_ = other.cond_->clone(); for(std::list::const_iterator it = other.true_clause_.begin(); it != other.true_clause_.end(); ++it) { true_clause_.push_back((*it)->clone()); } } ExpConditional::case_t::~case_t() { delete cond_; while (! true_clause_.empty()) { Expression*tmp = true_clause_.front(); true_clause_.pop_front(); delete tmp; } } ExpSelected::ExpSelected(Expression*selector, std::list*options) : ExpConditional(NULL, NULL, options), selector_(selector) { // Currently condition field contains only value, // so substitute it with a comparison to create a valid condition for(std::list::iterator it = options_.begin(); it != options_.end(); ++it) { Expression*cond = (*it)->condition(); if(cond) (*it)->set_condition(new ExpRelation(ExpRelation::EQ, selector_->clone(), cond)); } } ExpSelected::~ExpSelected() { } Expression*ExpSelected::clone() const { std::list*new_options = NULL; if(!options_.empty()) { new_options = new std::list(); for(std::list::const_iterator it = options_.begin(); it != options_.end(); ++it) { new_options->push_back(new case_t(**it)); } } return new ExpSelected(selector_->clone(), new_options); } void ExpConditional::case_t::visit(ExprVisitor& func) { if(cond_) func(cond_); for(std::list::iterator it = true_clause_.begin(); it != true_clause_.end(); ++it) { func(*it); } } ExpEdge::ExpEdge(ExpEdge::fun_t typ, Expression*op) : ExpUnary(op), fun_(typ) { } ExpEdge::~ExpEdge() { } ExpFunc::ExpFunc(perm_string nn) : name_(nn), def_(0) { } ExpFunc::ExpFunc(perm_string nn, list*args) : name_(nn), argv_(args->size()), def_(0) { for (size_t idx = 0; idx < argv_.size() ; idx += 1) { ivl_assert(*this, !args->empty()); argv_[idx] = args->front(); args->pop_front(); } ivl_assert(*this, args->empty()); } ExpFunc::~ExpFunc() { for (size_t idx = 0 ; idx < argv_.size() ; idx += 1) delete argv_[idx]; } Expression*ExpFunc::clone() const { std::list*new_args = NULL; if(!argv_.empty()) { new_args = new std::list(); for(std::vector::const_iterator it = argv_.begin(); it != argv_.end(); ++it) new_args->push_back((*it)->clone()); } ExpFunc*f = new ExpFunc(name_, new_args); f->def_ = def_; return f; } void ExpFunc::visit(ExprVisitor& func) { if(!argv_.empty()) { for(std::vector::iterator it = argv_.begin(); it != argv_.end(); ++it) (*it)->visit(func); } func(this); } const VType* ExpFunc::func_ret_type() const { return def_ ? def_->peek_return_type() : NULL; } ExpInteger::ExpInteger(int64_t val) : value_(val) { } ExpInteger::~ExpInteger() { } bool ExpInteger::evaluate(ScopeBase*, int64_t&val) const { val = value_; return true; } ExpReal::ExpReal(double val) : value_(val) { } ExpReal::~ExpReal() { } ExpLogical::ExpLogical(ExpLogical::fun_t ty, Expression*op1, Expression*op2) : ExpBinary(op1, op2), fun_(ty) { } ExpLogical::~ExpLogical() { } ExpName::ExpName(perm_string nn) : name_(nn), index_(0), lsb_(0) { } ExpName::ExpName(perm_string nn, list*indices) : name_(nn), index_(0), lsb_(0) { /* For now, assume a single index. */ ivl_assert(*this, indices->size() == 1); index_ = indices->front(); indices->pop_front(); } ExpName::ExpName(perm_string nn, Expression*msb, Expression*lsb) : name_(nn), index_(msb), lsb_(lsb) { } ExpName::ExpName(ExpName*prefix, perm_string nn) : prefix_(prefix), name_(nn), index_(0), lsb_(0) { } ExpName::ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb) : prefix_(prefix), name_(nn), index_(msb), lsb_(lsb) { } ExpName::~ExpName() { delete index_; delete lsb_; } bool ExpName::symbolic_compare(const Expression*that) const { const ExpName*that_name = dynamic_cast (that); if (that_name == 0) return false; if (name_ != that_name->name_) return false; if (that_name->index_ && !index_) return false; if (index_ && !that_name->index_) return false; if (index_) { assert(that_name->index_); return index_->symbolic_compare(that_name->index_); } return true; } void ExpName::set_range(Expression*msb, Expression*lsb) { assert(index_==0); index_ = msb; assert(lsb_==0); lsb_ = lsb; } void ExpName::visit(ExprVisitor& func) { if(prefix_.get()) prefix_.get()->visit(func); if(index_) index_->visit(func); if(lsb_) lsb_->visit(func); func(this); } int ExpName::index_t::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "("; if(idx_ && size_) { errors += idx_->emit(out, ent, scope); out << "*"; errors += size_->emit(out, ent, scope); } if(offset_) { if(idx_ && size_) out << "+"; errors += offset_->emit(out, ent, scope); } out << ")"; return errors; } ExpRelation::ExpRelation(ExpRelation::fun_t ty, Expression*op1, Expression*op2) : ExpBinary(op1, op2), fun_(ty) { } ExpRelation::~ExpRelation() { } ExpShift::ExpShift(ExpShift::shift_t op, Expression*op1, Expression*op2) : ExpBinary(op1, op2), shift_(op) { } ExpString::ExpString(const char* value) : value_(strlen(value)) { for(size_t idx = 0; idx < value_.size(); idx += 1) value_[idx] = value[idx]; } ExpString::~ExpString() { } ExpUAbs::ExpUAbs(Expression*op1) : ExpUnary(op1) { } ExpUAbs::~ExpUAbs() { } ExpUNot::ExpUNot(Expression*op1) : ExpUnary(op1) { } ExpUNot::~ExpUNot() { } ExpCast::ExpCast(Expression*base, const VType*type) : base_(base), type_(type) { } ExpCast::~ExpCast() { } void ExpCast::visit(ExprVisitor& func) { base_->visit(func); func(this); } ExpNew::ExpNew(Expression*size) : size_(size) { } ExpNew::~ExpNew() { delete size_; } void ExpNew::visit(ExprVisitor& func) { size_->visit(func); func(this); } ExpTime::ExpTime(uint64_t amount, timeunit_t unit) : amount_(amount), unit_(unit) { } double ExpTime::to_fs() const { double val = amount_; switch(unit_) { case FS: break; case PS: val *= 1e3; break; case NS: val *= 1e6; break; case US: val *= 1e9; break; case MS: val *= 1e12; break; case S: val *= 1e15; break; default: ivl_assert(*this, false); break; } return val; } iverilog-10_1/vhdlpp/expression.h000066400000000000000000000727731265551621300172270ustar00rootroot00000000000000#ifndef IVL_expression_H #define IVL_expression_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2015 / Stephen Williams (steve@icarus.com), * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include "LineInfo.h" # include "entity.h" # include # include # include # include class prange_t; class Entity; class ScopeBase; class SubprogramHeader; class VType; class VTypeArray; class VTypePrimitive; class ExpName; struct ExprVisitor { virtual ~ExprVisitor() {}; virtual void operator() (Expression*s) = 0; }; /* * The Expression class represents parsed expressions from the parsed * VHDL input. The Expression class is a virtual class that holds more * specific derived expression types. */ class Expression : public LineInfo { public: Expression(); virtual ~Expression() =0; // Returns a deep copy of the expression. virtual Expression*clone() const =0; // This virtual method handles the special case of elaborating // an expression that is the l-value of a sequential variable // assignment. This generates an error for most cases, but // expressions that are valid l-values return 0 and set any // flags needed to indicate their status as writable variables. virtual int elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ); // This virtual method probes the expression to get the most // constrained type for the expression. For a given instance, // this may be called before the elaborate_expr method. virtual const VType*probe_type(Entity*ent, ScopeBase*scope) const; // The fit_type virtual method is used by the ExpConcat class // to probe the type of operands. The atype argument is the // type of the ExpConcat expression itself. This expression // returns its type as interpreted in this context. Really, // this is mostly about helping aggregate expressions within // concatenations to figure out their type. virtual const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; // This virtual method elaborates an expression. The ltype is // the type of the lvalue expression, if known, and can be // used to calculate the type for the expression being // elaborated. virtual int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); // Return the type that this expression would be if it were an // l-value. This should only be called after elaborate_lval is // called and only if elaborate_lval succeeded. inline const VType*peek_type(void) const { return type_; } // This virtual method writes a VHDL-accurate representation // of this expression to the designated stream. This is used // for writing parsed types to library files. virtual void write_to_stream(std::ostream&fd) const =0; // The emit virtual method is called by architecture emit to // output the generated code for the expression. The derived // class fills in the details of what exactly happened. virtual int emit(ostream&out, Entity*ent, ScopeBase*scope) =0; // The emit_package virtual message is similar, but is called // in a package context and to emit SV packages. virtual int emit_package(std::ostream&out); // The evaluate virtual method tries to evaluate expressions // to constant literal values. Return true and set the val // argument if the evaluation works, or return false if it // cannot be done. virtual bool evaluate(ScopeBase*scope, int64_t&val) const; virtual bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; // The symbolic compare returns true if the two expressions // are equal without actually calculating the value. virtual bool symbolic_compare(const Expression*that) const; // This method returns true if the drawn Verilog for this // expression is a primary. A containing expression can use // this method to know if it needs to wrap parentheses. This // is somewhat optional, so it is better to return false if // not certain. The default implementation does return false. virtual bool is_primary(void) const; // Debug dump of the expression. virtual void dump(ostream&out, int indent = 0) const =0; virtual ostream& dump_inline(ostream&out) const; // Recursively visits a tree of expressions (useful of complex expressions). virtual void visit(ExprVisitor& func) { func(this); } protected: // This function is called by the derived class during // elaboration to set the type of the current expression that // elaboration assigns to this expression. void set_type(const VType*); private: const VType*type_; private: // Not implemented Expression(const Expression&); Expression& operator = (const Expression&); }; /* * Checks before cloning if the other expression actually exists (!=NULL). */ static inline Expression*safe_clone(const Expression*other) { return (other ? other->clone() : NULL); } static inline void FILE_NAME(Expression*tgt, const LineInfo*src) { tgt->set_line(*src); } static inline ostream& operator <<(ostream&out, const Expression&exp) { return exp.dump_inline(out); } class ExpUnary : public Expression { public: ExpUnary(Expression*op1); virtual ~ExpUnary() =0; inline const Expression*peek_operand() const { return operand1_; } const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void visit(ExprVisitor& func); protected: inline void write_to_stream_operand1(std::ostream&fd) const { operand1_->write_to_stream(fd); } int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope); void dump_operand1(ostream&out, int indent = 0) const; private: Expression*operand1_; }; /* * This is an abstract class that collects some of the common features * of binary operators. */ class ExpBinary : public Expression { public: ExpBinary(Expression*op1, Expression*op2); virtual ~ExpBinary() =0; inline const Expression* peek_operand1(void) const { return operand1_; } inline const Expression* peek_operand2(void) const { return operand2_; } const VType*probe_type(Entity*ent, ScopeBase*scope) const; void visit(ExprVisitor& func); protected: int elaborate_exprs(Entity*, ScopeBase*, const VType*); int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope); int emit_operand2(ostream&out, Entity*ent, ScopeBase*scope); bool eval_operand1(ScopeBase*scope, int64_t&val) const; bool eval_operand2(ScopeBase*scope, int64_t&val) const; inline void write_to_stream_operand1(std::ostream&out) const { operand1_->write_to_stream(out); } inline void write_to_stream_operand2(std::ostream&out) const { operand2_->write_to_stream(out); } void dump_operands(ostream&out, int indent = 0) const; private: virtual const VType*resolve_operand_types_(const VType*t1, const VType*t2) const; private: Expression*operand1_; Expression*operand2_; }; class ExpAggregate : public Expression { public: // A "choice" is only part of an element. It is the thing that // is used to identify an element of the aggregate. It can // represent the index (or range) of an array, or the name of // a record member. class choice_t { public: // Create an "others" choice choice_t(); // Create a simple_expression choice explicit choice_t(Expression*exp); // Create a named choice explicit choice_t(perm_string name); // discreate_range choice explicit choice_t(prange_t*ran); choice_t(const choice_t&other); ~choice_t(); // true if this represents an "others" choice bool others() const; // Return expression if this represents a simple_expression. Expression*simple_expression(bool detach_flag =true); // Return prange_t if this represents a range_expression prange_t*range_expressions(void); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: std::auto_ptrexpr_; std::auto_ptr range_; private: // not implemented choice_t& operator= (const choice_t&); }; struct choice_element { choice_element() : choice(), expr() {} choice_element(const choice_element&other) { choice = other.choice ? new choice_t(*other.choice) : NULL; expr = safe_clone(other.expr); } choice_t*choice; Expression*expr; bool alias_flag; }; // Elements are the syntactic items in an aggregate // expression. Each element expressions a bunch of fields // (choices) and binds them to a single expression class element_t { public: explicit element_t(std::list*fields, Expression*val); element_t(const element_t&other); ~element_t(); size_t count_choices() const { return fields_.size(); } void map_choices(choice_element*dst); inline Expression* extract_expression() { return val_; } void write_to_stream(std::ostream&fd) const; void dump(ostream&out, int indent) const; private: std::vectorfields_; Expression*val_; private: // not implemented element_t& operator = (const element_t&); }; public: ExpAggregate(std::list*el); ~ExpAggregate(); Expression*clone() const; const VType*probe_type(Entity*ent, ScopeBase*scope) const; const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: int elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*ltype); int elaborate_expr_record_(Entity*ent, ScopeBase*scope, const VTypeRecord*ltype); int emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*ltype); int emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*ltype); private: // This is the elements as directly parsed. std::vector elements_; // These are the elements after elaboration. This form is // easier to check and emit. std::vector aggregate_; }; class ExpArithmetic : public ExpBinary { public: enum fun_t { PLUS, MINUS, MULT, DIV, MOD, REM, POW, xCONCAT }; public: ExpArithmetic(ExpArithmetic::fun_t op, Expression*op1, Expression*op2); ~ExpArithmetic(); Expression*clone() const { return new ExpArithmetic(fun_, peek_operand1()->clone(), peek_operand2()->clone()); } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); virtual bool evaluate(ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: const VType* resolve_operand_types_(const VType*t1, const VType*t2) const; private: fun_t fun_; }; class ExpAttribute : public Expression { public: ExpAttribute(ExpName*base, perm_string name); ~ExpAttribute(); Expression*clone() const; inline perm_string peek_attribute() const { return name_; } inline const ExpName* peek_base() const { return base_; } const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); // Some attributes can be evaluated at compile time bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: ExpName*base_; perm_string name_; }; class ExpBitstring : public Expression { public: explicit ExpBitstring(const char*); ExpBitstring(const ExpBitstring&other) : Expression() { value_ = other.value_; } ~ExpBitstring(); Expression*clone() const { return new ExpBitstring(*this); } const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; private: std::vectorvalue_; }; class ExpCharacter : public Expression { public: ExpCharacter(char val); ExpCharacter(const ExpCharacter&other) : Expression() { value_ = other.value_; } ~ExpCharacter(); Expression*clone() const { return new ExpCharacter(*this); } const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; char value() const { return value_; } private: int emit_primitive_bit_(ostream&out, Entity*ent, ScopeBase*scope, const VTypePrimitive*etype); private: char value_; }; class ExpConcat : public Expression { public: ExpConcat(Expression*op1, Expression*op2); ~ExpConcat(); Expression*clone() const { return new ExpConcat(operand1_->clone(), operand2_->clone()); } const VType*probe_type(Entity*ent, ScopeBase*scope) const; const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); virtual bool evaluate(ScopeBase*scope, int64_t&val) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: int elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*ltype); private: Expression*operand1_; Expression*operand2_; }; /* * The conditional expression represents the VHDL when-else * expressions. Note that by the VHDL syntax rules, these cannot show * up other than at the root of an expression. */ class ExpConditional : public Expression { public: class case_t : public LineInfo { public: case_t(Expression*cond, std::list*tru); case_t(const case_t&other); ~case_t(); inline Expression*condition() { return cond_; } inline void set_condition(Expression*cond) { cond_ = cond; } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*lt); int emit_option(ostream&out, Entity*ent, ScopeBase*scope); int emit_default(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; std::list& extract_true_clause() { return true_clause_; } void visit(ExprVisitor& func); private: Expression*cond_; std::list true_clause_; }; public: ExpConditional(Expression*cond, std::list*tru, std::list*options); virtual ~ExpConditional(); virtual Expression*clone() const; const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); protected: std::list options_; }; /* * Expression to handle selected assignments (with .. select target <= value when ..) */ class ExpSelected : public ExpConditional { public: ExpSelected(Expression*selector, std::list*options); ~ExpSelected(); Expression*clone() const; private: Expression*selector_; }; /* * This is a special expression type that represents posedge/negedge * expressions in sensitivity lists. */ class ExpEdge : public ExpUnary { public: enum fun_t { NEGEDGE, ANYEDGE, POSEDGE }; public: explicit ExpEdge(ExpEdge::fun_t ty, Expression*op); ~ExpEdge(); Expression*clone() const { return new ExpEdge(fun_, peek_operand()->clone()); } inline fun_t edge_fun() const { return fun_; } void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; private: fun_t fun_; }; class ExpFunc : public Expression { public: explicit ExpFunc(perm_string nn); ExpFunc(perm_string nn, std::list*args); ~ExpFunc(); Expression*clone() const; const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; inline perm_string func_name() const { return name_; } inline size_t func_args() const { return argv_.size(); } inline const Expression*func_arg(size_t idx) const { return argv_[idx]; } const VType*func_ret_type() const; public: // Base methods const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); // NOTE: does not handle expressions in subprogram private: perm_string name_; std::vector argv_; SubprogramHeader*def_; }; class ExpInteger : public Expression { public: ExpInteger(int64_t val); ExpInteger(const ExpInteger&other) : Expression(), value_(other.value_) {} ~ExpInteger(); Expression*clone() const { return new ExpInteger(*this); } const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); int emit_package(std::ostream&out); bool is_primary(void) const { return true; } bool evaluate(ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; virtual ostream& dump_inline(ostream&out) const; private: int64_t value_; }; class ExpReal : public Expression { public: ExpReal(double val); ExpReal(const ExpReal&other) : Expression(), value_(other.value_) {} ~ExpReal(); Expression*clone() const { return new ExpReal(*this); } const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); int emit_package(std::ostream&out); bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; virtual ostream& dump_inline(ostream&out) const; private: double value_; }; class ExpLogical : public ExpBinary { public: enum fun_t { AND, OR, NAND, NOR, XOR, XNOR }; public: ExpLogical(ExpLogical::fun_t ty, Expression*op1, Expression*op2); ~ExpLogical(); Expression*clone() const { return new ExpLogical(fun_, peek_operand1()->clone(), peek_operand2()->clone()); } inline fun_t logic_fun() const { return fun_; } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; private: fun_t fun_; }; /* * The ExpName class represents an expression that is an identifier or * other sort of name. The ExpNameALL is a special case of ExpName * that represents the "all" keyword is contexts that can handle it. */ class ExpName : public Expression { public: explicit ExpName(perm_string nn); ExpName(perm_string nn, std::list*indices); ExpName(perm_string nn, Expression*msb, Expression*lsb); ExpName(ExpName*prefix, perm_string nn); ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb); ~ExpName(); public: // Base methods Expression*clone() const { return new ExpName(static_cast(safe_clone(prefix_.get())), name_, safe_clone(index_), safe_clone(lsb_)); } int elaborate_lval(Entity*ent, ScopeBase*scope, bool); int elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*); const VType* probe_type(Entity*ent, ScopeBase*scope) const; const VType* fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*host) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); bool is_primary(void) const; bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; bool symbolic_compare(const Expression*that) const; void dump(ostream&out, int indent = 0) const; inline const char* name() const { return name_; } inline const perm_string& peek_name() const { return name_; } void set_range(Expression*msb, Expression*lsb); void visit(ExprVisitor& func); private: class index_t { public: index_t(Expression*idx, Expression*size, Expression*offset = NULL) : idx_(idx), size_(size), offset_(offset) {} ~index_t() { delete idx_; delete size_; delete offset_; } int emit(ostream&out, Entity*ent, ScopeBase*scope); private: Expression*idx_; Expression*size_; Expression*offset_; }; const VType* elaborate_adjust_type_with_range_(Entity*ent, ScopeBase*scope, const VType*type); int elaborate_lval_(Entity*ent, ScopeBase*scope, bool, ExpName*suffix); const VType* probe_prefix_type_(Entity*ent, ScopeBase*scope) const; const VType* probe_prefixed_type_(Entity*ent, ScopeBase*scope) const; int emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope); // There are some workarounds required for constant arrays/records, as // they are currently emitted as flat localparams (without any type // information). The following workarounds adjust the access indices // to select appropriate parts of the localparam. bool try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, list&indices, int&data_size); bool check_const_array_workaround_(const VTypeArray*arr, ScopeBase*scope, list&indices, int&data_size) const; bool check_const_record_workaround_(const VTypeRecord*rec, ScopeBase*scope, list&indices, int&data_size) const; int emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope, const list&indices, int field_size); private: std::auto_ptr prefix_; perm_string name_; Expression*index_; Expression*lsb_; }; class ExpNameALL : public ExpName { public: ExpNameALL() : ExpName(perm_string()) { } public: int elaborate_lval(Entity*ent, ScopeBase*scope, bool); const VType* probe_type(Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent =0) const; }; class ExpRelation : public ExpBinary { public: enum fun_t { EQ, LT, GT, NEQ, LE, GE }; inline fun_t relation_fun(void) const { return fun_; } public: ExpRelation(ExpRelation::fun_t ty, Expression*op1, Expression*op2); ~ExpRelation(); Expression*clone() const { return new ExpRelation(fun_, peek_operand1()->clone(), peek_operand2()->clone()); } const VType* probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; private: fun_t fun_; }; class ExpShift : public ExpBinary { public: enum shift_t { SRL, SLL, SRA, SLA, ROL, ROR }; public: ExpShift(ExpShift::shift_t op, Expression*op1, Expression*op2); Expression*clone() const { return new ExpShift(shift_, peek_operand1()->clone(), peek_operand2()->clone()); } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); virtual bool evaluate(ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: shift_t shift_; }; class ExpString : public Expression { public: explicit ExpString(const char*); ExpString(const ExpString&other) : Expression(), value_(other.value_) {} ~ExpString(); Expression*clone() const { return new ExpString(*this); } const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; const std::vector& get_value() const { return value_; } private: int emit_as_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*arr); private: std::vector value_; }; class ExpUAbs : public ExpUnary { public: ExpUAbs(Expression*op1); ~ExpUAbs(); Expression*clone() const { return new ExpUAbs(peek_operand()->clone()); } void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; }; class ExpUNot : public ExpUnary { public: ExpUNot(Expression*op1); ~ExpUNot(); Expression*clone() const { return new ExpUNot(peek_operand()->clone()); } void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; }; /* * Class that wraps other expressions to cast them to other types. */ class ExpCast : public Expression { public: ExpCast(Expression*base, const VType*type); ~ExpCast(); Expression*clone() const { return new ExpCast(base_->clone(), type_->clone()); } inline int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { return base_->elaborate_expr(ent, scope, type_); } void write_to_stream(std::ostream&fd) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: Expression*base_; const VType*type_; }; /* * Class that handles 'new' statement. VHDL is not capable of dynamic memory * allocation, but it is useful for emitting some cases. */ class ExpNew : public Expression { public: ExpNew(Expression*size); ~ExpNew(); Expression*clone() const { return new ExpNew(size_->clone()); } // There is no 'new' in VHDL - do not emit anything void write_to_stream(std::ostream&) const {}; int emit(ostream&out, Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: Expression*size_; }; class ExpTime : public Expression { public: typedef enum { FS, PS, NS, US, MS, S } timeunit_t; ExpTime(uint64_t amount, timeunit_t unit); Expression*clone() const { return new ExpTime(amount_, unit_); } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&) const; int emit(ostream&out, Entity*ent, ScopeBase*scope); bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: // Returns the time value expressed in femtoseconds double to_fs() const; uint64_t amount_; timeunit_t unit_; }; #endif /* IVL_expression_H */ iverilog-10_1/vhdlpp/expression_debug.cc000066400000000000000000000054701265551621300205210ustar00rootroot00000000000000/* * Copyright (c) 2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "entity.h" # include "architec.h" # include "expression.h" # include # include # include # include using namespace std; void ExpArithmetic::dump(ostream&out, int indent) const { const char*fun_name = "?"; switch (fun_) { case PLUS: fun_name = "+"; break; case MINUS: fun_name = "-"; break; case MULT: fun_name = "*"; break; case DIV: fun_name = "/"; break; case MOD: fun_name = "mod"; break; case REM: fun_name = "rem"; break; case POW: fun_name = "**"; break; case xCONCAT: ivl_assert(*this, 0); break; } out << setw(indent) << "" << "Arithmetic " << fun_name << " at " << get_fileline() << endl; dump_operands(out, indent+4); } void ExpConcat::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Concatenation at " << get_fileline() << endl; operand1_->dump(out, indent); operand2_->dump(out, indent); } void ExpCast::dump(ostream&out, int indent) const { out << "Casting "; base_->dump(out, indent+4); out << " to "; type_->emit_def(out, empty_perm_string); } void ExpNew::dump(ostream&out, int indent) const { out << "New dynamic array size: "; size_->dump(out, indent); } void ExpShift::dump(ostream&out, int indent) const { const char*fun_name = "?"; switch (shift_) { case SRL: fun_name = "srl"; break; case SLL: fun_name = "sll"; break; case SLA: fun_name = "sla"; break; case SRA: fun_name = "sra"; break; case ROR: fun_name = "ror"; break; case ROL: fun_name = "rol"; break; } out << setw(indent) << "" << "Shift " << fun_name << " at " << get_fileline() << endl; dump_operands(out, indent+4); } void ExpTime::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Time "; write_to_stream(out); } iverilog-10_1/vhdlpp/expression_elaborate.cc000066400000000000000000000737611265551621300214010ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Picture Elements, Inc., 777 Panoramic Way, Berkeley, CA 94704. */ # include "expression.h" # include "architec.h" # include "entity.h" # include "vsignal.h" # include "subprogram.h" # include "library.h" # include "std_types.h" # include # include # include "parse_types.h" # include "compiler.h" # include "ivl_assert.h" using namespace std; int Expression::elaborate_lval(Entity*, ScopeBase*, bool) { cerr << get_fileline() << ": error: Expression is not a valid l-value." << endl; return 1; } const VType* Expression::probe_type(Entity*, ScopeBase*) const { return 0; } const VType* Expression::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*) const { const VType*res = probe_type(ent,scope); if (res == 0) { cerr << get_fileline() << ": internal error: " << "fit_type for " << typeid(*this).name() << " is not implemented." << endl; } return res; } const VType*ExpName::elaborate_adjust_type_with_range_(Entity*, ScopeBase*scope, const VType*type) { // Unfold typedefs while (const VTypeDef*tdef = dynamic_cast(type)) { type = tdef->peek_definition(); } if (const VTypeArray*array = dynamic_cast(type)) { if (index_ && !lsb_) { // If the name is an array or a vector, then an // indexed name has the type of the element. type = array->element_type(); } else if (index_ && lsb_) { // If the name is an array, then a part select is // also an array, but with different bounds. int64_t use_msb, use_lsb; bool flag; flag = index_->evaluate(scope, use_msb); ivl_assert(*this, flag); flag = lsb_->evaluate(scope, use_lsb); ivl_assert(*this, flag); type = new VTypeArray(array->element_type(), use_msb, use_lsb); } } return type; } int ExpName::elaborate_lval_(Entity*ent, ScopeBase*scope, bool is_sequ, ExpName*suffix) { int errors = 0; if (debug_elaboration) { debug_log_file << get_fileline() << ": ExpName::elaborate_lval_: " << "name_=" << name_ << ", suffix->name()=" << suffix->name(); if (index_) debug_log_file << ", index_=" << *index_; if (lsb_) debug_log_file << ", lsb_=" << *lsb_; debug_log_file << endl; } if (prefix_.get()) { cerr << get_fileline() << ": sorry: I don't know how to elaborate " << "ExpName prefix of " << name_ << " in l-value expressions." << endl; errors += 1; } const VType*found_type = 0; if (const InterfacePort*cur = ent->find_port(name_)) { if (cur->mode != PORT_OUT && cur->mode != PORT_INOUT) { cerr << get_fileline() << ": error: Assignment to " "input port " << name_ << "." << endl; return errors + 1; } if (is_sequ) ent->set_declaration_l_value(name_, is_sequ); found_type = cur->type; } else if (ent->find_generic(name_)) { cerr << get_fileline() << ": error: Assignment to generic " << name_ << " from entity " << ent->get_name() << "." << endl; return errors + 1; } else if (Signal*sig = scope->find_signal(name_)) { // Tell the target signal that this may be a sequential l-value. if (is_sequ) sig->count_ref_sequ(); found_type = sig->peek_type(); } else if (Variable*var = scope->find_variable(name_)) { // Tell the target signal that this may be a sequential l-value. if (is_sequ) var->count_ref_sequ(); found_type = var->peek_type(); } // Resolve type definition to get an actual type. while (const VTypeDef*tdef = dynamic_cast (found_type)) { found_type = tdef->peek_definition(); if (debug_elaboration) { debug_log_file << get_fileline() << ": ExpName::elaborate_lval_: " << "Resolve typedef " << tdef->peek_name() << " to defined type=" << typeid(*found_type).name() << endl; } } ivl_assert(*this, found_type); // If the prefix type is an array, then we may actually have a // case of an array of structs. For example: // foo(n).bar // where foo is an array, (n) is an array index and foo(n) is // something that takes a suffix. For the purpose of our // expression type calculations, we need the element type. if (const VTypeArray*array = dynamic_cast (found_type)) { found_type = array->element_type(); while (const VTypeDef*tdef = dynamic_cast (found_type)) { found_type = tdef->peek_definition(); } if (debug_elaboration) { debug_log_file << get_fileline() << ": ExpName::elaborate_lval_: " << "Extract array element type=" << typeid(*found_type).name() << endl; } } const VType*suffix_type = 0; if (const VTypeRecord*record = dynamic_cast (found_type)) { const VTypeRecord::element_t*element = record->element_by_name(suffix->name_); ivl_assert(*this, element); const VType*element_type = element->peek_type(); ivl_assert(*this, element_type); suffix_type = element_type; } if (suffix_type == 0) { cerr << get_fileline() << ": error: I don't know how to handle prefix " << name_ << " with suffix " << suffix->name_ << endl; errors += 1; return errors; } suffix_type = suffix->elaborate_adjust_type_with_range_(ent, scope, suffix_type); ivl_assert(*this, suffix_type); suffix->set_type(suffix_type); return errors; } int ExpName::elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) { int errors = 0; if (prefix_.get()) { return prefix_->elaborate_lval_(ent, scope, is_sequ, this); } const VType*found_type = 0; if (const InterfacePort*cur = ent->find_port(name_)) { if (cur->mode != PORT_OUT && cur->mode != PORT_INOUT) { cerr << get_fileline() << ": error: Assignment to " "input port " << name_ << "." << endl; return errors += 1; } if (is_sequ) ent->set_declaration_l_value(name_, is_sequ); found_type = cur->type; } else if (ent->find_generic(name_)) { cerr << get_fileline() << ": error: Assignment to generic " << name_ << " from entity " << ent->get_name() << "." << endl; return 1; } else if (Signal*sig = scope->find_signal(name_)) { // Tell the target signal that this may be a sequential l-value. if (is_sequ) sig->count_ref_sequ(); found_type = sig->peek_type(); } else if (Variable*var = scope->find_variable(name_)) { // Tell the target signal that this may be a sequential l-value. if (is_sequ) var->count_ref_sequ(); found_type = var->peek_type(); } if (found_type == 0) { cerr << get_fileline() << ": error: Signal/variable " << name_ << " not found in this context." << endl; return errors + 1; } found_type = elaborate_adjust_type_with_range_(ent, scope, found_type); set_type(found_type); return errors; } int ExpName::elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*lval) { int errors = 0; if (prefix_.get()) { cerr << get_fileline() << ": sorry: I don't know how to elaborate " << "ExpName prefix parts in r-value expressions." << endl; errors += 1; } const VType*dummy_type; Expression*dummy_expr; if (const InterfacePort*cur = ent->find_port(name_)) { /* IEEE 1076-2008, p.80: * For a formal port IN, associated port should be IN, OUT, INOUT or BUFFER * For a formal port OUT, associated port should be OUT, INOUT or BUFFER * For a formal port INOUT, associated port should be OUT, INOUT or BUFFER * For a formal port BUFFER, associated port should be OUT, INOUT or BUFFER */ switch(lval->mode) { case PORT_OUT: //case PORT_INOUT: if (cur->mode == PORT_IN) { cerr << get_fileline() << ": error: Connecting " "formal output port " << lval->name << " to actual input port " << name_ << "." << endl; errors += 1; } break; case PORT_IN: case PORT_NONE: default: break; } } else if (scope->find_signal(name_)) { /* OK */ } else if (ent->find_generic(name_)) { /* OK */ } else if (scope->find_constant(name_, dummy_type, dummy_expr)) { /* OK */ } else if (scope->is_enum_name(name_)) { /* OK */ } else { cerr << get_fileline() << ": error: No port, signal or constant " << name_ << " to be used as r-value." << endl; errors += 1; } return errors; } int ExpNameALL::elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) { return Expression::elaborate_lval(ent, scope, is_sequ); } int Expression::elaborate_expr(Entity*, ScopeBase*, const VType*) { cerr << get_fileline() << ": internal error: I don't know how to elaborate expression type=" << typeid(*this).name() << endl; return 1; } const VType* ExpBinary::probe_type(Entity*ent, ScopeBase*scope) const { const VType*t1 = operand1_->probe_type(ent, scope); const VType*t2 = operand2_->probe_type(ent, scope); if (t1 == 0) return t2; if (t2 == 0) return t1; if (t1 == t2) return t1; if (const VType*tb = resolve_operand_types_(t1, t2)) return tb; // FIXME: I should at this point try harder to find an // operator that has the proper argument list and use this // here, but for now we leave it for the back-end to figure out. #if 0 cerr << get_fileline() << ": internal error: I don't know how to resolve types of generic binary expressions." << endl; #endif return 0; } const VType*ExpBinary::resolve_operand_types_(const VType*, const VType*) const { return 0; } int ExpBinary::elaborate_exprs(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; errors += operand1_->elaborate_expr(ent, scope, ltype); errors += operand2_->elaborate_expr(ent, scope, ltype); return errors; } /* * the default fit_type method for unary operator expressions is to * return the fit_type for the operand. The assumption is that the * operator doesn't change the type. */ const VType*ExpUnary::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const { return operand1_->fit_type(ent, scope, atype); } int ExpUnary::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { ivl_assert(*this, ltype != 0); set_type(ltype); return operand1_->elaborate_expr(ent, scope, ltype); } const VType*ExpAggregate::probe_type(Entity*ent, ScopeBase*scope) const { return Expression::probe_type(ent, scope); } const VType*ExpAggregate::fit_type(Entity*, ScopeBase*, const VTypeArray*host) const { ivl_assert(*this, elements_.size() == 1); size_t choice_count = elements_[0]->count_choices(); ivl_assert(*this, choice_count > 0); vector ce (choice_count); elements_[0]->map_choices(&ce[0]); ivl_assert(*this, ce.size() == 1); prange_t*prange = ce[0].choice->range_expressions(); ivl_assert(*this, prange); Expression*use_msb = prange->msb(); Expression*use_lsb = prange->lsb(); ivl_assert(*this, host->dimensions() == 1); vector range (1); range[0] = VTypeArray::range_t(use_msb, use_lsb); const VTypeArray*res = new VTypeArray(host->element_type(), range); return res; } int ExpAggregate::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { if (ltype == 0) { cerr << get_fileline() << ": error: Elaboration of aggregate types needs well known type context?" << endl; return 1; } set_type(ltype); while (const VTypeDef*cur = dynamic_cast(ltype)) { ltype = cur->peek_definition(); } if (const VTypeArray*larray = dynamic_cast(ltype)) { return elaborate_expr_array_(ent, scope, larray); } else if(const VTypeRecord*lrecord = dynamic_cast(ltype)) { return elaborate_expr_record_(ent, scope, lrecord); } cerr << get_fileline() << ": internal error: I don't know how to elaborate aggregate expressions. type=" << typeid(*ltype).name() << endl; return 1; } /* * Elaboration of array aggregates is elaboration of the element * expressions (the elements_ member) using the element type as the * ltype for the subexpression. */ int ExpAggregate::elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*ltype) { const VType*element_type = ltype->element_type(); int errors = 0; size_t choice_count = 0; // Figure out how many total elements we have here. Note that // each parsed element may be bound to multiple choices, so // account for that. for (size_t edx = 0 ; edx < elements_.size() ; edx += 1) { element_t*ecur = elements_[edx]; if (ecur->count_choices() == 0) choice_count += 1; else choice_count += ecur->count_choices(); } aggregate_.resize(choice_count); // Translate the elements_ array to the aggregate_ array. In // the target array, each expression is attached to a single // choice. size_t cdx = 0; for (size_t edx = 0 ; edx < elements_.size() ; edx += 1) { element_t*ecur = elements_[edx]; if (ecur->count_choices() == 0) { // positional associations have no "choice" // associated with them. aggregate_[cdx].choice = 0; aggregate_[cdx].expr = ecur->extract_expression(); aggregate_[cdx].alias_flag = false; cdx += 1; } else { ecur->map_choices(&aggregate_[cdx]); cdx += ecur->count_choices(); } } ivl_assert(*this, cdx == choice_count); // Now run through the more convenient mapping and elaborate // all the expressions that I find. for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) { if (aggregate_[idx].alias_flag) continue; errors += aggregate_[idx].expr->elaborate_expr(ent, scope, element_type); } // done with the obsolete elements_ vector. elements_.clear(); return errors; } int ExpAggregate::elaborate_expr_record_(Entity*ent, ScopeBase*scope, const VTypeRecord*ltype) { int errors = 0; aggregate_.resize(elements_.size()); choice_element tmp; int idx; // Translate the elements_ array to the aggregate_ array. In // the target array, each expression is attached to a single // choice. for (size_t edx = 0 ; edx < elements_.size() ; edx += 1) { element_t*ecur = elements_[edx]; // it is invalid to have more than one choice in record assignment ivl_assert(*this, ecur->count_choices() == 1); ecur->map_choices(&tmp); choice_t*ch = tmp.choice; ivl_assert(*this, !ch->others()); ivl_assert(*this, !tmp.alias_flag); // Get the appropriate type for a field const ExpName*field = dynamic_cast(ch->simple_expression(false)); ivl_assert(*this, field); perm_string field_name = field->peek_name(); idx = -1; const VTypeRecord::element_t*el = ltype->element_by_name(field_name, &idx); ivl_assert(*this, idx >= 0); aggregate_[idx] = tmp; errors += aggregate_[idx].expr->elaborate_expr(ent, scope, el->peek_type()); } // done with the obsolete elements_ vector. elements_.clear(); return errors; } void ExpAggregate::element_t::map_choices(ExpAggregate::choice_element*dst) { for (size_t idx = 0 ; idx < fields_.size() ; idx += 1) { dst->choice = fields_[idx]; dst->expr = val_; dst->alias_flag = (idx != 0); dst += 1; } } int ExpArithmetic::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); errors += elaborate_exprs(ent, scope, ltype); return errors; } const VType* ExpArithmetic::resolve_operand_types_(const VType*t1, const VType*t2) const { while (const VTypeRange*tmp = dynamic_cast (t1)) t1 = tmp->base_type(); while (const VTypeRange*tmp = dynamic_cast (t2)) t2 = tmp->base_type(); if (t1->type_match(t2)) return t1; if (t2->type_match(t2)) return t2; return 0; } const VType* ExpAttribute::probe_type(Entity*ent, ScopeBase*scope) const { base_->probe_type(ent, scope); if (name_ == "length" || name_ == "left" || name_ == "right") { return &primitive_NATURAL; } return 0; } int ExpAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { int errors = 0; const VType*sub_type = base_->probe_type(ent, scope); errors += base_->elaborate_expr(ent, scope, sub_type); return errors; } const VType*ExpBitstring::fit_type(Entity*, ScopeBase*, const VTypeArray*atype) const { // Really should check that this string can work with the // array element type? return atype->element_type(); } int ExpBitstring::elaborate_expr(Entity*, ScopeBase*, const VType*) { int errors = 0; const VTypeArray*type = new VTypeArray(&primitive_STDLOGIC, value_.size() - 1, 0); set_type(type); return errors; } const VType*ExpCharacter::fit_type(Entity*, ScopeBase*, const VTypeArray*atype) const { // Really should check that this character can work with the // array element type? return atype->element_type(); } int ExpCharacter::elaborate_expr(Entity*, ScopeBase*, const VType*ltype) { ivl_assert(*this, ltype != 0); set_type(ltype); return 0; } const VType*ExpConcat::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const { Expression*operands[2] = {operand1_, operand2_}; const VType*types[2] = {NULL, NULL}; Expression*sizes[2] = {NULL, NULL}; // determine the type and size of concatenated expressions for(int i = 0; i < 2; ++i) { types[i] = operands[i]->fit_type(ent, scope, atype); if(const VTypeArray*arr = dynamic_cast(types[i])) { types[i] = arr->element_type(); ivl_assert(*this, arr->dimensions() == 1); const VTypeArray::range_t&dim = arr->dimension(0); sizes[i] = new ExpArithmetic(ExpArithmetic::MINUS, dim.msb(), dim.lsb()); } else { sizes[i] = new ExpInteger(0); } } // the range of the concatenated expression is (size1 + size2 + 1):0 // note that each of the sizes are already decreased by one, // e.g. 3:0 <=> size == 3 even though there are 4 bits Expression*size = new ExpArithmetic(ExpArithmetic::PLUS, new ExpArithmetic(ExpArithmetic::PLUS, sizes[0], sizes[1]), new ExpInteger(1)); std::list ranges; ranges.push_front(new prange_t(size, new ExpInteger(0), true)); const VType*array = new VTypeArray(types[1], &ranges); return array; } /* * I don't know how to probe the type of a concatenation, quite yet. */ const VType*ExpConcat::probe_type(Entity*, ScopeBase*) const { ivl_assert(*this, 0); return 0; } int ExpConcat::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); if (const VTypeArray*atype = dynamic_cast(ltype)) { errors += elaborate_expr_array_(ent, scope, atype); } else { errors += operand1_->elaborate_expr(ent, scope, ltype); errors += operand2_->elaborate_expr(ent, scope, ltype); } return errors; } int ExpConcat::elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*atype) { int errors = 0; // For now, only support single-dimension arrays here. ivl_assert(*this, atype->dimensions() == 1); const VType*type1 = operand1_->fit_type(ent, scope, atype); ivl_assert(*this, type1); const VType*type2 = operand2_->fit_type(ent, scope, atype); ivl_assert(*this, type2); errors += operand1_->elaborate_expr(ent, scope, type1); errors += operand2_->elaborate_expr(ent, scope, type2); return errors; } const VType* ExpConditional::probe_type(Entity*, ScopeBase*) const { return 0; } int ExpConditional::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) ltype = probe_type(ent, scope); ivl_assert(*this, ltype); set_type(ltype); /* Note that the type for the condition expression need not have anything to do with the type of this expression. */ for (list::const_iterator cur = options_.begin() ; cur != options_.end() ; ++cur) { errors += (*cur)->elaborate_expr(ent, scope, ltype); } return errors; } int ExpConditional::case_t::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (cond_) errors += cond_->elaborate_expr(ent, scope, 0); for (list::const_iterator cur = true_clause_.begin() ; cur != true_clause_.end() ; ++cur) { errors += (*cur)->elaborate_expr(ent, scope, ltype); } return errors; } const VType*ExpFunc::probe_type(Entity*, ScopeBase*scope) const { SubprogramHeader*prog = def_; if(!prog) { prog = scope->find_subprogram(name_); } if(!prog) prog = library_find_subprogram(name_); if(!prog) { cerr << get_fileline() << ": sorry: VHDL function " << name_ << " not yet implemented" << endl; ivl_assert(*this, false); } return prog->peek_return_type(); } int ExpFunc::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { int errors = 0; ivl_assert(*this, scope); SubprogramHeader*prog = scope->find_subprogram(name_); if(!prog) prog = library_find_subprogram(name_); ivl_assert(*this, def_==0); def_ = prog; // Elaborate arguments for (size_t idx = 0 ; idx < argv_.size() ; idx += 1) { const VType*tmp = argv_[idx]->probe_type(ent, scope); const VType*param_type = prog ? prog->peek_param_type(idx) : NULL; if(!tmp && param_type) tmp = param_type; errors += argv_[idx]->elaborate_expr(ent, scope, tmp); } // SystemVerilog functions work only with defined size data types, therefore // if header does not specify argument or return type size, create a function // instance that work with this particular size. if(def_ && !def_->is_std() && def_->unbounded()) { def_ = prog->make_instance(argv_, scope); name_ = def_->name(); } if(!def_) { cerr << get_fileline() << ": error: could not find function " << name_ << endl; ++errors; } return errors; } const VType* ExpFunc::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*) const { return probe_type(ent, scope); } const VType* ExpInteger::probe_type(Entity*, ScopeBase*) const { return &primitive_INTEGER; } int ExpInteger::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); return errors; } const VType* ExpReal::probe_type(Entity*, ScopeBase*) const { return &primitive_REAL; } int ExpReal::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); return errors; } int ExpLogical::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); errors += elaborate_exprs(ent, scope, ltype); return errors; } const VType* ExpName::probe_prefix_type_(Entity*ent, ScopeBase*scope) const { if (prefix_.get()) { cerr << get_fileline() << ": sorry: I do not know how to support nested prefix parts." << endl; return 0; } const VType*type = probe_type(ent, scope); return type; } /* * This method is the probe_type() implementation for ExpName objects * that have prefix parts. In this case we try to get the type of the * prefix and interpret the name in that context. */ const VType* ExpName::probe_prefixed_type_(Entity*ent, ScopeBase*scope) const { // First, get the type of the prefix. const VType*prefix_type = prefix_->probe_prefix_type_(ent, scope); if (prefix_type == 0) { return 0; } while (const VTypeDef*def = dynamic_cast (prefix_type)) { prefix_type = def->peek_definition(); } // If the prefix type is a record, then the current name is // the name of a member. if (const VTypeRecord*pref_record = dynamic_cast (prefix_type)) { const VTypeRecord::element_t*element = pref_record->element_by_name(name_); ivl_assert(*this, element); const VType*element_type = element->peek_type(); ivl_assert(*this, element_type); return element_type; } if (const VTypeArray*pref_array = dynamic_cast (prefix_type)) { const VType*element_type = pref_array->element_type(); ivl_assert(*this, element_type); return element_type; } cerr << get_fileline() << ": sorry: I don't know how to probe " << "prefix type " << typeid(*prefix_type).name() << " of " << name_ << "." << endl; return 0; } const VType* ExpName::probe_type(Entity*ent, ScopeBase*scope) const { if (prefix_.get()) return probe_prefixed_type_(ent, scope); if(ent) { if (const InterfacePort*cur = ent->find_port(name_)) { ivl_assert(*this, cur->type); return cur->type; } if (const InterfacePort*cur = ent->find_generic(name_)) { ivl_assert(*this, cur->type); return cur->type; } } if(scope) { if (Signal*sig = scope->find_signal(name_)) return sig->peek_type(); if (Variable*var = scope->find_variable(name_)) return var->peek_type(); const VType*type = 0; Expression*cval = 0; if (scope->find_constant(name_, type, cval)) return type; Architecture*arc = dynamic_cast(scope); if (arc && (type = arc->probe_genvar_type(name_))) { return type; } if (const InterfacePort*port = scope->find_param(name_)) { return port->type; } if ((type = scope->is_enum_name(name_))) { return type; } } cerr << get_fileline() << ": error: Signal/variable " << name_ << " not found in this context." << endl; return 0; } const VType* ExpName::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*)const { return probe_type(ent, scope); } int ExpName::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { if (ltype) { ivl_assert(*this, ltype != 0); set_type(ltype); } if(prefix_.get()) prefix_.get()->elaborate_expr(ent, scope, NULL); if(index_) index_->elaborate_expr(ent, scope, &primitive_INTEGER); if(lsb_) lsb_->elaborate_expr(ent, scope, &primitive_INTEGER); return 0; } const VType* ExpNameALL::probe_type(Entity*, ScopeBase*) const { return 0; } const VType* ExpRelation::probe_type(Entity*, ScopeBase*) const { return &type_BOOLEAN; } int ExpRelation::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); // The type of the operands must match, but need not match the // type for the ExpRelation itself. So get the operand type // separately. const VType*otype = ExpBinary::probe_type(ent, scope); errors += elaborate_exprs(ent, scope, otype); return errors; } int ExpShift::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (ltype == 0) { ltype = probe_type(ent, scope); } ivl_assert(*this, ltype != 0); errors += elaborate_exprs(ent, scope, ltype); return errors; } /* * When a string appears in a concatenation, then the type of the * string is an array with the same element type of the concatenation, * but with elements for each character of the string. */ const VType*ExpString::fit_type(Entity*, ScopeBase*, const VTypeArray*atype) const { vector range (atype->dimensions()); // Generate an array range for this string ivl_assert(*this, range.size() == 1); VTypeArray*type = new VTypeArray(atype->element_type(), value_.size(), 0); return type; } int ExpString::elaborate_expr(Entity*, ScopeBase*, const VType*ltype) { ivl_assert(*this, ltype != 0); set_type(ltype); return 0; } int ExpTime::elaborate_expr(Entity*, ScopeBase*, const VType*) { set_type(&primitive_INTEGER); return 0; } iverilog-10_1/vhdlpp/expression_emit.cc000066400000000000000000000620051265551621300203660ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2015 / Stephen Williams (steve@icarus.com) * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "expression.h" # include "vtype.h" # include "architec.h" # include "package.h" # include "std_funcs.h" # include "std_types.h" # include "parse_types.h" # include # include # include # include # include "ivl_assert.h" # include using namespace std; inline static int emit_logic(char val, ostream& out, const VTypePrimitive::type_t type) { // TODO case 'W': case 'L': case 'H': switch (val) { case '-': case 'U': val = 'x'; /* fall through */ case 'X': case 'Z': assert(type == VTypePrimitive::STDLOGIC); /* fall through */ case '0': case '1': out << (char) tolower(val); break; default: assert(false); out << "x"; return 1; } return 0; } int Expression::emit(ostream&out, Entity*, ScopeBase*) { out << " /* " << get_fileline() << ": internal error: " << "I don't know how to emit this expression! " << "type=" << typeid(*this).name() << " */ "; return 1; } int Expression::emit_package(ostream&out) { out << " /* " << get_fileline() << ": internal error: " << "I don't know how to emit_package this expression! " << "type=" << typeid(*this).name() << " */ "; return 1; } bool Expression::is_primary(void) const { return false; } int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; bool oper_primary = operand1_->is_primary(); if (! oper_primary) out << "("; errors += operand1_->emit(out, ent, scope); if (! oper_primary) out << ")"; return errors; } int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; bool oper_primary = operand2_->is_primary(); if (! oper_primary) out << "("; errors += operand2_->emit(out, ent, scope); if (! oper_primary) out << ")"; return errors; } int ExpUnary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += operand1_->emit(out, ent, scope); return errors; } int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope) { if (peek_type() == 0) { out << "/* " << get_fileline() << ": internal error: " << "Aggregate literal needs well defined type." << endl; return 1; } const VType*use_type = peek_type(); while (const VTypeDef*def = dynamic_cast (use_type)) { use_type = def->peek_definition(); } if (const VTypeArray*atype = dynamic_cast (use_type)) return emit_array_(out, ent, scope, atype); else if (const VTypeRecord*arecord = dynamic_cast (use_type)) return emit_record_(out, ent, scope, arecord); out << "/* " << get_fileline() << ": internal error: " << "I don't know how to elab/emit aggregate in " << typeid(use_type).name() << " type context. */"; return 1; } int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*atype) { int errors = 0; // Special case: The aggregate is a single "others" item. if (aggregate_.size() == 1 && aggregate_[0].choice->others()) { assert(atype->dimensions() == 1); const VTypeArray::range_t&rang = atype->dimension(0); assert(! rang.is_box()); int64_t use_msb; int64_t use_lsb; bool rc_msb, rc_lsb; rc_msb = rang.msb()->evaluate(ent, scope, use_msb); rc_lsb = rang.lsb()->evaluate(ent, scope, use_lsb); if (rc_msb && rc_lsb) { int asize = (use_msb >= use_lsb) ? (use_msb - use_lsb) + 1 : (use_lsb - use_msb) + 1; out << "{" << asize << "{"; errors += aggregate_[0].expr->emit(out, ent, scope); out << "}}"; } else { out << "{("; if (rc_msb) { out << use_msb; } else { out << "("; errors += rang.msb()->emit(out, ent, scope); out << ")"; } if (rc_lsb && use_lsb==0) { } else if (rc_lsb) { out << "-" << use_lsb; } else { out << "-("; errors += rang.lsb()->emit(out, ent, scope); out << ")"; } out << "+1){"; errors += aggregate_[0].expr->emit(out, ent, scope); out << "}}"; } return errors; } const VTypeArray::range_t&rang = atype->dimension(0); assert(! rang.is_box()); // Fully calculate the range numbers. int64_t use_msb, use_lsb; bool rc; rc = rang.msb()->evaluate(ent, scope, use_msb); ivl_assert(*this, rc); rc = rang.lsb()->evaluate(ent, scope, use_lsb); ivl_assert(*this, rc); if(use_msb < use_lsb) swap(use_msb, use_lsb); map element_map; choice_element*element_other = 0; bool positional_section = true; int64_t positional_idx = use_msb; for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) { if (aggregate_[idx].choice == 0) { // positional association! if (!positional_section) { cerr << get_fileline() << ": error: " << "All positional associations must be before" << " any named associations." << endl; errors += 1; } element_map[positional_idx] = &aggregate_[idx]; positional_idx -= 1; continue; } if (aggregate_[idx].choice->others()) { ivl_assert(*this, element_other == 0); element_other = &aggregate_[idx]; continue; } // If this is a range choice, then calculate the bounds // of the range and scan through the values, mapping the // value to the aggregate_[idx] element. if (prange_t*range = aggregate_[idx].choice->range_expressions()) { int64_t begin_val, end_val; if (! range->msb()->evaluate(ent, scope, begin_val)) { cerr << range->msb()->get_fileline() << ": error: " << "Unable to evaluate aggregate choice expression." << endl; errors += 1; continue; } if (! range->lsb()->evaluate(ent, scope, end_val)) { cerr << range->msb()->get_fileline() << ": error: " << "Unable to evaluate aggregate choice expression." << endl; errors += 1; continue; } if (begin_val < end_val) { int64_t tmp = begin_val; begin_val = end_val; end_val = tmp; } while (begin_val >= end_val) { element_map[begin_val] = &aggregate_[idx]; begin_val -= 1; } continue; } int64_t tmp_val; Expression*tmp = aggregate_[idx].choice->simple_expression(false); ivl_assert(*this, tmp); // Named aggregate element. Once we see one of // these, we can no longer accept positional // elements so disable further positional // processing. positional_section = false; if (! tmp->evaluate(ent, scope, tmp_val)) { cerr << tmp->get_fileline() << ": error: " << "Unable to evaluate aggregate choice expression." << endl; errors += 1; continue; } element_map[tmp_val] = &aggregate_[idx]; } // Emit the elements as a concatenation. This works great for // vectors of bits. We implement VHDL arrays as packed arrays, // so this should be generally correct. // TODO uncomment this once ivl supports assignments of '{} /*if(!peek_type()->can_be_packed()) out << "'";*/ out << "{"; for (int64_t idx = use_msb ; idx >= use_lsb ; idx -= 1) { choice_element*cur = element_map[idx]; if (cur == 0) cur = element_other; if (idx < use_msb) out << ", "; if (cur == 0) { out << "/* Missing element " << idx << " */"; cerr << get_fileline() << ": error: " << "Missing element " << idx << "." << endl; errors += 1; } else { errors += cur->expr->emit(out, ent, scope); } } out << "}"; return errors; } int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*) { int errors = 0; out << "{"; for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) { ivl_assert(*this, !aggregate_[idx].choice->others()); ivl_assert(*this, !aggregate_[idx].choice->range_expressions()); //Expression*name = aggregate_[idx].choice->simple_expression(false); //ivl_assert(*this, name); Expression*val = aggregate_[idx].expr; ivl_assert(*this, val); if(idx != 0) out << ","; //errors += name->emit(out, ent, scope); //out << ": "; errors += val->emit(out, ent, scope); } out << "}"; return errors; } int ExpAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; // Try to evaluate first int64_t val; if(evaluate(scope, val)) { out << val; return 0; } if (name_ == "event") { out << "$ivlh_attribute_event("; errors += base_->emit(out, ent, scope); out << ")"; return errors; } /* Special Case: The length,left & right attributes can be calculated all the down to a literal integer at compile time, and all it needs is the type of the base expression. (The base expression doesn't even need to be evaluated.) */ if (name_=="length") { out << "$bits("; errors += base_->emit(out, ent, scope); out << ")"; return errors; } else if (name_=="left" || name_=="right") { out << "$" << name_ << "("; errors += base_->emit(out, ent, scope); out << ")"; return errors; } out << "$ivl_attribute("; errors += base_->emit(out, ent, scope); out << ", \"" << name_ << "\")"; return errors; } int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += emit_operand1(out, ent, scope); switch (fun_) { case PLUS: out << " + "; break; case MINUS: out << " - "; break; case MULT: out << " * "; break; case DIV: out << " / "; break; case MOD: out << " % "; break; case POW: out << " ** "; break; case REM: out << " /* ?remainder? */ "; break; case xCONCAT: ivl_assert(*this, 0); out << " /* ?concat? */ "; break; } errors += emit_operand2(out, ent, scope); return errors; } int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*) { int errors = 0; out << value_.size() << "'b"; for (size_t idx = 0 ; idx < value_.size() ; idx += 1) out << value_[value_.size()-idx-1]; return errors; } int ExpCharacter::emit_primitive_bit_(ostream&out, Entity*, ScopeBase*, const VTypePrimitive*etype) { out << "1'b"; int res = emit_logic(value_, out, etype->type()); if(res) cerr << get_fileline() << ": internal error: " << "Don't know how to handle bit " << value_ << " with etype==" << etype->type() << endl; return res; } int ExpCharacter::emit(ostream&out, Entity*ent, ScopeBase*scope) { const VType*etype = peek_type(); if (const VTypePrimitive*use_type = dynamic_cast(etype)) { return emit_primitive_bit_(out, ent, scope, use_type); } if (const VTypeArray*array = dynamic_cast(etype)) { if (const VTypePrimitive*use_type = dynamic_cast(array->element_type())) { return emit_primitive_bit_(out, ent, scope, use_type); } } out << "\"" << value_ << "\""; return 0; } bool ExpCharacter::is_primary(void) const { return true; } /* * This is not exactly a "primary", but it is wrapped in its own * parentheses (braces) so we return true here. */ bool ExpConcat::is_primary(void) const { return true; } int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "{"; errors += operand1_->emit(out, ent, scope); out << ", "; errors += operand2_->emit(out, ent, scope); out << "}"; return errors; } int ExpConditional::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "("; // Draw out any when-else expressions. These are all the else_ // clauses besides the last. if (options_.size() > 1) { list::iterator last = options_.end(); --last; for (list::iterator cur = options_.begin() ; cur != last ; ++cur) { errors += (*cur)->emit_option(out, ent, scope); } } errors += options_.back()->emit_default(out, ent, scope); out << ")"; // The emit_option() functions do not close the last // parentheses so that the following expression can be // nested. But that means come the end, we have some // expressions to close. for (size_t idx = 1 ; idx < options_.size() ; idx += 1) out << ")"; return errors; } int ExpConditional::case_t::emit_option(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; assert(cond_ != 0); out << "("; errors += cond_->emit(out, ent, scope); out << ")? ("; if (true_clause_.size() > 1) { cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl; errors += 1; } Expression*tmp = true_clause_.front(); errors += tmp->emit(out, ent, scope); out << ") : ("; return errors; } int ExpConditional::case_t::emit_default(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; // Trailing else must have no condition. assert(cond_ == 0); if (true_clause_.size() > 1) { cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl; errors += 1; } Expression*tmp = true_clause_.front(); errors += tmp->emit(out, ent, scope); return errors; } int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; switch (fun_) { case NEGEDGE: out << "negedge "; break; case POSEDGE: out << "posedge "; break; case ANYEDGE: break; } errors += emit_operand1(out, ent, scope); return errors; } int ExpFunc::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; ivl_assert(*this, def_); // If this function has an elaborated definition, and if // that definition is in a package, then include the // package name as a scope qualifier. This assures that // the SV elaborator finds the correct VHDL elaborated // definition. const Package*pkg = dynamic_cast (def_->get_parent()); if (pkg != 0) out << "\\" << pkg->name() << " ::"; errors += def_->emit_name(argv_, out, ent, scope); out << " ("; def_->emit_args(argv_, out, ent, scope); out << ")"; return errors; } int ExpInteger::emit(ostream&out, Entity*, ScopeBase*) { out << "32'd" << value_; return 0; } int ExpInteger::emit_package(ostream&out) { out << value_; return 0; } int ExpReal::emit(ostream&out, Entity*, ScopeBase*) { out << value_; return 0; } int ExpReal::emit_package(ostream&out) { out << value_; return 0; } bool ExpReal::is_primary(void) const { return true; } int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += emit_operand1(out, ent, scope); switch (fun_) { case AND: out << " & "; break; case OR: out << " | "; break; case XOR: out << " ^ "; break; case NAND: out << " ~& "; break; case NOR: out << " ~| "; break; case XNOR: out << " ~^ "; break; } errors += emit_operand2(out, ent, scope); return errors; } int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; if (prefix_.get()) { errors += prefix_->emit_as_prefix_(out, ent, scope); } out << "\\" << name_ << " "; if (index_) { out << "["; errors += index_->emit(out, ent, scope); out << "]"; ivl_assert(*this, lsb_ == 0); } out << "."; return errors; } int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; int field_size = 0; list indices; if(try_workarounds_(out, ent, scope, indices, field_size)) { emit_workaround_(out, ent, scope, indices, field_size); for(list::iterator it = indices.begin(); it != indices.end(); ++it) { delete *it; } return 0; } if (prefix_.get()) { errors += prefix_->emit_as_prefix_(out, ent, scope); } const GenerateStatement*gs = 0; Architecture*arc = dynamic_cast(scope); if (arc && (gs = arc->probe_genvar_emit(name_))) out << "\\" << gs->get_name() << ":" << name_ << " "; else out << "\\" << name_ << " "; if (index_) { out << "["; errors += index_->emit(out, ent, scope); if (lsb_) { out << ":"; errors += lsb_->emit(out, ent, scope); } out << "]"; } return errors; } bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, list& indices, int& data_size) { Expression*exp = NULL; bool wrkand_required = false; const VType*type = NULL; if(!scope) return false; if(prefix_.get()) prefix_->try_workarounds_(out, ent, scope, indices, data_size); if(index_ && !lsb_ && scope->find_constant(name_, type, exp)) { while(const VTypeDef*type_def = dynamic_cast(type)) { type = type_def->peek_definition(); } const VTypeArray*arr = dynamic_cast(type); assert(arr); wrkand_required |= check_const_array_workaround_(arr, scope, indices, data_size); } if(prefix_.get() && scope->find_constant(prefix_->name_, type, exp)) { // Handle the case of array of records if(prefix_->index_) { const VTypeArray*arr = dynamic_cast(type); assert(arr); type = arr->element_type(); data_size = type->get_width(scope); } while(const VTypeDef*type_def = dynamic_cast(type)) { type = type_def->peek_definition(); } const VTypeRecord*rec = dynamic_cast(type); assert(rec); wrkand_required |= check_const_record_workaround_(rec, scope, indices, data_size); } return wrkand_required; } bool ExpName::check_const_array_workaround_(const VTypeArray*arr, ScopeBase*scope, list&indices, int&data_size) const { const VType*element = arr->element_type(); data_size = element->get_width(scope); if(data_size < 0) return false; indices.push_back(new index_t(index_->clone(), new ExpInteger(data_size))); return true; } bool ExpName::check_const_record_workaround_(const VTypeRecord*rec, ScopeBase*scope, list&indices, int&data_size) const { int tmp_offset = 0; const vector& elements = rec->get_elements(); for(vector::const_reverse_iterator it = elements.rbegin(); it != elements.rend(); ++it) { VTypeRecord::element_t* el = (*it); if(el->peek_name() == name_) { const VType*type = el->peek_type(); int tmp_field = type->get_width(scope); if(tmp_field < 0) return false; data_size = tmp_field; indices.push_back(new index_t(NULL, NULL, new ExpInteger(tmp_offset))); if(index_) { const VTypeArray*arr = dynamic_cast(type); assert(arr); return check_const_array_workaround_(arr, scope, indices, data_size); } return true; } int w = el->peek_type()->get_width(scope); if(w < 0) return false; tmp_offset += w; } return false; } int ExpName::emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope, const list& indices, int field_size) { int errors = 0; out << "\\" << (prefix_.get() ? prefix_->name_ : name_) << " ["; for(list::const_iterator it = indices.begin(); it != indices.end(); ++it) { errors += (*it)->emit(out, ent, scope); out << "+"; } out << ":" << field_size << "]"; return errors; } bool ExpName::is_primary(void) const { return true; } int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += emit_operand1(out, ent, scope); switch (fun_) { case EQ: out << " == "; break; case LT: out << " < "; break; case GT: out << " > "; break; case NEQ: out << " != "; break; case LE: out << " <= "; break; case GE: out << " >= "; break; } errors += emit_operand2(out, ent, scope); return errors; } int ExpShift::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += emit_operand1(out, ent, scope); switch (shift_) { case SRL: out << " >> "; break; case SLL: out << " << "; break; case SRA: out << " >>> "; break; case SLA: out << " <<< "; break; case ROR: case ROL: out << " /* ?ror/rol? */ "; break; } errors += emit_operand2(out, ent, scope); return errors; } bool ExpString::is_primary(void) const { return true; } int ExpString::emit(ostream& out, Entity*ent, ScopeBase*scope) { const VType*type = peek_type(); assert(type != 0); if (const VTypeArray*arr = dynamic_cast(type)) { return emit_as_array_(out, ent, scope, arr); } out << "\""; for(vector::const_iterator it = value_.begin() ; it != value_.end(); ++it) out << *it; out << "\""; return 0; } int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArray*arr) { int errors = 0; assert(arr->dimensions() == 1); const VTypePrimitive*etype = dynamic_cast (arr->basic_type()); assert(etype); // Detect the special case that this is an array of // CHARACTER. In this case, emit at a Verilog string. if (etype->type()==VTypePrimitive::CHARACTER) { vector tmp (value_.size() + 3); tmp[0] = '"'; memcpy(&tmp[1], &value_[0], value_.size()); tmp[value_.size()+1] = '"'; tmp[value_.size()+2] = 0; out << &tmp[0]; return errors; } assert(etype->type() != VTypePrimitive::INTEGER); out << value_.size() << "'b"; for (size_t idx = 0 ; idx < value_.size() ; idx += 1) { int res = emit_logic(value_[idx], out, etype->type()); errors += res; if(res) cerr << get_fileline() << ": internal error: " << "Don't know how to handle bit " << value_[idx] << " with etype==" << etype->type() << endl; } return errors; } int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "abs("; errors += emit_operand1(out, ent, scope); out << ")"; return errors; } int ExpUNot::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; const VType*op_type = peek_operand()->probe_type(ent, scope); if(op_type && op_type->type_match(&type_BOOLEAN)) out << "!("; else out << "~("; errors += emit_operand1(out, ent, scope); out << ")"; return errors; } int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += type_->emit_def(out, empty_perm_string); out << "'("; errors += base_->emit(out, ent, scope); out << ")"; return errors; } int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "new["; errors += size_->emit(out, ent, scope); out << "]"; return errors; } int ExpTime::emit(ostream&out, Entity*, ScopeBase*) { out << amount_; switch(unit_) { case FS: out << "fs"; break; case PS: out << "ps"; break; case NS: out << "ns"; break; case US: out << "us"; break; case MS: out << "ms"; break; case S: out << "s"; break; } return 0; } iverilog-10_1/vhdlpp/expression_evaluate.cc000066400000000000000000000160511265551621300212360ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "expression.h" # include "architec.h" # include # include bool Expression::evaluate(ScopeBase*, int64_t&) const { return false; } bool Expression::evaluate(Entity*, ScopeBase*scope, int64_t&val) const { return evaluate(scope, val); } bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const { int64_t val1, val2; bool rc; rc = eval_operand1(scope, val1); if (rc == false) return false; rc = eval_operand2(scope, val2); if (rc == false) return false; switch (fun_) { case PLUS: val = val1 + val2; break; case MINUS: val = val1 - val2; break; case MULT: val = val1 * val2; break; case DIV: if (val2 == 0) return false; val = val1 / val2; break; case MOD: if (val2 == 0) return false; val = val1 % val2; break; case REM: return false; case POW: return false; case xCONCAT: // not possible return false; } return true; } bool ExpAttribute::evaluate(ScopeBase*scope, int64_t&val) const { /* Special Case: The array attributes can sometimes be calculated all the down to a literal integer at compile time, and all it needs is the type of the base expression. (The base expression doesn't even need to be evaluated.) */ if (name_ == "length" || name_ == "right" || name_ == "left") { const VType*base_type = base_->peek_type(); if(!base_type) { const ExpName*name = NULL; if(scope && (name = dynamic_cast(base_))) { const perm_string& n = name->peek_name(); if(const Variable*var = scope->find_variable(n)) base_type = var->peek_type(); else if(const Signal*sig = scope->find_signal(n)) base_type = sig->peek_type(); else if(const InterfacePort*port = scope->find_param(n)) base_type = port->type; } } if(!base_type) return false; // I tried really hard, sorry const VTypeArray*arr = dynamic_cast(base_type); if (arr == 0) { cerr << endl << get_fileline() << ": error: " << "Cannot apply the '" << name_ << " attribute to non-array objects" << endl; ivl_assert(*this, false); return false; } if(name_ == "length") { int64_t size = arr->get_width(scope); if(size > 0) val = size; else return false; } else if(name_ == "left") { arr->dimension(0).msb()->evaluate(scope, val); } else if(name_ == "right") { arr->dimension(0).lsb()->evaluate(scope, val); } else ivl_assert(*this, false); return true; } return false; } bool ExpAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { if (!ent || !scope) { // it's impossible to evaluate, probably it is inside a subprogram return false; } if (name_ == "left" || name_ == "right") { const VType*base_type = base_->peek_type(); if (base_type == 0) base_type = base_->probe_type(ent, scope); ivl_assert(*this, base_type); const VTypeArray*arr = dynamic_cast(base_type); if (arr == 0) { cerr << endl << get_fileline() << ": error: " << "Cannot apply the '" << name_ << " attribute to non-array objects" << endl; ivl_assert(*this, false); return false; } ivl_assert(*this, arr->dimensions() == 1); if(name_ == "left") arr->dimension(0).msb()->evaluate(ent, scope, val); else // "right" arr->dimension(0).lsb()->evaluate(ent, scope, val); return true; } return evaluate(scope, val); } /* * I don't yet know how to evaluate concatenations. It is not likely * to come up anyhow. */ bool ExpConcat::evaluate(ScopeBase*, int64_t&) const { return false; } bool ExpName::evaluate(ScopeBase*scope, int64_t&val) const { const VType*type; Expression*exp; if (prefix_.get()) { cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; return false; } if (!scope) return false; if (!scope->find_constant(name_, type, exp)) return false; return exp->evaluate(scope, val); } bool ExpName::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { if (prefix_.get()) { cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; return false; } const InterfacePort*gen = ent->find_generic(name_); if (gen) { cerr << get_fileline() << ": sorry: I don't necessarily handle generic overrides." << endl; // Evaluate the default expression and use that. if (gen->expr) return gen->expr->evaluate(ent, scope, val); } return evaluate(scope, val); } bool ExpShift::evaluate(ScopeBase*scope, int64_t&val) const { int64_t val1, val2; bool rc; rc = eval_operand1(scope, val1); if (rc == false) return false; rc = eval_operand2(scope, val2); if (rc == false) return false; switch (shift_) { case SRL: val = (uint64_t)val1 >> (uint64_t)val2; break; case SLL: val = (uint64_t)val1 << (uint64_t)val2; break; case SRA: val = (int64_t)val1 >> (int64_t)val2; break; case SLA: val = (int64_t)val1 << (int64_t)val2; break; case ROR: case ROL: return false; } return true; } bool ExpTime::evaluate(ScopeBase*, int64_t&val) const { double v = to_fs(); if(v > std::numeric_limits::max()) { val = std::numeric_limits::max(); cerr << get_fileline() << ": sorry: Time value is higher than the " << "handled limit, reduced to " << val << " fs." << endl; } val = v; return true; } bool ExpTime::evaluate(Entity*, ScopeBase*, int64_t&val) const { return evaluate(NULL, NULL, val); } iverilog-10_1/vhdlpp/expression_stream.cc000066400000000000000000000144611265551621300207260ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "expression.h" # include "parse_types.h" # include # include using namespace std; void ExpAggregate::write_to_stream(ostream&fd) const { fd << "("; for (vector::const_iterator cur = elements_.begin() ; cur != elements_.end() ; ++cur) { if(cur != elements_.begin()) fd << ", "; (*cur)->write_to_stream(fd); } fd << ")"; } void ExpAggregate::element_t::write_to_stream(ostream&fd) const { for (vector::const_iterator cur = fields_.begin() ; cur != fields_.end() ; ++cur) { (*cur)->write_to_stream(fd); } if(!fields_.empty()) fd << "=>"; val_->write_to_stream(fd); } void ExpAggregate::choice_t::write_to_stream(ostream&fd) { if (others()) { fd << "others"; return; } if (Expression*sim = simple_expression()) { sim->write_to_stream(fd); return; } if (prange_t*rp = range_expressions()) { rp->msb()->write_to_stream(fd); if (rp->is_downto()) fd << " downto "; else fd << " to "; rp->msb()->write_to_stream(fd); } fd << "/* ERROR */"; } void ExpArithmetic::write_to_stream(ostream&out) const { out << "("; write_to_stream_operand1(out); out << ")"; switch (fun_) { case PLUS: out << "+"; break; case MINUS: out << "-"; break; case MULT: out << "*"; break; case DIV: out << "/"; break; case MOD: out << "mod"; break; case REM: out << "rem"; break; case POW: out << "**"; break; case xCONCAT: ivl_assert(*this, 0); break; } out << "("; write_to_stream_operand2(out); out << ")"; } void ExpAttribute::write_to_stream(ostream&fd) const { base_->write_to_stream(fd); fd << "'" << name_; } void ExpBitstring::write_to_stream(ostream&fd) const { fd << "B\""; for(vector::const_reverse_iterator it = value_.rbegin(); it != value_.rend(); ++it) { fd << *it; } fd << "\""; } void ExpCharacter::write_to_stream(ostream&fd) const { char buf[4]; buf[0] = '\''; buf[1] = value_; buf[2] = '\''; buf[3] = 0; fd << buf; } void ExpConcat::write_to_stream(ostream&fd) const { fd << "("; operand1_->write_to_stream(fd); fd << ")&("; operand2_->write_to_stream(fd); fd << ")"; } void ExpConditional::write_to_stream(ostream&) const { ivl_assert(*this, !"Not supported"); } void ExpEdge::write_to_stream(ostream&) const { ivl_assert(*this, !"Not supported"); } void ExpFunc::write_to_stream(ostream&fd) const { const char*comma = ""; fd << name_ << "("; for (vector::const_iterator cur = argv_.begin() ; cur != argv_.end() ; ++cur) { fd << comma; (*cur)->write_to_stream(fd); comma = ", "; } fd << ")"; } void ExpInteger::write_to_stream(ostream&fd) const { fd << value_; } void ExpReal::write_to_stream(ostream&fd) const { fd << value_; } void ExpLogical::write_to_stream(ostream&) const { ivl_assert(*this, !"Not supported"); } void ExpName::write_to_stream(ostream&fd) const { if (prefix_.get()) { prefix_->write_to_stream(fd); fd << "."; } fd << name_; if (index_) { fd << "("; index_->write_to_stream(fd); if (lsb_) { fd << " downto "; lsb_->write_to_stream(fd); } fd << ")"; } } void ExpRelation::write_to_stream(ostream&fd) const { peek_operand1()->write_to_stream(fd); switch(fun_) { case EQ: fd << " = "; break; case LT: fd << " < "; break; case GT: fd << " > "; break; case NEQ: fd << " != "; break; case LE: fd << " <= "; break; case GE: fd << " >= "; break; } peek_operand2()->write_to_stream(fd); } void ExpShift::write_to_stream(ostream&out) const { out << "("; write_to_stream_operand1(out); out << ")"; switch (shift_) { case SRL: out << "srl"; break; case SLL: out << "sll"; break; case SLA: out << "sla"; break; case SRA: out << "sra"; break; case ROR: out << "ror"; break; case ROL: out << "rol"; break; } out << "("; write_to_stream_operand2(out); out << ")"; } void ExpString::write_to_stream(ostream&fd) const { fd << "\""; for(vector::const_iterator it = value_.begin(); it != value_.end(); ++it) { fd << *it; } fd << "\""; } void ExpUAbs::write_to_stream(ostream&fd) const { fd << "abs "; write_to_stream_operand1(fd); } void ExpUNot::write_to_stream(ostream&fd) const { fd << "not "; write_to_stream_operand1(fd); } void ExpCast::write_to_stream(ostream&fd) const { // Type casting is introduced only for a few specific cases in // SystemVerilog, so no need to use it here base_->write_to_stream(fd); } void ExpTime::write_to_stream(ostream&fd) const { fd << amount_; switch(unit_) { case FS: fd << " fs"; break; case PS: fd << " ps"; break; case NS: fd << " ns"; break; case US: fd << " us"; break; case MS: fd << " ms"; break; case S: fd << " s"; break; } } iverilog-10_1/vhdlpp/ivl_assert.h000066400000000000000000000023221265551621300171620ustar00rootroot00000000000000#ifndef IVL_ivl_assert_H #define IVL_ivl_assert_H /* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include #define ivl_assert(tok, expression) \ do { \ if (! (expression)) { \ cerr << (tok).get_fileline() << ": assert: " \ << __FILE__ << ":" << __LINE__ \ << ": failed assertion " << #expression << endl; \ abort(); \ } \ } while (0) #endif /* IVL_ivl_assert_H */ iverilog-10_1/vhdlpp/lexor.lex000066400000000000000000000440051265551621300165050ustar00rootroot00000000000000%option prefix="yy" %option never-interactive %option nounput %option reentrant %option noyywrap %{ /* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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 * aint64_t with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "parse_api.h" # include "lexor_keyword.h" # include "vhdlint.h" # include "vhdlreal.h" # include "parse_wrap.h" # include # include # include # include # define YY_NO_INPUT # define YY_DECL int yylex(YYSTYPE*yylvalp, YYLTYPE*yyllocp, yyscan_t yyscanner) //class vhdlnum; //class vhdlreal; extern int lexor_keyword_code (const char*str, unsigned len); /* * Lexical location information is passed in the yylloc variable to the * parser. The file names, strings, are kept in a list so that I can * re-use them. The set_file_name function will return a pointer to * the name as it exists in the list (and delete the passed string). * If the name is new, it will be added to the list. */ #define yylloc (*yyllocp) #define yylval (*yylvalp) static bool are_underscores_correct(char* text); static bool is_based_correct(char* text); static char* escape_quot_and_dup(char* text); static char* escape_apostrophe_and_dup(char* text); static double make_double_from_based(char* text); static int64_t make_long_from_based(char* text); static char* make_bitstring_literal(const char*text); static int64_t lpow(int64_t left, int64_t right); static unsigned short short_from_hex_char(char ch); static char* strdupnew(char const *str) { return str ? strcpy(new char [strlen(str)+1], str) : 0; } static int comment_enter; %} %x CCOMMENT %x LCOMMENT W [ \t\b\f\r]+ decimal_literal {integer}(\.{integer})?({exponent})? integer [0-9](_?[0-9])* exponent [eE][-+]?{integer} based_literal {integer}#{based_integer}(\.{based_integer})?#{exponent}? based_integer [0-9a-fA-F](_?[0-9a-fA-F])* time {integer}{W}*([fFpPnNuUmM]?[sS]) %% [ \t\b\f\r] { ; } \n { yylloc.first_line += 1; } /* Single-line comments start with -- and run to the end of the current line. These are very easy to handle. */ "--".* { comment_enter = YY_START; BEGIN(LCOMMENT); } . { yymore(); } \n { yylloc.first_line += 1; BEGIN(comment_enter); } /* The contents of C-style comments are ignored, like white space. */ "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); } . { ; } \n { yylloc.first_line += 1; } "*/" { BEGIN(comment_enter); } \'.\' { yylval.text = escape_apostrophe_and_dup(yytext); return CHARACTER_LITERAL; } (\"([^\"]|(\"\"))*?\")|(\"[^\"]*\") { /* first pattern: string literals with doubled quotation mark */ /* second pattern: string literals without doubled quotation */ yylval.text = escape_quot_and_dup(yytext); assert(yylval.text); return STRING_LITERAL; } [a-zA-Z_][a-zA-Z0-9_]* { for (char*cp = yytext ; *cp ; cp += 1) *cp = tolower(*cp); int rc = lexor_keyword_code(yytext, yyleng); switch (rc) { case IDENTIFIER: if(!are_underscores_correct(yytext)) std::cerr << "An invalid underscore in the identifier:" << yytext << std::endl; //yywarn(yylloc, "An invalid underscore in the identifier"); yylval.text = strdupnew(yytext); break; default: break; } return rc; } \\([^\\]|\\\\)*\\ { /* extended identifiers */ yylval.text = strdupnew(yytext); return IDENTIFIER; } {decimal_literal} { char*tmp = new char[strlen(yytext)+1]; char*dst, *src; int rc = INT_LITERAL; for (dst = tmp, src = yytext ; *src ; ++src) { if (*src == '_') continue; if (*src == '.') rc = REAL_LITERAL; *dst++ = *src; } *dst = 0; if (rc == REAL_LITERAL) { yylval.uni_real = strtod(tmp, 0); } else { yylval.uni_integer = strtoimax(tmp, 0, 10); } delete[]tmp; return rc; } {based_literal} { if(!are_underscores_correct(yytext) || !is_based_correct(yytext)) std::cerr << "An invalid form of based literal:" << yytext << std::endl; if(strchr(yytext, '.')) { double val = make_double_from_based(yytext); yylval.uni_real = val; return REAL_LITERAL; } else { int64_t val = make_long_from_based(yytext); yylval.uni_integer = val; return INT_LITERAL; } } {integer}?[sSuU]?[xXbBoOdD]\"[^\"]+\" { yylval.text = make_bitstring_literal(yytext); return BITSTRING_LITERAL; } /* Compound symbols */ "<=" { return LEQ; } ">=" { return GEQ; } ":=" { return VASSIGN; } "/=" { return NE; } "<>" { return BOX; } "**" { return EXP; } "=>" { return ARROW; } "<<" { return DLT; } ">>" { return DGT; } "??" { return CC; } "?=" { return M_EQ;} "?/=" { return M_NE;} "?<" { return M_LT; } "?<=" { return M_LEQ;} "?>" { return M_GT; } "?>=" {return M_GEQ; } . { return yytext[0]; } %% extern void yyparse_set_filepath(const char*path); /** * This function checks if underscores in an identifier * or in a number are correct. * * \return true is returned if underscores are placed * correctly according to specification */ static bool are_underscores_correct(char* text) { unsigned char underscore_allowed = 0; const char* cp; for( cp = text; *cp; ++cp) { if (*cp == '_') { if (!underscore_allowed || *(cp+1) == '\0') return 0; underscore_allowed = 0; } else underscore_allowed = 1; } return 1; } /** * This function checks if the format of a based number * is correct according to the VHDL standard * * \return true is returned if a based number * is formed well according to specification */ static bool is_based_correct(char* text) { char* ptr; //BASE examination char clean_base[4]; clean_base[3] = '\0'; char* clean_base_ptr = clean_base; for(ptr = text; ptr != strchr(text, '#'); ++ptr) { if(*ptr == '_') ++ptr; if(!(*ptr >= '0' && *ptr <= '9')) //the base uses chars other than digits return 0; if(*clean_base_ptr == '\0') break; *clean_base_ptr = *ptr; ++clean_base_ptr; } unsigned length = clean_base_ptr - clean_base; unsigned base; if(length > 2 || length == 0) return 0; //the base is too big or too small if(length == 2) { base = 10*(clean_base[0] - '0') + (clean_base[1] - '0'); //the base exceeds 16 or equals 0 if(base > 16 || base == 0) return 0; } else { //the base consists of one char and is equal to zero base = clean_base[0] - '0'; if(base == 0) return 0; } bool point = false; set allowed_chars; unsigned c; if(base <= 10) { for(c = 0; c < base; ++c) allowed_chars.insert(c + '0'); } else { for(c = 0; c < 10; ++c) allowed_chars.insert(c + '0'); for(c = 0; c < base - 10; ++c) allowed_chars.insert(c + 'a'); } //MANTISSA examination for(ptr = strchr(text, '#') + 1, length = 0; ptr != strrchr(text, '#'); ++ptr) { if(*ptr == '.') { //we found a dot and another one was already found if(point) return 0; else { //notice the fact of finding a point and continue, without increasing the length point = true; continue; } } //the number consists of other chars than allowed if(allowed_chars.find(*ptr) == allowed_chars.end()) return 0; ++length; } if(length == 0) return 0; //EXPONENT examination if(strchr(text, '\0') - strrchr(text, '#') > 1) { //the number contains an exponent if(*(strrchr(text, '#') + 2) == '-') return 0; length = 0; for(ptr = strrchr(text, '#')+2; *ptr != '\0'; ++ptr) { //the exponent consists of other chars than {'0'.,'9','a'..'f'} if(!((*ptr >= '0' && *ptr <= '9') || (*ptr >= 'a' && *ptr <= 'f'))) return 0; } } return 1; } /** * This function takes a string literal, gets rid of * quotation marks and copies the remaining characters * to a new persistent C-string * * \return pointer to the new string is returned */ static char* escape_quot_and_dup(char* text) { char* newstr = new char[strlen(text)+1]; unsigned old_idx, new_idx; for(new_idx = 0, old_idx = 0; old_idx < strlen(text); ) { if(text[old_idx] == '"' && old_idx == 0) { //the beginning of the literal ++old_idx; continue; } else if(text[old_idx] == '"' && text[old_idx+1] == '\0') { //the end newstr[new_idx] = '\0'; return newstr; } else if(text[old_idx] == '"' && text[old_idx+1] == '"') { newstr[new_idx++] = '"'; old_idx += 2; //jump across two chars } else { newstr[new_idx] = text[old_idx]; ++old_idx; ++new_idx; } } //the function should never reach this point return 0; } /** * This function takes a character literal, gets rid * of the apostrophes and returns new C-string * * \return pointer to the new string is returned */ static char* escape_apostrophe_and_dup(char* text) { char* newstr = new char[2]; newstr[0] = text[1]; newstr[1] = '\0'; return newstr; } static char*make_bitstring_bin(int width_prefix, bool sflag, bool, const char*src) { int src_len = strlen(src); if (width_prefix < 0) width_prefix = src_len; char*res = new char[width_prefix+1]; char*rp = res; if (width_prefix > src_len) { size_t pad = width_prefix - src_len; for (size_t idx = 0 ; idx < pad ; idx += 1) *rp++ = sflag? src[0] : '0'; } else if (src_len > width_prefix) { src += src_len - width_prefix; } while (*src) { *rp++ = *src++; } *rp = 0; return res; } static char*make_bitstring_oct(int width_prefix, bool sflag, bool, const char*src) { int src_len = strlen(src); if (width_prefix < 0) width_prefix = 3*src_len; char*res = new char[width_prefix+1]; char*rp = res + width_prefix; *rp = 0; rp -= 1; for (const char*sp = src + src_len - 1; sp >= src ; sp -= 1) { int val; switch (*sp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': val = *sp - '0'; *rp-- = (val&1)? '1' : '0'; if (rp >= res) *rp-- = (val&2)? '1' : '0'; if (rp >= res) *rp-- = (val&4)? '1' : '0'; break; default: *rp-- = *sp; if (rp >= res) *rp-- = *sp; if (rp >= res) *rp-- = *sp; break; } if (rp < res) break; } if (rp >= res) { char pad = sflag? src[0] : '0'; while (rp >= res) *rp-- = pad; } return res; } static char*make_bitstring_hex(int width_prefix, bool sflag, bool, const char*src) { int src_len = strlen(src); if (width_prefix <= 0) width_prefix = 4*src_len; char*res = new char[width_prefix+1]; char*rp = res + width_prefix; *rp = 0; rp -= 1; for (const char*sp = src + src_len - 1; sp >= src ; sp -= 1) { int val; switch (*sp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': val = *sp - '0'; *rp-- = (val&1)? '1' : '0'; if (rp >= res) *rp-- = (val&2)? '1' : '0'; if (rp >= res) *rp-- = (val&4)? '1' : '0'; if (rp >= res) *rp-- = (val&8)? '1' : '0'; break; case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': val = 10 + toupper(*sp) - 'A'; *rp-- = (val&1)? '1' : '0'; if (rp >= res) *rp-- = (val&2)? '1' : '0'; if (rp >= res) *rp-- = (val&4)? '1' : '0'; if (rp >= res) *rp-- = (val&8)? '1' : '0'; break; default: *rp-- = *sp; if (rp >= res) *rp-- = *sp; if (rp >= res) *rp-- = *sp; if (rp >= res) *rp-- = *sp; break; } if (rp < res) break; } if (rp >= res) { char pad = sflag? src[0] : '0'; while (rp >= res) *rp-- = pad; } return res; } static char*make_bitstring_dec(int, bool, bool, const char*) { assert(0); return 0; } static char* make_bitstring_literal(const char*text) { int width_prefix = -1; const char*cp = text; bool signed_flag = false; bool unsigned_flag = false; unsigned base = 0; // Parse out the explicit width, if present. if (size_t len = strspn(cp, "0123456789")) { width_prefix = 0; while (len > 0) { width_prefix *= 10; width_prefix += *cp - '0'; cp += 1; --len; } } else { width_prefix = -1; } // Detect and s/u flags. if (*cp == 's' || *cp == 'S') { signed_flag = true; cp += 1; } else if (*cp == 'u' || *cp == 'U') { unsigned_flag = true; cp += 1; } // Now get the base marker. switch (*cp) { case 'b': case 'B': base = 2; break; case 'o': case 'O': base = 8; break; case 'x': case 'X': base = 16; break; case 'd': case 'D': base = 10; break; default: assert(0); } cp += 1; char*simplified = new char [strlen(cp) + 1]; char*dp = simplified; assert(*cp == '"'); cp += 1; while (*cp && *cp != '"') { if (*cp == '_') { cp += 1; continue; } *dp++ = *cp++; } *dp = 0; char*res; switch (base) { case 2: res = make_bitstring_bin(width_prefix, signed_flag, unsigned_flag, simplified); break; case 8: res = make_bitstring_oct(width_prefix, signed_flag, unsigned_flag, simplified); break; case 10: res = make_bitstring_dec(width_prefix, signed_flag, unsigned_flag, simplified); break; case 16: res = make_bitstring_hex(width_prefix, signed_flag, unsigned_flag, simplified); break; default: assert(0); res = 0; } delete[]simplified; return res; } /** * This function takes a floating point based number * in form of a C-strings and converts it to a double. * * \return new double is returned */ static double make_double_from_based(char* text) { char* first_hash_ptr = strchr(text, '#'); char* second_hash_ptr = strrchr(text, '#'); char* last_char_ptr = strchr(text, '\0') - 1; //put null byte in lieu of hashes *first_hash_ptr = '\0'; *second_hash_ptr = '\0'; //now let's deduce the base unsigned base = (unsigned)strtol(text, 0, 10) ; double mantissa = 0.0; char*ptr = first_hash_ptr + 1; for( ; ptr != second_hash_ptr ; ++ptr) { if(*ptr == '.') break; if(*ptr != '_') { mantissa = mantissa*base + short_from_hex_char(*ptr); } } double fraction = 0.0; double factor = 1.0/base; for(++ptr ; ptr != second_hash_ptr; ++ptr) { if(*ptr != '_') { fraction = fraction + short_from_hex_char(*ptr)*factor; factor = factor / base; } } if(last_char_ptr == second_hash_ptr) //there is no exponent { return mantissa + fraction; } //now calculate the value of the exponent double exponent = 0.0; //leave 'e'/'E' and '+' ptr = *(second_hash_ptr + 2) == '+' ? second_hash_ptr + 3 : second_hash_ptr + 2; for( ; *ptr != '\0'; ++ptr) { if(*ptr != '_') { exponent = exponent*base + short_from_hex_char(*ptr); } } return pow(mantissa + fraction, exponent); } /** * This function takes a hexadecimal digit in form of * a char and returns its litteral value as short */ static unsigned short short_from_hex_char(char ch) { if(ch >= '0' && ch <= '9') return ch - '0'; else return ch - 'a' + 10; } /** * This function takes a based number in form of * a C-strings and converts it to a int64_t. * * \return new double is returned */ static int64_t make_long_from_based(char* text) { char* first_hash_ptr = strchr(text, '#'); char* second_hash_ptr = strrchr(text, '#'); char* end_ptr = strrchr(text, '\0'); //now lets deduce the base *first_hash_ptr = '\0'; unsigned base = (unsigned)strtol(text, 0, 10) ; char *ptr = first_hash_ptr + 1; int64_t mantissa = 0; for( ; ptr != second_hash_ptr ; ++ptr) { if(*ptr != '_') { mantissa = mantissa * base + short_from_hex_char(*ptr); } } //if there is an exponent if(end_ptr - second_hash_ptr > 1) { int64_t exponent = 0L; ptr = *(second_hash_ptr + 2) == '+' ? second_hash_ptr + 3 : second_hash_ptr + 2; for( ; *ptr != '\0'; ++ptr) { if(*ptr != '_') exponent = base*exponent + short_from_hex_char(*ptr); } return lpow(mantissa, exponent); } else return mantissa; } /** * Recursive power function for int64_t */ static int64_t lpow(int64_t left, int64_t right) { if(right == 0) return 1; else return left*lpow(left, right - 1); } yyscan_t prepare_lexor(FILE*fd) { yyscan_t scanner; yylex_init(&scanner); yyrestart(fd, scanner); return scanner; } void destroy_lexor(yyscan_t scanner) { yylex_destroy(scanner); } iverilog-10_1/vhdlpp/lexor_keyword.gperf000066400000000000000000000110311265551621300205550ustar00rootroot00000000000000/* * We need this to prevent -Wextra (-W) from complaining that the mask and * tokenType values are not initialized for the empty table entries. */ %define initializer-suffix ,0,0 %language=C++ %define class-name Lkwd %{ /* Command-line: gperf -o -i 7 --ignore-case -C -k '1-4,6,9,$' -H keyword_hash -N check_identifier -t lexor_keyword.gperf */ #include "vhdlpp_config.h" #include #include "compiler.h" #include "parse_api.h" #include "parse_wrap.h" %} struct lexor_keyword { const char*name; int mask; int tokenType; }; %% abs, GN_KEYWORD_2008, K_abs access, GN_KEYWORD_2008, K_access after, GN_KEYWORD_2008, K_after alias, GN_KEYWORD_2008, K_alias all, GN_KEYWORD_2008, K_all and, GN_KEYWORD_2008, K_and architecture, GN_KEYWORD_2008, K_architecture array, GN_KEYWORD_2008, K_array assert, GN_KEYWORD_2008, K_assert attribute, GN_KEYWORD_2008, K_attribute begin, GN_KEYWORD_2008, K_begin block, GN_KEYWORD_2008, K_block body, GN_KEYWORD_2008, K_body buffer, GN_KEYWORD_2008, K_buffer bus, GN_KEYWORD_2008, K_bus case, GN_KEYWORD_2008, K_case component, GN_KEYWORD_2008, K_component configuration, GN_KEYWORD_2008, K_configuration constant, GN_KEYWORD_2008, K_constant context, GN_KEYWORD_2008, K_context cover, GN_KEYWORD_2008, K_cover default, GN_KEYWORD_2008, K_default disconnect, GN_KEYWORD_2008, K_disconnect downto, GN_KEYWORD_2008, K_downto else, GN_KEYWORD_2008, K_else elsif, GN_KEYWORD_2008, K_elsif end, GN_KEYWORD_2008, K_end entity, GN_KEYWORD_2008, K_entity exit, GN_KEYWORD_2008, K_exit fairness, GN_KEYWORD_2008, K_fairness file, GN_KEYWORD_2008, K_file for, GN_KEYWORD_2008, K_for force, GN_KEYWORD_2008, K_force function, GN_KEYWORD_2008, K_function generate, GN_KEYWORD_2008, K_generate generic, GN_KEYWORD_2008, K_generic group, GN_KEYWORD_2008, K_group guarded, GN_KEYWORD_2008, K_guarded if, GN_KEYWORD_2008, K_if impure, GN_KEYWORD_2008, K_impure in, GN_KEYWORD_2008, K_in inertial, GN_KEYWORD_2008, K_inertial inout, GN_KEYWORD_2008, K_inout is, GN_KEYWORD_2008, K_is label, GN_KEYWORD_2008, K_label library, GN_KEYWORD_2008, K_library linkage, GN_KEYWORD_2008, K_linkage literal, GN_KEYWORD_2008, K_literal loop, GN_KEYWORD_2008, K_loop map, GN_KEYWORD_2008, K_map mod, GN_KEYWORD_2008, K_mod nand, GN_KEYWORD_2008, K_nand new, GN_KEYWORD_2008, K_new next, GN_KEYWORD_2008, K_next nor, GN_KEYWORD_2008, K_nor not, GN_KEYWORD_2008, K_not null, GN_KEYWORD_2008, K_null of, GN_KEYWORD_2008, K_of on, GN_KEYWORD_2008, K_on open, GN_KEYWORD_2008, K_open or, GN_KEYWORD_2008, K_or others, GN_KEYWORD_2008, K_others out, GN_KEYWORD_2008, K_out package, GN_KEYWORD_2008, K_package port, GN_KEYWORD_2008, K_port postponed, GN_KEYWORD_2008, K_postponed procedure, GN_KEYWORD_2008, K_procedure process, GN_KEYWORD_2008, K_process property, GN_KEYWORD_2008, K_property protected, GN_KEYWORD_2008, K_protected pure, GN_KEYWORD_2008, K_pure range, GN_KEYWORD_2008, K_range record, GN_KEYWORD_2008, K_record register, GN_KEYWORD_2008, K_register reject, GN_KEYWORD_2008, K_reject release, GN_KEYWORD_2008, K_release rem, GN_KEYWORD_2008, K_rem report, GN_KEYWORD_2008, K_report restrict, GN_KEYWORD_2008, K_restrict return, GN_KEYWORD_2008, K_return reverse_range, GN_KEYWORD_2008, K_reverse_range rol, GN_KEYWORD_2008, K_rol ror, GN_KEYWORD_2008, K_ror select, GN_KEYWORD_2008, K_select sequence, GN_KEYWORD_2008, K_sequence severity, GN_KEYWORD_2008, K_severity signal, GN_KEYWORD_2008, K_signal shared, GN_KEYWORD_2008, K_shared sla, GN_KEYWORD_2008, K_sla sll, GN_KEYWORD_2008, K_sll sra, GN_KEYWORD_2008, K_sra srl, GN_KEYWORD_2008, K_srl strong, GN_KEYWORD_2008, K_strong subtype, GN_KEYWORD_2008, K_subtype then, GN_KEYWORD_2008, K_then to, GN_KEYWORD_2008, K_to transport, GN_KEYWORD_2008, K_transport type, GN_KEYWORD_2008, K_type unaffected, GN_KEYWORD_2008, K_unaffected units, GN_KEYWORD_2008, K_units until, GN_KEYWORD_2008, K_until use, GN_KEYWORD_2008, K_use variable, GN_KEYWORD_2008, K_variable vmode, GN_KEYWORD_2008, K_vmode vprop, GN_KEYWORD_2008, K_vprop vunit, GN_KEYWORD_2008, K_vunit wait, GN_KEYWORD_2008, K_wait when, GN_KEYWORD_2008, K_when while, GN_KEYWORD_2008, K_while with, GN_KEYWORD_2008, K_with xnor, GN_KEYWORD_2008, K_xnor xor, GN_KEYWORD_2008, K_xor %% int lexor_keyword_mask = GN_KEYWORD_2008; int lexor_keyword_code(const char*str, unsigned nstr) { const struct lexor_keyword*rc = Lkwd::check_identifier(str, nstr); if (rc == 0) return IDENTIFIER; else if ((rc->mask & lexor_keyword_mask) == 0) return IDENTIFIER; else return rc->tokenType; } iverilog-10_1/vhdlpp/library.cc000066400000000000000000000302561265551621300166200ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # define __STDC_LIMIT_MACROS # include "parse_misc.h" # include "compiler.h" # include "package.h" # include "std_types.h" # include # include # include # include # include # include # include using namespace std; /* * The library_work_path is the path to the work directory. */ static const char*library_work_path = 0; /* * The library_search_path is a list of directory names to search to * find a named library. The library name is the name given to the * "import" statement. */ static list library_search_path; /* * The "import" statement causes me to build a map of a library name * to a library directory. */ static map library_dir; /* * The "libraries" table maps library name to a set of packages. This * map is filled in by "use" statements. */ struct library_contents { map packages; }; static map libraries; void library_add_directory(const char*directory) { // Make sure the directory path really is a directory. Ignore // it if it is not. struct stat stat_buf; int rc = stat(directory, &stat_buf); if (rc < 0 || !S_ISDIR(stat_buf.st_mode)) { return; } library_search_path.push_front(directory); } SubprogramHeader*library_find_subprogram(perm_string name) { SubprogramHeader*subp = NULL; map::const_iterator lib_it; for(lib_it = libraries.begin(); lib_it != libraries.end(); ++lib_it) { const struct library_contents&lib = lib_it->second; map::const_iterator pack_it; for(pack_it = lib.packages.begin(); pack_it != lib.packages.end(); ++pack_it) { if((subp = pack_it->second->find_subprogram(name))) return subp; } } return NULL; } static void store_package_in_work(const Package*pack); static string make_work_package_path(const char*name) { return string(library_work_path).append("/").append(name).append(".pkg"); } static string make_library_package_path(perm_string lib_name, perm_string name) { string path = library_dir[lib_name]; if (path == "") return ""; path = path.append("/").append(name).append(".pkg"); return path; } static void import_ieee(void); static void import_ieee_use(ActiveScope*res, perm_string package, perm_string name); static void import_std_use(const YYLTYPE&loc, ActiveScope*res, perm_string package, perm_string name); static void dump_library_package(ostream&file, perm_string lname, perm_string pname, Package*pack) { file << "package " << lname << "." << pname << endl; if (pack) { pack->dump_scope(file); } else { file << " " << endl; } file << "end package " << lname << "." << pname << endl; } static void dump_library_packages(ostream&file, perm_string lname, mappackages) { for (map::iterator cur = packages.begin() ; cur != packages.end() ; ++cur) { dump_library_package(file, lname, cur->first, cur->second); } } void dump_libraries(ostream&file) { for (map::iterator cur = libraries.begin() ; cur != libraries.end() ; ++cur) { dump_library_packages(file, cur->first, cur->second.packages); } } /* * This function saves a package into the named library. Create the * library if necessary. The parser uses this when it is finished with * a package declaration. */ void library_save_package(perm_string parse_library_name, Package*pack) { perm_string use_libname = parse_library_name.str() ? parse_library_name : perm_string::literal("work"); struct library_contents&lib = libraries[use_libname]; lib.packages[pack->name()] = pack; // If this is a work package, and we are NOT parsing the work // library right now, then store it in the work library. if (parse_library_name.str() == 0) store_package_in_work(pack); else pack->set_library(parse_library_name); } /* * The parser uses this function in the package body rule to recall * the package that was declared earlier. */ Package*library_recall_package(perm_string parse_library_name, perm_string package_name) { perm_string use_libname = parse_library_name.str() ? parse_library_name : perm_string::literal("work"); map::iterator lib = libraries.find(use_libname); if (lib == libraries.end()) return 0; map::iterator pkg = lib->second.packages.find(package_name); if (pkg == lib->second.packages.end()) return 0; return pkg->second; } static void import_library_name(const YYLTYPE&loc, perm_string name) { if (library_dir[name] != string()) return; for (list::const_iterator cur = library_search_path.begin() ; cur != library_search_path.end() ; ++cur) { string curdir = *cur; string try_path = curdir.append("/").append(name); struct stat stat_buf; int rc = stat(try_path.c_str(), &stat_buf); if (rc < 0) continue; if (!S_ISDIR(stat_buf.st_mode)) continue; library_dir[name] = try_path; return; } errormsg(loc, "library import cannot find library %s\n", name.str()); } void library_import(const YYLTYPE&loc, const std::list*names) { for (std::list::const_iterator cur = names->begin() ; cur != names->end() ; ++cur) { if (*cur == "ieee") { // The ieee library is special and handled by an // internal function. import_ieee(); } else if (*cur == "std") { // The std library is always implicitly imported. } else if (*cur == "work") { // The work library is always implicitly imported. } else { // Otherwise, do a generic library import import_library_name(loc, *cur); } } } void library_use(const YYLTYPE&loc, ActiveScope*res, const char*libname, const char*package, const char*name) { if (libname == 0) { errormsg(loc, "error: No library name for this use clause?\n"); return; } perm_string use_library = lex_strings.make(libname); perm_string use_package = lex_strings.make(package); perm_string use_name = name? lex_strings.make(name) : perm_string::literal("all"); // Special case handling for the IEEE library. if (use_library == "ieee") { import_ieee_use(res, use_package, use_name); return; } // Special case handling for the STD library. if (use_library == "std") { import_std_use(loc, res, use_package, use_name); return; } struct library_contents&lib = libraries[use_library]; Package*pack = lib.packages[use_package]; // If the package is not found in the work library already // parsed, then see if it exists unparsed. if (use_library=="work" && pack == 0) { string path = make_work_package_path(use_package.str()); parse_source_file(path.c_str(), use_library); pack = lib.packages[use_package]; } else if (use_library != "ieee" && pack == 0) { string path = make_library_package_path(use_library, use_package); if (path == "") { errormsg(loc, "Unable to find library %s\n", use_library.str()); return; } int rc = parse_source_file(path.c_str(), use_library); if (rc < 0) errormsg(loc, "Unable to open library file %s\n", path.c_str()); else if (rc > 0) errormsg(loc, "Errors in library file %s\n", path.c_str()); else pack = lib.packages[use_package]; } // If the package is still not found, then error. if (pack == 0) { errormsg(loc, "No package %s in library %s\n", use_package.str(), use_library.str()); return; } // We have a package that we are going to extract names // from. Use the name to get the selected objects, and write // results into the "res" members. if (use_name == "all") { res->use_from(pack); return; } if (ComponentBase*cur = pack->find_component(use_name)) { res->bind_name(use_name, cur); return; } errormsg(loc, "No such name %s in package %s\n", use_name.str(), pack->name().str()); } static void import_ieee(void) { } static void import_ieee_use_std_logic_1164(ActiveScope*res, perm_string name) { bool all_flag = name=="all"; if (all_flag || name == "std_logic_vector") { res->use_name(perm_string::literal("std_logic_vector"), &primitive_STDLOGIC_VECTOR); } } static void import_ieee_use_std_logic_arith(ActiveScope*, perm_string) { } static void import_ieee_use_numeric_bit(ActiveScope*res, perm_string name) { bool all_flag = name=="all"; if (all_flag || name == "signed") { res->use_name(perm_string::literal("signed"), &primitive_SIGNED); } if (all_flag || name == "unsigned") { res->use_name(perm_string::literal("unsigned"), &primitive_UNSIGNED); } } static void import_ieee_use_numeric_std(ActiveScope*res, perm_string name) { bool all_flag = name=="all"; if (all_flag || name == "signed") { res->use_name(perm_string::literal("signed"), &primitive_SIGNED); } if (all_flag || name == "unsigned") { res->use_name(perm_string::literal("unsigned"), &primitive_UNSIGNED); } } static void import_ieee_use(ActiveScope*res, perm_string package, perm_string name) { if (package == "std_logic_1164") { import_ieee_use_std_logic_1164(res, name); return; } if (package == "std_logic_arith") { import_ieee_use_std_logic_arith(res, name); return; } if (package == "numeric_bit") { import_ieee_use_numeric_bit(res, name); return; } if (package == "numeric_std") { import_ieee_use_numeric_std(res, name); return; } } static void import_std_use(const YYLTYPE&loc, ActiveScope*/*res*/, perm_string package, perm_string name) { if (package == "standard") { // do nothing return; } else if (package == "textio") { cerr << "warning: textio package not really supported" << endl; return; } else { sorrymsg(loc, "package %s of library %s not yet supported", package.str(), name.str()); return; } } void library_set_work_path(const char*path) { assert(library_work_path == 0); library_work_path = path; } list work_packages; static void store_package_in_work(const Package*pack) { work_packages.push_back(pack); } static int emit_packages(perm_string, const map&packages) { int errors = 0; for (map::const_iterator cur = packages.begin() ; cur != packages.end() ; ++cur) { errors += cur->second->emit_package(cout); } for (list::const_iterator cur = work_packages.begin() ; cur != work_packages.end(); ++cur) { string path = make_work_package_path((*cur)->name()); ofstream file (path.c_str(), ios_base::out); (*cur)->write_to_stream(file); } return errors; } int emit_packages(void) { int errors = 0; for (map::iterator cur = libraries.begin() ; cur != libraries.end() ; ++cur) { errors += emit_packages(cur->first, cur->second.packages); } return 0; } iverilog-10_1/vhdlpp/library.h000066400000000000000000000021571265551621300164610ustar00rootroot00000000000000#ifndef IVL_library_H #define IVL_library_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ class SubprogramHeader; extern void library_set_work_path(const char*work_path); extern void library_add_directory(const char*directory); extern SubprogramHeader*library_find_subprogram(perm_string name); #endif /* IVL_library_H */ iverilog-10_1/vhdlpp/main.cc000066400000000000000000000166421265551621300161030ustar00rootroot00000000000000 const char COPYRIGHT[] = "Copyright (c) 2011-2012 Stephen Williams (steve@icarus.com)\n" "Copyright CERN 2012 / Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vhdlpp_config.h" # include "version_base.h" # include "version_tag.h" /* * Usage: vhdlpp [flags] sourcefile... * Flags: * ** -D * This activates various sorts of debugging aids. The * specifies which debugging aid to activate. Valid tokens are: * * yydebug | no-yydebug * Enable (disable) debug prints from the bison parser * * libraries= * Enable debugging of library support by dumping library * information to the file named . * * elaboration= * Enable debugging of elaboration by dumping elaboration * process information to the file named . * * entities= * Enable debugging of elaborated entities by writing the * elaboration results to the file named . * ** -v * Verbose operation. Display verbose non-debug information. * ** -V * Version. Print the version of this binary. * ** -w * Work path. This sets the path to the working library * directory. I write into that directory files for packages that * I declare, and I read from that directory packages that are * already declared. The default path is "ivl_vhdl_work". */ const char NOTICE[] = " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" " (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public License along\n" " with this program; if not, write to the Free Software Foundation, Inc.,\n" " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; # include "compiler.h" # include "library.h" # include "std_funcs.h" # include "std_types.h" # include "parse_api.h" # include "vtype.h" # include # include # include # include # include # include #if defined(HAVE_GETOPT_H) # include #endif # include // MinGW only supports mkdir() with a path. If this stops working because // we need to use _mkdir() for mingw-w32 and mkdir() for mingw-w64 look // at using the autoconf AX_FUNC_MKDIR macro to figure this all out. #if defined(__MINGW32__) # include # define mkdir(path, mode) mkdir(path) #endif bool verbose_flag = false; // Where to dump design entities const char*dump_design_entities_path = 0; const char*dump_libraries_path = 0; const char*debug_log_path = 0; bool debug_elaboration = false; ofstream debug_log_file; extern void dump_libraries(ostream&file); extern void parser_cleanup(); static void process_debug_token(const char*word) { if (strcmp(word, "yydebug") == 0) { yydebug = 1; } else if (strcmp(word, "no-yydebug") == 0) { yydebug = 0; } else if (strncmp(word, "entities=", 9) == 0) { dump_design_entities_path = strdup(word+9); } else if (strncmp(word, "libraries=", 10) == 0) { dump_libraries_path = strdup(word+10); } else if (strncmp(word, "log=", 4) == 0) { debug_log_path = strdup(word+4); } else if (strcmp(word, "elaboration") == 0) { debug_elaboration = true; } } int main(int argc, char*argv[]) { int opt; int rc; const char*work_path = "ivl_vhdl_work"; while ( (opt=getopt(argc, argv, "D:L:vVw:")) != EOF) switch (opt) { case 'D': process_debug_token(optarg); break; case 'L': library_add_directory(optarg); break; case 'v': fprintf(stderr, "Icarus Verilog VHDL Parse version " VERSION " (" VERSION_TAG ")\n\n"); fprintf(stderr, "%s\n\n", COPYRIGHT); fputs(NOTICE, stderr); verbose_flag = true; break; case 'V': fprintf(stdout, "Icarus Verilog VHDL Parse version " VERSION " (" VERSION_TAG ")\n\n"); fprintf(stdout, "%s\n\n", COPYRIGHT); fputs(NOTICE, stdout); break; case 'w': work_path = optarg; break; } if (debug_log_path) { debug_log_file.open(debug_log_path); } if ( (rc = mkdir(work_path, 0777)) < 0 ) { if (errno != EEXIST) { fprintf(stderr, "Icarus Verilog VHDL unable to create work directory %s, errno=%d\n", work_path, errno); return -1; } struct stat stat_buf; rc = stat(work_path, &stat_buf); if (!S_ISDIR(stat_buf.st_mode)) { fprintf(stderr, "Icarus Verilog VHDL work path `%s' is not a directory.\n", work_path); return -1; } } std::cout.precision(std::numeric_limits::digits10); library_set_work_path(work_path); preload_global_types(); preload_std_funcs(); int errors = 0; for (int idx = optind ; idx < argc ; idx += 1) { parse_errors = 0; parse_sorrys = 0; rc = parse_source_file(argv[idx], perm_string()); if (rc < 0) return 1; if (verbose_flag) fprintf(stderr, "parse_source_file() returns %d, parse_errors=%d, parse_sorrys=%d\n", rc, parse_errors, parse_sorrys); if (parse_errors > 0) { fprintf(stderr, "Encountered %d errors parsing %s\n", parse_errors, argv[idx]); } if (parse_sorrys > 0) { fprintf(stderr, "Encountered %d unsupported constructs parsing %s\n", parse_sorrys, argv[idx]); } if (parse_errors || parse_sorrys) { errors += parse_errors; errors += parse_sorrys; break; } } if (dump_libraries_path) { ofstream file(dump_libraries_path); dump_libraries(file); } if (dump_design_entities_path) { ofstream file(dump_design_entities_path); dump_design_entities(file); } if (errors > 0) { parser_cleanup(); return 2; } errors = elaborate_entities(); if (errors > 0) { fprintf(stderr, "%d errors elaborating design.\n", errors); parser_cleanup(); return 3; } emit_std_types(cout); errors = emit_packages(); if (errors > 0) { fprintf(stderr, "%d errors emitting packages.\n", errors); parser_cleanup(); return 4; } errors = emit_entities(); if (errors > 0) { fprintf(stderr, "%d errors emitting design.\n", errors); parser_cleanup(); return 4; } parser_cleanup(); return 0; } iverilog-10_1/vhdlpp/package.cc000066400000000000000000000115231265551621300165430ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "package.h" # include "entity.h" # include "subprogram.h" # include "parse_misc.h" # include "std_types.h" # include "ivl_assert.h" Package::Package(perm_string n, const ActiveScope&ref) : Scope(ref), name_(n) { } Package::~Package() { ScopeBase::cleanup(); } void Package::set_library(perm_string lname) { ivl_assert(*this, from_library_.str() == 0); from_library_ = lname; } /* * The Package::write_to_stream is used to write the package to the * work space (or library) so writes proper VHDL that the library * parser can bring back in as needed. */ void Package::write_to_stream(ostream&fd) const { fd << "package " << name_ << " is" << endl; // Start out pre-declaring all the type definitions so that // there is no confusion later in the package between types // and identifiers. for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { const VTypeDef*def = dynamic_cast (cur->second); if (def == 0) continue; // Do not include global types in types dump if (is_global_type(cur->first)) continue; fd << "type " << cur->first << ";" << endl; } for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { const VTypeDef*def = dynamic_cast (cur->second); if (def == 0) continue; // Do not include global types in types dump if (is_global_type(cur->first)) continue; fd << "type " << cur->first << ";" << endl; } for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { // Do not include global types in types dump if (is_global_type(cur->first)) continue; if(!dynamic_cast(cur->second)) fd << "sub"; fd << "type " << cur->first << " is "; cur->second->write_type_to_stream(fd); fd << "; -- imported" << endl; } for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { // Do not include global types in types dump if (is_global_type(cur->first)) continue; if(!dynamic_cast(cur->second)) fd << "sub"; fd << "type " << cur->first << " is "; cur->second->write_type_to_stream(fd); fd << ";" << endl; } for (map::const_iterator cur = cur_constants_.begin() ; cur != cur_constants_.end() ; ++ cur) { if (cur->second==0 || cur->second->typ==0) { fd << "-- const " << cur->first << " has errors." << endl; continue; } fd << "constant " << cur->first << ": "; cur->second->typ->write_to_stream(fd); fd << " := "; cur->second->val->write_to_stream(fd); fd << ";" << endl; } for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { cur->second->write_to_stream(fd); fd << ";" << endl; } for (map::const_iterator cur = old_components_.begin() ; cur != old_components_.end() ; ++cur) { cur->second->write_to_stream(fd); } for (map::const_iterator cur = new_components_.begin() ; cur != new_components_.end() ; ++cur) { cur->second->write_to_stream(fd); } fd << "end package " << name_ << ";" << endl; fd << "package body " << name_ << " is" << endl; for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { SubprogramHeader*subp = cur->second; if(subp->body()) { subp->write_to_stream(fd); fd << " is" << endl; subp->body()->write_to_stream(fd); } } fd << "end " << name_ << ";" << endl; } iverilog-10_1/vhdlpp/package.h000066400000000000000000000032411265551621300164030ustar00rootroot00000000000000#ifndef IVL_package_H #define IVL_package_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "scope.h" # include "LineInfo.h" # include class Package : public Scope, public LineInfo { public: Package(perm_string name, const ActiveScope&ref); ~Package(); // The the library from which this package came. Having a // source library influences the emit_package() method. void set_library(perm_string); perm_string name() const { return name_; } SubprogramHeader* recall_subprogram(perm_string name) const; // This method writes a package header to a library file. void write_to_stream(std::ostream&fd) const; int emit_package(std::ostream&fd) const; private: perm_string from_library_; perm_string name_; }; #endif /* IVL_package_H */ iverilog-10_1/vhdlpp/package_emit.cc000066400000000000000000000060601265551621300175610ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "package.h" # include "subprogram.h" # include # include "ivl_assert.h" using namespace std; int Package::emit_package(ostream&fd) const { // Don't emit the package if there is nothing in it that SV // cares about. if (cur_types_.empty() && cur_constants_.empty() && cur_subprograms_.empty()) return 0; int errors = 0; fd << "`ifndef package_" << name() << endl; fd << "`define package_" << name() << endl; // Only emit types that were defined within this package. Skip // the types that were imported from elsewhere. typedef_context_t typedef_ctx; for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++ cur) { if(const VTypeDef*def = dynamic_cast(cur->second)) errors += def->emit_typedef(fd, typedef_ctx); //fd << "typedef "; //errors += cur->second->emit_def(fd, //dynamic_cast(cur->second) ? empty_perm_string : cur->first); //fd << " ;" << endl; } //for (map::const_iterator cur = use_constants_.begin() //; cur != use_constants_.end() ; ++cur) { //fd << "localparam \\" << cur->first << " = "; //errors += cur->second->val->emit_package(fd); //fd << ";" << endl; //} //for (map::const_iterator cur = cur_constants_.begin() //; cur != cur_constants_.end() ; ++cur) { //fd << "localparam " << cur->first << " = "; //errors += cur->second->val->emit_package(fd); //fd << ";" << endl; //} fd << "package \\" << name() << " ;" << endl; for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++ cur) { // Do not emit unbounded functions, we will just need fixed instances later if(!cur->second->unbounded()) errors += cur->second->emit_package(fd); else fd << "/* function " << cur->second->name() << " has to be instantiated, skipping */" << endl; } fd << "endpackage /* " << name() << " */" << endl; fd << "`endif" << endl; return errors; } iverilog-10_1/vhdlpp/parse.y000066400000000000000000002207621265551621300161540ustar00rootroot00000000000000 %pure-parser %lex-param { yyscan_t yyscanner } %parse-param {yyscan_t yyscanner } %parse-param {const char*file_path} %parse-param {perm_string parse_library_name} %{ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2014 / Stephen Williams (steve@icarus.com), * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vhdlpp_config.h" # include "vhdlint.h" # include "vhdlreal.h" # include "compiler.h" # include "parse_api.h" # include "parse_misc.h" # include "architec.h" # include "expression.h" # include "sequential.h" # include "subprogram.h" # include "package.h" # include "vsignal.h" # include "vtype.h" # include "std_funcs.h" # include "std_types.h" # include # include # include # include # include # include # include "parse_types.h" # include # include inline void FILE_NAME(LineInfo*tmp, const struct yyltype&where) { tmp->set_lineno(where.first_line); tmp->set_file(filename_strings.make(where.text)); } /* Recent version of bison expect that the user supply a YYLLOC_DEFAULT macro that makes up a yylloc value from existing values. I need to supply an explicit version to account for the text field, that otherwise won't be copied. */ # define YYLLOC_DEFAULT(Current, Rhs, N) do { \ (Current).first_line = (Rhs)[1].first_line; \ (Current).text = file_path; /*(Rhs)[1].text;*/ } while (0) static void yyerror(YYLTYPE*yyllocp,yyscan_t yyscanner,const char*file_path,bool, const char*msg); int parse_errors = 0; int parse_sorrys = 0; /* * The parser calls yylex to get the next lexical token. It is only * called by the bison-generated parser. */ extern int yylex(union YYSTYPE*yylvalp,YYLTYPE*yyllocp,yyscan_t yyscanner); /* * Create an initial scope that collects all the global * declarations. Also save a stack of previous scopes, as a way to * manage lexical scopes. */ static ActiveScope*active_scope = new ActiveScope; static stack scope_stack; static SubprogramHeader*active_sub = NULL; // perm_strings for attributes const static perm_string left_attr = perm_string::literal("left"); const static perm_string right_attr = perm_string::literal("right"); /* * When a scope boundary starts, call the push_scope function to push * a scope context. Preload this scope context with the contents of * the parent scope, then make this the current scope. When the scope * is done, the pop_scope function pops the scope off the stack and * resumes the scope that was the parent. */ static void push_scope(void) { assert(active_scope); scope_stack.push(active_scope); active_scope = new ActiveScope (active_scope); } static void pop_scope(void) { delete active_scope; assert(! scope_stack.empty()); active_scope = scope_stack.top(); scope_stack.pop(); } static bool is_subprogram_param(perm_string name) { if(!active_sub) return false; return (active_sub->find_param(name) != NULL); } void preload_global_types(void) { generate_global_types(active_scope); } //Remove the scope created at the beginning of parser's work. //After the parsing active_scope should keep it's address static void delete_global_scope(void) { active_scope->destroy_global_scope(); delete active_scope; } //delete global entities that were gathered over the parsing process static void delete_design_entities(void) { for(map::iterator cur = design_entities.begin() ; cur != design_entities.end(); ++cur) delete cur->second; } //clean the mess caused by the parser void parser_cleanup(void) { delete_design_entities(); delete_global_scope(); delete_std_funcs(); lex_strings.cleanup(); } const VType*parse_type_by_name(perm_string name) { return active_scope->find_type(name); } // This function is called when an aggregate expression is detected by // the parser. It makes the ExpAggregate. It also tries to detect the // special case that the aggregate is really a primary. The problem is // that this: // ( ) // also matches the pattern: // ( [ choices => ] ... ) // so try to assume that a single expression in parentheses is a // primary and fix the parse by returning an Expression instead of an // ExpAggregate. static Expression*aggregate_or_primary(const YYLTYPE&loc, std::list*el) { if (el->size() != 1) { ExpAggregate*tmp = new ExpAggregate(el); FILE_NAME(tmp,loc); return tmp; } ExpAggregate::element_t*el1 = el->front(); if (el1->count_choices() > 0) { ExpAggregate*tmp = new ExpAggregate(el); FILE_NAME(tmp,loc); return tmp; } return el1->extract_expression(); } static list* record_elements(list*names, const VType*type) { list*res = new list; for (list::iterator cur = names->begin() ; cur != names->end() ; ++cur) { res->push_back(new VTypeRecord::element_t(*cur, type)); } return res; } static void touchup_interface_for_functions(std::list*ports) { for (list::iterator cur = ports->begin() ; cur != ports->end() ; ++cur) { InterfacePort*curp = *cur; if (curp->mode == PORT_NONE) curp->mode = PORT_IN; } } %} %union { port_mode_t port_mode; char*text; std::list* name_list; bool flag; int64_t uni_integer; double uni_real; Expression*expr; std::list* expr_list; SequentialStmt* sequ; std::list*sequ_list; IfSequential::Elsif*elsif; std::list*elsif_list; ExpConditional::case_t*exp_options; std::list*exp_options_list; CaseSeqStmt::CaseStmtAlternative* case_alt; std::list* case_alt_list; named_expr_t*named_expr; std::list*named_expr_list; entity_aspect_t* entity_aspect; instant_list_t* instantiation_list; std::pair* component_specification; const VType* vtype; prange_t* range; std::list*range_list; ExpArithmetic::fun_t arithmetic_op; std::list*adding_terms; ExpAggregate::choice_t*choice; std::list*choice_list; ExpAggregate::element_t*element; std::list*element_list; std::list*record_elements; std::list* interface_list; Architecture::Statement* arch_statement; std::list* arch_statement_list; ReportStmt::severity_t severity; SubprogramHeader*subprogram; }; /* The keywords are all tokens. */ %token K_abs K_access K_after K_alias K_all K_and K_architecture %token K_array K_assert K_assume K_assume_guarantee K_attribute %token K_begin K_block K_body K_buffer K_bus %token K_case K_component K_configuration K_constant K_context K_cover %token K_default K_disconnect K_downto %token K_else K_elsif K_end K_entity K_exit %token K_fairness K_file K_for K_force K_function %token K_generate K_generic K_group K_guarded %token K_if K_impure K_in K_inertial K_inout K_is %token K_label K_library K_linkage K_literal K_loop %token K_map K_mod %token K_nand K_new K_next K_nor K_not K_null %token K_of K_on K_open K_or K_others K_out %token K_package K_parameter K_port K_postponed K_procedure K_process %token K_property K_protected K_pure %token K_range K_record K_register K_reject K_release K_rem K_report %token K_restrict K_restrict_guarantee K_return K_reverse_range K_rol K_ror %token K_select K_sequence K_severity K_signal K_shared %token K_sla K_sll K_sra K_srl K_strong K_subtype %token K_then K_to K_transport K_type %token K_unaffected K_units K_until K_use %token K_variable K_vmode K_vprop K_vunit %token K_wait K_when K_while K_with %token K_xnor K_xor /* Identifiers that are not keywords are identifiers. */ %token IDENTIFIER %token INT_LITERAL %token REAL_LITERAL %token STRING_LITERAL CHARACTER_LITERAL BITSTRING_LITERAL /* compound symbols */ %token LEQ GEQ VASSIGN NE BOX EXP ARROW DLT DGT CC M_EQ M_NE M_LT M_LEQ M_GT M_GEQ /* The rules may have types. */ %type direction %type adding_operator %type simple_expression_terms %type interface_element interface_list %type port_clause port_clause_opt %type generic_clause generic_clause_opt %type parameter_list parameter_list_opt %type mode mode_opt %type entity_aspect entity_aspect_opt binding_indication binding_indication_semicolon_opt %type instantiation_list %type component_specification %type concurrent_statement component_instantiation_statement %type concurrent_conditional_signal_assignment %type concurrent_signal_assignment_statement concurrent_simple_signal_assignment %type for_generate_statement generate_statement if_generate_statement %type process_statement selected_signal_assignment %type architecture_statement_part generate_statement_body %type choice %type choices %type element_association %type element_association_list %type expression factor primary relation %type expression_logical expression_logical_and expression_logical_or %type expression_logical_xnor expression_logical_xor %type name prefix selected_name %type shift_expression signal_declaration_assign_opt %type simple_expression simple_expression_2 term %type variable_declaration_assign_opt waveform_element interface_element_expression %type waveform waveform_elements %type name_list expression_list %type process_sensitivity_list process_sensitivity_list_opt %type selected_names use_clause %type association_element %type association_list port_map_aspect port_map_aspect_opt %type generic_map_aspect generic_map_aspect_opt %type composite_type_definition record_type_definition %type subtype_indication type_definition %type element_declaration element_declaration_list %type architecture_body_start package_declaration_start %type package_body_start %type identifier_opt identifier_colon_opt logical_name suffix instantiated_unit %type logical_name_list identifier_list %type enumeration_literal_list enumeration_literal %type if_statement_else list_of_statements %type sequence_of_statements subprogram_statement_part %type sequential_statement if_statement signal_assignment signal_assignment_statement %type case_statement procedure_call procedure_call_statement %type loop_statement variable_assignment variable_assignment_statement %type assertion_statement report_statement return_statement wait_statement %type range %type range_list index_constraint %type case_statement_alternative %type case_statement_alternative_list %type if_statement_elsif %type if_statement_elsif_list if_statement_elsif_list_opt %type else_when_waveform selected_waveform %type else_when_waveforms selected_waveform_list %type function_specification procedure_specification %type subprogram_specification subprogram_body_start %type severity severity_opt %% /* The design_file is the root for the VHDL parse. This rule is also where I put some run-time initializations. */ design_file : { yylloc.text = file_path; } design_units ; adding_operator : '+' { $$ = ExpArithmetic::PLUS; } | '-' { $$ = ExpArithmetic::MINUS; } | '&' { $$ = ExpArithmetic::xCONCAT; } ; architecture_body : architecture_body_start K_of IDENTIFIER { bind_entity_to_active_scope($3, active_scope); } K_is block_declarative_items_opt K_begin architecture_statement_part K_end K_architecture_opt identifier_opt ';' { Architecture*tmp = new Architecture(lex_strings.make($1), *active_scope, *$8); FILE_NAME(tmp, @1); bind_architecture_to_entity($3, tmp); if ($11 && tmp->get_name() != $11) errormsg(@1, "Architecture name doesn't match closing name.\n"); delete[]$1; delete[]$3; delete $8; pop_scope(); if ($11) delete[]$11; } ; architecture_body_start : K_architecture IDENTIFIER { $$ = $2; push_scope(); } ; /* * The architecture_statement_part is a list of concurrent * statements. */ architecture_statement_part : architecture_statement_part concurrent_statement { std::list*tmp = $1; if ($2) tmp->push_back($2); $$ = tmp; } | concurrent_statement { std::list*tmp = new std::list; if ($1) tmp->push_back($1); $$ = tmp; } | error ';' { $$ = 0; errormsg(@1, "Syntax error in architecture statement.\n"); yyerrok; } ; assertion_statement : K_assert expression report_statement { ReportStmt*report = dynamic_cast($3); assert(report); AssertStmt*tmp = new AssertStmt($2, report->message().c_str(), report->severity()); delete report; FILE_NAME(tmp,@2); $$ = tmp; } | K_assert expression severity_opt ';' { AssertStmt*tmp = new AssertStmt($2, NULL, $3); FILE_NAME(tmp,@2); $$ = tmp; } ; association_element : IDENTIFIER ARROW expression { named_expr_t*tmp = new named_expr_t(lex_strings.make($1), $3); delete[]$1; $$ = tmp; } | IDENTIFIER ARROW K_open { named_expr_t*tmp = new named_expr_t(lex_strings.make($1), 0); delete[]$1; $$ = tmp; } | IDENTIFIER ARROW error { errormsg(@3, "Invalid target for port map association.\n"); yyerrok; $$ = 0; } ; association_list : association_list ',' association_element { std::list*tmp = $1; tmp->push_back($3); $$ = tmp; } | association_element { std::list*tmp = new std::list; tmp->push_back($1); $$ = tmp; } ; binding_indication : K_use entity_aspect_opt port_map_aspect_opt generic_map_aspect_opt { $$ = $2; if ($3) sorrymsg(@3, "Port map aspect not supported here. (binding_indication)\n"); if ($4) sorrymsg(@4, "Generic map aspect not supported here. (binding_indication)\n"); delete $3; delete $4; } ; binding_indication_semicolon_opt : binding_indication ';' { $$ = $1; } | { $$ = 0; } ; block_configuration : K_for IDENTIFIER use_clauses_opt configuration_items_opt K_end K_for ';' { delete[] $2; } ; block_configuration_opt : block_configuration | ; block_declarative_item : K_signal identifier_list ':' subtype_indication signal_declaration_assign_opt ';' { /* Save the signal declaration in the block_signals map. */ for (std::list::iterator cur = $2->begin() ; cur != $2->end() ; ++cur) { Signal*sig = new Signal(*cur, $4, $5); FILE_NAME(sig, @1); active_scope->bind_name(*cur, sig); } delete $2; } | component_declaration | constant_declaration | subprogram_declaration | subprogram_body | type_declaration | use_clause_lib /* Various error handling rules for block_declarative_item... */ | K_signal error ';' { errormsg(@2, "Syntax error declaring signals.\n"); yyerrok; } | error ';' { errormsg(@1, "Syntax error in block declarations.\n"); yyerrok; } ; /* * The block_declarative_items rule matches "{ block_declarative_item }" * which is a synonym for "architecture_declarative_part" and * "block_declarative_part". */ block_declarative_items : block_declarative_items block_declarative_item | block_declarative_item ; block_declarative_items_opt : block_declarative_items | ; case_statement : K_case expression K_is case_statement_alternative_list K_end K_case ';' { CaseSeqStmt* tmp = new CaseSeqStmt($2, $4); FILE_NAME(tmp, @1); delete $4; $$ = tmp; } ; case_statement_alternative_list : case_statement_alternative_list case_statement_alternative { std::list* tmp = $1; tmp->push_back($2); $$ = tmp; } | case_statement_alternative { std::list*tmp = new std::list(); tmp->push_back($1); $$ = tmp; } ; /* * The case_statement_alternative uses the "choice" rule to select the * reference expression, but that rule is shared with the aggregate * expression. So convert the ExpAggregate::choice_t to a case * statement alternative and pass that up instead. */ case_statement_alternative : K_when choices ARROW sequence_of_statements { CaseSeqStmt::CaseStmtAlternative* tmp; std::list*choices = $2; std::list*exp_list = new std::list; bool others = false; for(std::list::iterator it = choices->begin(); it != choices->end(); ++it) { if((*it)->others() || others) // If there is one "others", then it also covers all other alternatives // Continue the loop to delete every choice_t, but do not // bother to add the expressions to the exp_list (we are going to // delete them very soon) others = true; else exp_list->push_back((*it)->simple_expression()); delete (*it); } if(others) { tmp = new CaseSeqStmt::CaseStmtAlternative(0, $4); for(std::list::iterator it = exp_list->begin(); it != exp_list->end(); ++it) { delete (*it); } } else { tmp = new CaseSeqStmt::CaseStmtAlternative(exp_list, $4); } if (tmp) FILE_NAME(tmp, @1); delete choices; delete $4; $$ = tmp; } ; choice : simple_expression { $$ = new ExpAggregate::choice_t($1);} | K_others { $$ = new ExpAggregate::choice_t; } | range /* discrete_range: range */ { $$ = new ExpAggregate::choice_t($1); } ; choices : choices '|' choice { std::list*tmp = $1; tmp->push_back($3); $$ = tmp; } | choice { std::list*tmp = new std::list; tmp->push_back($1); $$ = tmp; } ; component_configuration : K_for component_specification binding_indication_semicolon_opt block_configuration_opt K_end K_for ';' { sorrymsg(@1, "Component configuration in not yet supported\n"); if($3) delete $3; delete $2; } | K_for component_specification error K_end K_for { errormsg(@1, "Error in component configuration statement.\n"); delete $2; } ; component_declaration : K_component IDENTIFIER K_is_opt generic_clause_opt port_clause_opt K_end K_component identifier_opt ';' { perm_string name = lex_strings.make($2); if($8 && name != $8) { errormsg(@8, "Identifier %s doesn't match component name %s.\n", $8, name.str()); } ComponentBase*comp = new ComponentBase(name); comp->set_interface($4, $5); if ($4) delete $4; if ($5) delete $5; active_scope->bind_name(name, comp); delete[]$2; if ($8) delete[] $8; } | K_component IDENTIFIER K_is_opt error K_end K_component identifier_opt ';' { errormsg(@4, "Syntax error in component declaration.\n"); delete[] $2; if($7) delete[] $7; yyerrok; } ; instantiated_unit : IDENTIFIER | K_component IDENTIFIER { $$ = $2; } ; component_instantiation_statement : IDENTIFIER ':' instantiated_unit generic_map_aspect_opt port_map_aspect_opt ';' { perm_string iname = lex_strings.make($1); perm_string cname = lex_strings.make($3); ComponentInstantiation*tmp = new ComponentInstantiation(iname, cname, $4, $5); delete $4; delete $5; FILE_NAME(tmp, @1); delete[]$1; delete[]$3; $$ = tmp; } | IDENTIFIER ':' instantiated_unit error ';' { errormsg(@4, "Errors in component instantiation.\n"); delete[]$1; delete[]$3; $$ = 0; } ; component_specification : instantiation_list ':' name { ExpName* name = dynamic_cast($3); std::pair* tmp = new std::pair($1, name); $$ = tmp; } ; composite_type_definition /* constrained_array_definition */ : K_array index_constraint K_of subtype_indication { VTypeArray*tmp = new VTypeArray($4, $2); delete $2; $$ = tmp; } /* unbounded_array_definition IEEE 1076-2008 P5.3.2.1 */ | K_array '(' index_subtype_definition_list ')' K_of subtype_indication { std::list r; r.push_back(new prange_t(NULL, NULL, true)); // NULL boundaries indicate unbounded array type VTypeArray*tmp = new VTypeArray($6, &r); $$ = tmp; } | record_type_definition { $$ = $1; } ; /* The when...else..when...else syntax is not a general expression in VHDL but a specific sort of assignment statement model. We create Expression objects for it, but the parser will only recognize it it in specific situations. */ concurrent_conditional_signal_assignment /* IEEE 1076-2008 P11.6 */ : name LEQ waveform K_when expression else_when_waveforms ';' { ExpConditional*tmp = new ExpConditional($5, $3, $6); FILE_NAME(tmp, @3); delete $3; delete $6; ExpName*name = dynamic_cast ($1); assert(name); SignalAssignment*tmpa = new SignalAssignment(name, tmp); FILE_NAME(tmpa, @1); $$ = tmpa; } /* Error recovery rules. */ | name LEQ error K_when expression else_when_waveforms ';' { errormsg(@3, "Syntax error in waveform of conditional signal assignment.\n"); ExpConditional*tmp = new ExpConditional($5, 0, $6); FILE_NAME(tmp, @3); delete $6; ExpName*name = dynamic_cast ($1); assert(name); SignalAssignment*tmpa = new SignalAssignment(name, tmp); FILE_NAME(tmpa, @1); $$ = tmpa; } ; concurrent_simple_signal_assignment : name LEQ waveform ';' { ExpName*name = dynamic_cast ($1); assert(name); SignalAssignment*tmp = new SignalAssignment(name, *$3); FILE_NAME(tmp, @1); $$ = tmp; delete $3; } else_when_waveforms : else_when_waveforms else_when_waveform { list*tmp = $1; tmp ->push_back($2); $$ = tmp; } | else_when_waveform { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; else_when_waveform : K_else waveform K_when expression { ExpConditional::case_t*tmp = new ExpConditional::case_t($4, $2); FILE_NAME(tmp, @1); $$ = tmp; } | K_else waveform { ExpConditional::case_t*tmp = new ExpConditional::case_t(0, $2); FILE_NAME(tmp, @1); $$ = tmp; } ; concurrent_signal_assignment_statement /* IEEE 1076-2008 P11.6 */ : concurrent_simple_signal_assignment | IDENTIFIER ':' concurrent_simple_signal_assignment { delete[] $1; $$ = $3; } | concurrent_conditional_signal_assignment | IDENTIFIER ':' concurrent_conditional_signal_assignment { delete[] $1; $$ = $3; } | selected_signal_assignment | IDENTIFIER ':' selected_signal_assignment { $$ = $3; } | name LEQ error ';' { errormsg(@2, "Syntax error in signal assignment waveform.\n"); delete $1; $$ = 0; yyerrok; } | error LEQ waveform ';' { errormsg(@1, "Syntax error in l-value of signal assignment.\n"); yyerrok; delete $3; $$ = 0; } ; concurrent_statement : component_instantiation_statement | concurrent_signal_assignment_statement | generate_statement | process_statement ; configuration_declaration : K_configuration IDENTIFIER K_of IDENTIFIER K_is configuration_declarative_part block_configuration K_end K_configuration_opt identifier_opt ';' { if(design_entities.find(lex_strings.make($4)) == design_entities.end()) errormsg(@4, "Couldn't find entity %s used in configuration declaration\n", $4); //choose_architecture_for_entity(); sorrymsg(@1, "Configuration declaration is not yet supported.\n"); } | K_configuration error K_end K_configuration_opt identifier_opt ';' { errormsg(@2, "Too many errors, giving up on configuration declaration.\n"); if($5) delete $5; yyerrok; } ; //TODO: this list is only a sketch. It must be filled out later configuration_declarative_item : use_clause ; configuration_declarative_items : configuration_declarative_items configuration_declarative_item | configuration_declarative_item ; configuration_declarative_part : configuration_declarative_items | ; configuration_item : block_configuration | component_configuration ; configuration_items : configuration_items configuration_item | configuration_item ; configuration_items_opt : configuration_items | ; constant_declaration : K_constant identifier_list ':' subtype_indication VASSIGN expression ';' { // The syntax allows multiple names to have the same type/value. for (std::list::iterator cur = $2->begin() ; cur != $2->end() ; ++cur) { active_scope->bind_name(*cur, $4, $6); } delete $2; } | K_constant identifier_list ':' subtype_indication ';' { sorrymsg(@1, "Deferred constant declarations not supported\n"); delete $2; } /* Some error handling... */ | K_constant identifier_list ':' subtype_indication VASSIGN error ';' { // The syntax allows multiple names to have the same type/value. errormsg(@6, "Error in value expression for constants.\n"); yyerrok; for (std::list::iterator cur = $2->begin() ; cur != $2->end() ; ++cur) { active_scope->bind_name(*cur, $4, 0); } delete $2; } | K_constant identifier_list ':' error ';' { errormsg(@4, "Syntax error in constant declaration type.\n"); yyerrok; delete $2; } | K_constant error ';' { errormsg(@2, "Syntax error in constant declaration.\n"); yyerrok; } ; context_clause : context_items | ; context_item : library_clause | use_clause_lib ; context_items : context_items context_item | context_item ; design_unit : context_clause library_unit | error { errormsg(@1, "Invalid design_unit\n"); } ; design_units : design_units design_unit | design_unit ; /* Indicate the direction as a flag, with "downto" being TRUE. */ direction : K_to { $$ = false; } | K_downto { $$ = true; } ; element_association : choices ARROW expression { ExpAggregate::element_t*tmp = new ExpAggregate::element_t($1, $3); delete $1; $$ = tmp; } | expression { ExpAggregate::element_t*tmp = new ExpAggregate::element_t(0, $1); $$ = tmp; } ; element_association_list : element_association_list ',' element_association { std::list*tmp = $1; tmp->push_back($3); $$ = tmp; } | element_association { std::list*tmp = new std::list; tmp->push_back($1); $$ = tmp; } ; element_declaration : identifier_list ':' subtype_indication ';' { $$ = record_elements($1, $3); delete $1; } ; element_declaration_list : element_declaration_list element_declaration { $$ = $1; $$->splice($$->end(), *$2); delete $2; } | element_declaration { $$ = $1; } ; /* As an entity is declared, add it to the map of design entities. */ entity_aspect : K_entity name { ExpName* name = dynamic_cast($2); entity_aspect_t* tmp = new entity_aspect_t(entity_aspect_t::ENTITY, name); $$ = tmp; } | K_configuration name { ExpName* name = dynamic_cast($2); entity_aspect_t* tmp = new entity_aspect_t(entity_aspect_t::CONFIGURATION, name); $$ = tmp; } | K_open { entity_aspect_t* tmp = new entity_aspect_t(entity_aspect_t::OPEN, 0); $$ = tmp; } ; entity_aspect_opt : entity_aspect { $$ = $1; } | { $$ = 0; } ; entity_declaration : K_entity IDENTIFIER K_is generic_clause_opt port_clause_opt K_end K_entity_opt identifier_opt';' { Entity*tmp = new Entity(lex_strings.make($2)); FILE_NAME(tmp, @1); // Transfer the ports tmp->set_interface($4, $5); delete $4; delete $5; // Save the entity in the entity map. design_entities[tmp->get_name()] = tmp; delete[]$2; if($8 && tmp->get_name() != $8) { errormsg(@1, "Syntax error in entity clause. Closing name doesn't match.\n"); } delete[]$8; } | K_entity error K_end K_entity_opt identifier_opt ';' { errormsg(@1, "Too many errors, giving up on entity declaration.\n"); yyerrok; if ($5) delete[]$5; } ; enumeration_literal : IDENTIFIER { list*tmp = new list; tmp->push_back(lex_strings.make($1)); delete[]$1; $$ = tmp; } ; enumeration_literal_list : enumeration_literal_list ',' enumeration_literal { list*tmp = $1; list*tmp3 = $3; if (tmp3) { tmp->splice(tmp->end(), *tmp3); delete tmp3; } $$ = tmp; } | enumeration_literal { list*tmp = $1; $$ = tmp; } ; expression_list : expression_list ',' expression { list*tmp = $1; tmp->push_back($3); $$ = tmp; } | expression { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; expression : expression_logical { $$ = $1; } ; /* * The expression_logical matches the logical_expression from the * standard. Note that this is a little more tricky then usual because * of the strange VHDL rules for logical expressions. We have to * account for things like this: * * and and ... * * which is matched by the standard rule: * * logical_expression ::= * ... * relation { and relation } * * The {} is important, and implies that "and" can be strung together * with other "and" operators without parentheses. This is true for * "and", "or", "xor", and "xnor". The tricky part is that these * cannot be mixed. For example, this is not OK: * * and or * * Also note that "nand" and "nor" can not be chained in this manner. */ expression_logical : relation { $$ = $1; } | relation K_and expression_logical_and { ExpLogical*tmp = new ExpLogical(ExpLogical::AND, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | relation K_or expression_logical_or { ExpLogical*tmp = new ExpLogical(ExpLogical::OR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | relation K_xor expression_logical_xor { ExpLogical*tmp = new ExpLogical(ExpLogical::XOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | relation K_nand relation { ExpLogical*tmp = new ExpLogical(ExpLogical::NAND, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | relation K_nor relation { ExpLogical*tmp = new ExpLogical(ExpLogical::NOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | relation K_xnor expression_logical_xnor { ExpLogical*tmp = new ExpLogical(ExpLogical::XNOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; expression_logical_and : relation { $$ = $1; } | expression_logical_and K_and relation { ExpLogical*tmp = new ExpLogical(ExpLogical::AND, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; expression_logical_or : relation { $$ = $1; } | expression_logical_or K_or relation { ExpLogical*tmp = new ExpLogical(ExpLogical::OR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; expression_logical_xnor : relation { $$ = $1; } | expression_logical_xnor K_xnor relation { ExpLogical*tmp = new ExpLogical(ExpLogical::XNOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; expression_logical_xor : relation { $$ = $1; } | expression_logical_xor K_xor relation { ExpLogical*tmp = new ExpLogical(ExpLogical::XOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; factor : primary { $$ = $1; } | primary EXP primary { ExpArithmetic*tmp = new ExpArithmetic(ExpArithmetic::POW, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | K_abs primary { ExpUAbs*tmp = new ExpUAbs($2); FILE_NAME(tmp, @1); $$ = tmp; } | K_not primary { ExpUNot*tmp = new ExpUNot($2); FILE_NAME(tmp, @1); $$ = tmp; } ; for_generate_statement : IDENTIFIER ':' K_for IDENTIFIER K_in range K_generate generate_statement_body K_end K_generate identifier_opt ';' { perm_string name = lex_strings.make($1); perm_string gvar = lex_strings.make($4); ForGenerate*tmp = new ForGenerate(name, gvar, $6, *$8); FILE_NAME(tmp, @1); if ($11 && name != $11) { errormsg(@1, "for-generate name %s does not match closing name %s\n", name.str(), $11); } delete[]$1; delete[]$4; delete $8; delete[]$11; $$ = tmp; } ; function_specification /* IEEE 1076-2008 P4.2.1 */ : K_function IDENTIFIER parameter_list K_return IDENTIFIER { perm_string type_name = lex_strings.make($5); perm_string name = lex_strings.make($2); const VType*type_mark = active_scope->find_type(type_name); touchup_interface_for_functions($3); SubprogramHeader*tmp = new SubprogramHeader(name, $3, type_mark); FILE_NAME(tmp, @1); delete[]$2; delete[]$5; $$ = tmp; } ; generate_statement /* IEEE 1076-2008 P11.8 */ : if_generate_statement | for_generate_statement ; generate_statement_body : architecture_statement_part { $$ = $1; } ; generic_clause_opt : generic_clause { $$ = $1;} | { $$ = 0; } ; generic_clause : K_generic parameter_list ';' { $$ = $2; } | K_generic '(' error ')' ';' { errormsg(@3, "Error in interface list for generic.\n"); yyerrok; $$ = 0; } ; generic_map_aspect_opt : generic_map_aspect { $$ = $1; } | { $$ = 0; } ; generic_map_aspect : K_generic K_map '(' association_list ')' { $$ = $4; } | K_generic K_map '(' error ')' { errormsg(@4, "Error in association list for generic map.\n"); yyerrok; $$ = 0; } ; identifier_list : identifier_list ',' IDENTIFIER { std::list* tmp = $1; tmp->push_back(lex_strings.make($3)); delete[]$3; $$ = tmp; } | IDENTIFIER { std::list*tmp = new std::list; tmp->push_back(lex_strings.make($1)); delete[]$1; $$ = tmp; } ; identifier_opt : IDENTIFIER { $$ = $1; } | { $$ = 0; } ; identifier_colon_opt : IDENTIFIER ':' { $$ = $1; } | { $$ = 0; }; /* The if_generate_statement rule describes the if_generate syntax. NOTE: This does not yet implement the elsif and else parts of the syntax. This shouldn't be hard, but is simply not done yet. */ if_generate_statement /* IEEE 1076-2008 P11.8 */ : IDENTIFIER ':' K_if expression K_generate generate_statement_body K_end K_generate identifier_opt ';' { perm_string name = lex_strings.make($1); IfGenerate*tmp = new IfGenerate(name, $4, *$6); FILE_NAME(tmp, @3); if ($9 && name != $9) { errormsg(@1, "if-generate name %s does not match closing name %s\n", name.str(), $9); } delete[]$1; delete $6; delete[]$9; $$ = tmp; } ; if_statement : K_if expression K_then sequence_of_statements if_statement_elsif_list_opt if_statement_else K_end K_if ';' { IfSequential*tmp = new IfSequential($2, $4, $5, $6); FILE_NAME(tmp, @1); delete $4; delete $5; delete $6; $$ = tmp; } | K_if error K_then sequence_of_statements if_statement_elsif_list_opt if_statement_else K_end K_if ';' { errormsg(@2, "Error in if_statement condition expression.\n"); yyerrok; $$ = 0; delete $4; } | K_if expression K_then error K_end K_if ';' { errormsg(@4, "Too many errors in sequence within if_statement.\n"); yyerrok; $$ = 0; } | K_if error K_end K_if ';' { errormsg(@2, "Too many errors in if_statement.\n"); yyerrok; $$ = 0; } ; if_statement_elsif_list_opt : if_statement_elsif_list { $$ = $1; } | { $$ = 0; } ; if_statement_elsif_list : if_statement_elsif_list if_statement_elsif { list*tmp = $1; tmp->push_back($2); $$ = tmp; } | if_statement_elsif { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; if_statement_elsif : K_elsif expression K_then sequence_of_statements { IfSequential::Elsif*tmp = new IfSequential::Elsif($2, $4); FILE_NAME(tmp, @1); delete $4; $$ = tmp; } | K_elsif expression K_then error { errormsg(@4, "Too many errors in elsif sub-statements.\n"); yyerrok; $$ = 0; } ; if_statement_else : K_else sequence_of_statements { $$ = $2; } | K_else error { errormsg(@2, "Too many errors in else sub-statements.\n"); yyerrok; $$ = 0; } | { $$ = 0; } ; index_constraint : '(' range_list ')' { $$ = $2; } | '(' error ')' { errormsg(@2, "Errors in the index constraint.\n"); yyerrok; $$ = new list; } ; /* The identifier should be a type name */ index_subtype_definition /* IEEE 1076-2008 P5.3.2.1 */ : IDENTIFIER K_range BOX ; index_subtype_definition_list : index_subtype_definition_list ',' index_subtype_definition | index_subtype_definition ; instantiation_list : identifier_list { instant_list_t* tmp = new instant_list_t(instant_list_t::NONE, $1); $$ = tmp; } | K_others { instant_list_t* tmp = new instant_list_t(instant_list_t::OTHERS, 0); $$ = tmp; } | K_all { instant_list_t* tmp = new instant_list_t(instant_list_t::ALL, 0); $$ = tmp; } ; /* The interface_element is also an interface_declaration */ interface_element : identifier_list ':' mode_opt subtype_indication interface_element_expression { std::list*tmp = new std::list; for (std::list::iterator cur = $1->begin() ; cur != $1->end() ; ++cur) { InterfacePort*port = new InterfacePort; FILE_NAME(port, @1); port->mode = $3; port->name = *(cur); port->type = $4; port->expr = $5; tmp->push_back(port); } delete $1; $$ = tmp; } ; interface_element_expression : VASSIGN expression { $$ = $2; } | { $$ = 0; } ; interface_list : interface_list ';' interface_element { std::list*tmp = $1; tmp->splice(tmp->end(), *$3); delete $3; $$ = tmp; } | interface_element { std::list*tmp = $1; $$ = tmp; } ; library_clause : K_library logical_name_list ';' { library_import(@1, $2); delete $2; } | K_library error ';' { errormsg(@1, "Syntax error in library clause.\n"); yyerrok; } ; /* Collapse the primary_unit and secondary_unit of the library_unit into this single set of rules. */ library_unit : primary_unit | secondary_unit ; logical_name : IDENTIFIER { $$ = $1; } ; logical_name_list : logical_name_list ',' logical_name { std::list*tmp = $1; tmp->push_back(lex_strings.make($3)); delete[]$3; $$ = tmp; } | logical_name { std::list*tmp = new std::list; tmp->push_back(lex_strings.make($1)); delete[]$1; $$ = tmp; } ; loop_statement : identifier_colon_opt K_while expression_logical K_loop sequence_of_statements K_end K_loop identifier_opt ';' { perm_string loop_name = $1? lex_strings.make($1) : perm_string() ; if ($8 && !$1) { errormsg(@8, "Loop statement closing name %s for un-named statement\n", $8); } else if($8 && loop_name != $8) { errormsg(@1, "Loop statement name %s doesn't match closing name %s.\n", loop_name.str(), $8); } if($1) delete[]$1; if($8) delete[]$8; ExpLogical* cond = dynamic_cast($3); if(!cond) { errormsg(@3, "Iteration condition is not a correct logical expression.\n"); } WhileLoopStatement* tmp = new WhileLoopStatement(loop_name, cond, $5); FILE_NAME(tmp, @1); sorrymsg(@1, "Loop statements are not supported.\n"); $$ = tmp; } | identifier_colon_opt K_for IDENTIFIER K_in range K_loop sequence_of_statements K_end K_loop identifier_opt ';' { perm_string loop_name = $1? lex_strings.make($1) : perm_string() ; perm_string index_name = lex_strings.make($3); if ($10 && !$1) { errormsg(@10, "Loop statement closing name %s for un-named statement\n", $10); } else if($10 && loop_name != $10) { errormsg(@1, "Loop statement name %s doesn't match closing name %s.\n", loop_name.str(), $10); } if($1) delete[] $1; delete[] $3; if($10) delete[] $10; ForLoopStatement* tmp = new ForLoopStatement(loop_name, index_name, $5, $7); FILE_NAME(tmp, @1); $$ = tmp; } | identifier_colon_opt K_loop sequence_of_statements K_end K_loop identifier_opt ';' { perm_string loop_name = $1? lex_strings.make($1) : perm_string() ; if ($6 && !$1) { errormsg(@6, "Loop statement closing name %s for un-named statement\n", $6); } else if($6 && loop_name != $6) { errormsg(@1, "Loop statement name %s doesn't match closing name %s.\n", loop_name.str(), $6); } if($1) delete[]$1; if($6) delete[]$6; BasicLoopStatement* tmp = new BasicLoopStatement(loop_name, $3); FILE_NAME(tmp, @2); sorrymsg(@1, "Loop statements are not supported.\n"); $$ = tmp; }; mode : K_in { $$ = PORT_IN; } | K_out { $$ = PORT_OUT; } | K_inout { $$ = PORT_INOUT; } ; mode_opt : mode {$$ = $1;} | {$$ = PORT_NONE;} ; name /* IEEE 1076-2008 P8.1 */ : IDENTIFIER /* simple_name (IEEE 1076-2008 P8.2) */ { Expression*tmp = new ExpName(lex_strings.make($1)); FILE_NAME(tmp, @1); delete[]$1; $$ = tmp; } | selected_name { $$ = $1; } /* Note that this rule can match array element selects and various function calls. The only way we can tell the difference is from left context, namely whether the name is a type name or function name. If none of the above, treat it as a array element select. */ | IDENTIFIER '(' expression_list ')' { perm_string name = lex_strings.make($1); delete[]$1; if (active_scope->is_vector_name(name) || is_subprogram_param(name)) { ExpName*tmp = new ExpName(name, $3); $$ = tmp; } else { ExpFunc*tmp = new ExpFunc(name, $3); $$ = tmp; } FILE_NAME($$, @1); } | IDENTIFIER '(' range ')' { ExpName*tmp = new ExpName(lex_strings.make($1), $3->msb(), $3->lsb()); FILE_NAME(tmp, @1); delete[]$1; $$ = tmp; } | selected_name '(' range ')' { ExpName*tmp = dynamic_cast ($1); tmp->set_range($3->msb(), $3->lsb()); $$ = tmp; } | selected_name '(' expression ')' { ExpName*tmp = dynamic_cast ($1); tmp->set_range($3, NULL); $$ = tmp; } ; /* Handle name lists as lists of expressions. */ name_list : name_list ',' name { std::list*tmp = $1; tmp->push_back($3); $$ = tmp; } | name { std::list*tmp = new std::list; tmp->push_back($1); $$ = tmp; } ; package_declaration : package_declaration_start K_is package_declarative_part_opt K_end K_package_opt identifier_opt ';' { perm_string name = lex_strings.make($1); if($6 && name != $6) { errormsg(@1, "Identifier %s doesn't match package name %s.\n", $6, name.str()); } Package*tmp = new Package(name, *active_scope); FILE_NAME(tmp, @1); delete[]$1; if ($6) delete[]$6; pop_scope(); /* Put this package into the work library, or the currently parsed library. Note that parse_library_name is an argument to the parser. */ library_save_package(parse_library_name, tmp); } | package_declaration_start K_is error K_end K_package_opt identifier_opt ';' { errormsg(@3, "Syntax error in package clause.\n"); yyerrok; pop_scope(); } ; package_declaration_start : K_package IDENTIFIER { push_scope(); $$ = $2; } ; /* TODO: this list must be extended in the future presently it is only a sketch */ package_body_declarative_item /* IEEE1076-2008 P4.8 */ : use_clause | subprogram_body ; package_body_declarative_items : package_body_declarative_items package_body_declarative_item | package_body_declarative_item ; package_body_declarative_part_opt : package_body_declarative_items | ; package_declarative_item : component_declaration | constant_declaration | subprogram_declaration | subtype_declaration | type_declaration | use_clause | error ';' { errormsg(@1, "Syntax error in package declarative item.\n"); yyerrok; } ; package_declarative_items : package_declarative_items package_declarative_item | package_declarative_item ; package_declarative_part_opt : package_declarative_items | ; package_body /* IEEE 1076-2008 P4.8 */ : package_body_start K_is package_body_declarative_part_opt K_end K_package_opt identifier_opt ';' { perm_string name = lex_strings.make($1); if ($6 && name != $6) errormsg(@6, "Package name (%s) doesn't match closing name (%s).\n", $1, $6); delete[] $1; if($6) delete[]$6; pop_scope(); } | package_body_start K_is error K_end K_package_opt identifier_opt ';' { errormsg(@1, "Errors in package %s body.\n", $1); yyerrok; pop_scope(); } ; /* * This is a portion of the package_body rule that we factor out so * that we can use this rule to start the scope. */ package_body_start : K_package K_body IDENTIFIER { perm_string name = lex_strings.make($3); push_scope(); Package*pkg = library_recall_package(parse_library_name, name); if (pkg != 0) { active_scope->set_package_header(pkg); } else { errormsg(@1, "Package body for %s has no matching header.\n", $3); } $$ = $3; } ; parameter_list : '(' interface_list ')' { $$ = $2; } ; parameter_list_opt : parameter_list { $$ = $1; } | { $$ = 0; } ; port_clause : K_port parameter_list ';' { $$ = $2; } | K_port '(' error ')' ';' { errormsg(@1, "Syntax error in port list.\n"); yyerrok; $$ = 0; } ; port_clause_opt : port_clause {$$ = $1;} | {$$ = 0;} ; port_map_aspect : K_port K_map '(' association_list ')' { $$ = $4; } | K_port K_map '(' error ')' { errormsg(@1, "Syntax error in port map aspect.\n"); yyerrok; } ; port_map_aspect_opt : port_map_aspect { $$ = $1; } | { $$ = 0; } ; prefix /* IEEE 1076-2008 P8.1 */ : name { $$ = $1; } ; primary : name { $$ = $1; } | name '\'' IDENTIFIER { perm_string name = lex_strings.make($3); ExpName*base = dynamic_cast ($1); ExpAttribute*tmp = new ExpAttribute(base, name); FILE_NAME(tmp, @3); delete[]$3; $$ = tmp; } | CHARACTER_LITERAL { ExpCharacter*tmp = new ExpCharacter($1[0]); FILE_NAME(tmp,@1); delete[]$1; $$ = tmp; } | INT_LITERAL { ExpInteger*tmp = new ExpInteger($1); FILE_NAME(tmp, @1); $$ = tmp; } | REAL_LITERAL { ExpReal*tmp = new ExpReal($1); FILE_NAME(tmp, @1); $$ = tmp; } | STRING_LITERAL { ExpString*tmp = new ExpString($1); FILE_NAME(tmp,@1); delete[]$1; $$ = tmp; } | BITSTRING_LITERAL { ExpBitstring*tmp = new ExpBitstring($1); FILE_NAME(tmp, @1); delete[]$1; $$ = tmp; } | INT_LITERAL IDENTIFIER { ExpTime::timeunit_t unit = ExpTime::FS; if(!strcasecmp($2, "us")) unit = ExpTime::US; else if(!strcasecmp($2, "ms")) unit = ExpTime::MS; else if(!strcasecmp($2, "ns")) unit = ExpTime::NS; else if(!strcasecmp($2, "s")) unit = ExpTime::S; else if(!strcasecmp($2, "ps")) unit = ExpTime::PS; else if(!strcasecmp($2, "fs")) unit = ExpTime::FS; else errormsg(@2, "Invalid time unit (accepted are fs, ps, ns, us, ms, s).\n"); if($1 < 0) errormsg(@1, "Time cannot be negative.\n"); ExpTime*tmp = new ExpTime($1, unit); FILE_NAME(tmp, @1); delete[] $2; $$ = tmp; } /*XXXX Caught up in element_association_list? | '(' expression ')' { $$ = $2; } */ /* This catches function calls that use association lists for the argument list. The position argument list is discovered elsewhere and must be discovered by elaboration (thanks to the ambiguity of VHDL syntax). */ | IDENTIFIER '(' association_list ')' { sorrymsg(@1, "Function calls not supported\n"); delete[] $1; $$ = 0; } /* Aggregates */ | '(' element_association_list ')' { Expression*tmp = aggregate_or_primary(@1, $2); $$ = tmp; } ; primary_unit : entity_declaration | configuration_declaration | package_declaration ; procedure_call : IDENTIFIER ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1)); delete[] $1; $$ = tmp; } | IDENTIFIER '(' association_list ')' ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $3); delete[] $1; $$ = tmp; } | IDENTIFIER '(' expression_list ')' ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $3); delete[] $1; delete $3; // parameters are copied in this variant $$ = tmp; } | IDENTIFIER '(' error ')' ';' { errormsg(@1, "Errors in procedure call.\n"); yyerrok; delete[]$1; $$ = 0; } ; procedure_call_statement : IDENTIFIER ':' procedure_call { delete[] $1; $$ = $3; } | procedure_call { $$ = $1; } ; procedure_specification /* IEEE 1076-2008 P4.2.1 */ : K_procedure IDENTIFIER parameter_list_opt { perm_string name = lex_strings.make($2); touchup_interface_for_functions($3); SubprogramHeader*tmp = new SubprogramHeader(name, $3, NULL); FILE_NAME(tmp, @1); delete[]$2; $$ = tmp; } ; process_declarative_item : variable_declaration ; process_declarative_part : process_declarative_part process_declarative_item | process_declarative_item ; process_declarative_part_opt : process_declarative_part | ; process_statement : identifier_colon_opt K_postponed_opt K_process process_sensitivity_list_opt K_is_opt process_declarative_part_opt K_begin sequence_of_statements K_end K_postponed_opt K_process identifier_opt ';' { perm_string iname = $1? lex_strings.make($1) : perm_string(); if ($1) delete[]$1; if ($12) { if (iname.nil()) { errormsg(@12, "Process end name %s for un-named processes.\n", $12); } else if (iname != $12) { errormsg(@12, "Process name %s does not match opening name %s.\n", $12, $1); } delete[]$12; } ProcessStatement*tmp = new ProcessStatement(iname, $4, $8); FILE_NAME(tmp, @3); delete $4; delete $8; $$ = tmp; } | identifier_colon_opt K_postponed_opt K_process process_sensitivity_list_opt K_is_opt process_declarative_part_opt K_begin error K_end K_postponed_opt K_process identifier_opt ';' { errormsg(@7, "Too many errors in process sequential statements.\n"); yyerrok; $$ = 0; } ; /* * A process_sensitivity_list is: * if the list is not present, or * or a non-empty list of actual expressions. */ process_sensitivity_list_opt : '(' process_sensitivity_list ')' { $$ = $2; } | '(' error ')' { errormsg(@2, "Error in process sensitivity list\n"); yyerrok; $$ = 0; } | { $$ = 0; } ; process_sensitivity_list : K_all { std::list*tmp = new std::list; ExpName*all = new ExpNameALL; FILE_NAME(all, @1); tmp->push_back(all); $$ = tmp; } | name_list { $$ = $1; } ; range : simple_expression direction simple_expression { prange_t* tmp = new prange_t($1, $3, $2); $$ = tmp; } | name '\'' K_range { prange_t*tmp = NULL; ExpName*name = NULL; if((name = dynamic_cast($1))) { ExpAttribute*left = new ExpAttribute(name, left_attr); ExpAttribute*right = new ExpAttribute(name, right_attr); tmp = new prange_t(left, right, true); tmp->set_auto_dir(); } else { errormsg(@1, "'range attribute can be used with named expressions only"); } $$ = tmp; } | name '\'' K_reverse_range { prange_t*tmp = NULL; ExpName*name = NULL; if((name = dynamic_cast($1))) { ExpAttribute*left = new ExpAttribute(name, left_attr); ExpAttribute*right = new ExpAttribute(name, right_attr); tmp = new prange_t(left, right, false); tmp->set_auto_dir(); } else { errormsg(@1, "'reverse_range attribute can be used with named expressions only"); } $$ = tmp; } ; range_list : range { list*tmp = new list; tmp->push_back($1); $$ = tmp; } | range_list ',' range { list*tmp = $1; tmp->push_back($3); $$ = tmp; } ; record_type_definition : K_record element_declaration_list K_end K_record { VTypeRecord*tmp = new VTypeRecord($2); $$ = tmp; } ; relation : shift_expression { $$ = $1; } | shift_expression '=' shift_expression { ExpRelation*tmp = new ExpRelation(ExpRelation::EQ, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | shift_expression '<' shift_expression { ExpRelation*tmp = new ExpRelation(ExpRelation::LT, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | shift_expression '>' shift_expression { ExpRelation*tmp = new ExpRelation(ExpRelation::GT, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | shift_expression LEQ shift_expression { ExpRelation*tmp = new ExpRelation(ExpRelation::LE, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | shift_expression GEQ shift_expression { ExpRelation*tmp = new ExpRelation(ExpRelation::GE, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | shift_expression NE shift_expression { ExpRelation*tmp = new ExpRelation(ExpRelation::NEQ, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; report_statement : K_report STRING_LITERAL severity_opt ';' { ReportStmt*tmp = new ReportStmt($2, $3); FILE_NAME(tmp,@2); delete[]$2; $$ = tmp; } return_statement : K_return expression ';' { ReturnStmt*tmp = new ReturnStmt($2); FILE_NAME(tmp, @1); $$ = tmp; } | K_return ';' { ReturnStmt*tmp = new ReturnStmt(0); FILE_NAME(tmp, @1); $$ = tmp; } | K_return error ';' { ReturnStmt*tmp = new ReturnStmt(0); FILE_NAME(tmp, @1); $$ = tmp; errormsg(@2, "Error in expression in return statement.\n"); yyerrok; } ; secondary_unit : architecture_body | package_body ; selected_name /* IEEE 1076-2008 P8.3 */ : prefix '.' suffix { Expression*pfx = $1; ExpName*pfx1 = dynamic_cast(pfx); assert(pfx1); perm_string tmp = lex_strings.make($3); $$ = new ExpName(pfx1, tmp); FILE_NAME($$, @3); delete[]$3; } | error '.' suffix { errormsg(@1, "Syntax error in prefix in front of \"%s\".\n", $3); yyerrok; $$ = new ExpName(lex_strings.make($3)); FILE_NAME($$, @3); delete[]$3; } ; selected_names : selected_names ',' selected_name { std::list* tmp = $1; tmp->push_back($3); $$ = tmp; } | selected_name { std::list* tmp = new std::list(); tmp->push_back($1); $$ = tmp; } ; /* The *_lib variant of selected_name is used by the "use" clause. It is syntactically identical to other selected_name rules, but is a convenient place to attach use_clause actions. */ selected_name_lib : IDENTIFIER '.' K_all { library_use(@1, active_scope, 0, $1, 0); delete[]$1; } | IDENTIFIER '.' IDENTIFIER '.' K_all { library_use(@1, active_scope, $1, $3, 0); delete[]$1; delete[]$3; } | IDENTIFIER '.' IDENTIFIER '.' IDENTIFIER { library_use(@1, active_scope, $1, $3, $5); delete[]$1; delete[]$3; delete[]$5; } ; selected_names_lib : selected_names_lib ',' selected_name_lib | selected_name_lib ; selected_signal_assignment : K_with expression K_select name LEQ selected_waveform_list ';' { ExpSelected*tmp = new ExpSelected($2, $6); FILE_NAME(tmp, @3); delete $2; delete $6; ExpName*name = dynamic_cast($4); assert(name); SignalAssignment*tmpa = new SignalAssignment(name, tmp); FILE_NAME(tmpa, @1); $$ = tmpa; } ; selected_waveform : waveform K_when expression { ExpConditional::case_t*tmp = new ExpConditional::case_t($3, $1); FILE_NAME(tmp, @1); $$ = tmp; } | waveform K_when K_others { ExpConditional::case_t*tmp = new ExpConditional::case_t(0, $1); FILE_NAME(tmp, @1); $$ = tmp; } ; selected_waveform_list : selected_waveform_list ',' selected_waveform { list*tmp = $1; tmp->push_back($3); $$ = tmp; } | selected_waveform { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; list_of_statements : list_of_statements sequential_statement { std::list*tmp = $1; if($2) tmp->push_back($2); $$ = tmp; } | sequential_statement { std::list*tmp = new std::list; if($1) tmp->push_back($1); $$ = tmp; } ; sequence_of_statements : list_of_statements { $1 = $$; } | { $$ = NULL; } ; sequential_statement : if_statement { $$ = $1; } | signal_assignment_statement { $$ = $1; } | variable_assignment_statement { $$ = $1; } | case_statement { $$ = $1; } | procedure_call_statement { $$ = $1; } | loop_statement { $$ = $1; } | return_statement { $$ = $1; } | report_statement { $$ = $1; } | assertion_statement { $$ = $1; } | wait_statement { $$ = $1; } | K_null ';' { $$ = 0; } | error ';' { errormsg(@1, "Syntax error in sequential statement.\n"); $$ = 0; yyerrok; } ; severity : K_severity IDENTIFIER { if(!strcasecmp($2, "NOTE")) $$ = ReportStmt::NOTE; else if(!strcasecmp($2, "WARNING")) $$ = ReportStmt::WARNING; else if(!strcasecmp($2, "ERROR")) $$ = ReportStmt::ERROR; else if(!strcasecmp($2, "FAILURE")) $$ = ReportStmt::FAILURE; else { errormsg(@1, "Invalid severity level (possible values: NOTE, WARNING, ERROR, FAILURE).\n"); $$ = ReportStmt::UNSPECIFIED; } delete[] $2; } severity_opt : severity { $$ = $1; } | { $$ = ReportStmt::UNSPECIFIED; } shift_expression : simple_expression | simple_expression K_srl simple_expression { ExpShift*tmp = new ExpShift(ExpShift::SRL, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | simple_expression K_sll simple_expression { ExpShift*tmp = new ExpShift(ExpShift::SLL, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | simple_expression K_sra simple_expression { ExpShift*tmp = new ExpShift(ExpShift::SRA, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | simple_expression K_sla simple_expression { ExpShift*tmp = new ExpShift(ExpShift::SLA, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | simple_expression K_ror simple_expression { sorrymsg(@2, "ROR is not supported.\n"); ExpShift*tmp = new ExpShift(ExpShift::ROR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | simple_expression K_rol simple_expression { sorrymsg(@2, "ROL is not supported.\n"); ExpShift*tmp = new ExpShift(ExpShift::ROL, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; sign : '+' | '-' ; signal_declaration_assign_opt : VASSIGN expression { $$ = $2; } | { $$ = 0; } ; /* * The LRM rule for simple_expression is: * simple_expression ::= [sign] term { adding_operator term } * * This is functionally a list of terms, with the adding_operator used * as a list element separator instead of a ','. The LRM rule, * however, is right-recursive, which is not too nice in real LALR * parsers. The solution is to rewrite it as below, to make it * left-recursive. This is much more efficient use of the parse stack. * * Note that although the concatenation operator '&' is syntactically * an addition operator, it is handled differently during elaboration * so detect it and create a different expression type. * * Note too that I'm using *right* recursion to implement the {...} * part of the rule. This is normally bad, but expression lists aren't * normally that long, and the while loop going through the formed * list fixes up the associations. */ simple_expression : sign simple_expression_2 { sorrymsg(@1, "Unary expression +- not supported.\n"); $$ = $2; } | simple_expression_2 { $$ = $1; } ; simple_expression_2 : term { $$ = $1; } | term simple_expression_terms { Expression*tmp = $1; list*lst = $2; while (! lst->empty()) { struct adding_term item = lst->front(); lst->pop_front(); if (item.op == ExpArithmetic::xCONCAT) tmp = new ExpConcat(tmp, item.term); else tmp = new ExpArithmetic(item.op, tmp, item.term); } delete lst; $$ = tmp; } ; simple_expression_terms : adding_operator term { struct adding_term item; item.op = $1; item.term = $2; list*tmp = new list; tmp->push_back(item); $$ = tmp; } | simple_expression_terms adding_operator term { list*tmp = $1; struct adding_term item; item.op = $2; item.term = $3; tmp->push_back(item); $$ = tmp; } ; signal_assignment : name LEQ waveform ';' { SignalSeqAssignment*tmp = new SignalSeqAssignment($1, $3); FILE_NAME(tmp, @1); delete $3; $$ = tmp; } | name LEQ waveform K_when expression K_else waveform ';' { SignalSeqAssignment*tmp = new SignalSeqAssignment($1, $3); FILE_NAME(tmp, @1); sorrymsg(@4, "Conditional signal assignment not supported.\n"); $$ = tmp; } ; signal_assignment_statement : signal_assignment | IDENTIFIER ':' signal_assignment { delete[] $1; $$ = $3; } subprogram_body_start : subprogram_specification K_is { assert(!active_sub); active_sub = $1; $$ = $1; } ; /* This is a function/task body. This may have a matching subprogram declaration, and if so it will be in the active scope. */ subprogram_body /* IEEE 1076-2008 P4.3 */ : subprogram_body_start subprogram_declarative_part K_begin subprogram_statement_part K_end subprogram_kind_opt identifier_opt ';' { SubprogramHeader*prog = $1; SubprogramHeader*tmp = active_scope->recall_subprogram(prog->name()); if (tmp && prog->compare_specification(tmp)) { delete prog; prog = tmp; } else if (tmp) { errormsg(@1, "Subprogram specification for %s doesn't match specification in package header.\n", prog->name().str()); } SubprogramBody*body = new SubprogramBody(); body->transfer_from(*active_scope, ScopeBase::VARIABLES); body->set_statements($4); prog->set_body(body); active_scope->bind_name(prog->name(), prog); active_sub = NULL; } | subprogram_body_start subprogram_declarative_part K_begin error K_end subprogram_kind_opt identifier_opt ';' { errormsg(@2, "Syntax errors in subprogram body.\n"); yyerrok; active_sub = NULL; if ($1) delete $1; if ($7) delete[]$7; } ; subprogram_declaration : subprogram_specification ';' { if ($1) active_scope->bind_name($1->name(), $1); } ; subprogram_declarative_item /* IEEE 1079-2008 P4.3 */ : variable_declaration ; subprogram_declarative_item_list : subprogram_declarative_item_list subprogram_declarative_item | subprogram_declarative_item ; subprogram_declarative_part /* IEEE 1076-2008 P4.3 */ : subprogram_declarative_item_list | ; subprogram_kind /* IEEE 1076-2008 P4.3 */ : K_function | K_procedure ; subprogram_kind_opt : subprogram_kind | ; subprogram_specification : function_specification { $$ = $1; } | procedure_specification { $$ = $1; } ; /* This is an implementation of the rule: subprogram_statement_part ::= { sequential_statement } where the sequence_of_statements rule is a list of sequential_statement. */ subprogram_statement_part : sequence_of_statements { $$ = $1; } ; subtype_declaration : K_subtype IDENTIFIER K_is subtype_indication ';' { perm_string name = lex_strings.make($2); delete[] $2; if ($4 == 0) { errormsg(@1, "Failed to declare type name %s.\n", name.str()); } else { active_scope->bind_name(name, $4); } } ; subtype_indication : IDENTIFIER { const VType*tmp = parse_type_by_name(lex_strings.make($1)); if (tmp == 0) { errormsg(@1, "Can't find type name `%s'\n", $1); tmp = new VTypeERROR; } delete[]$1; $$ = tmp; } | IDENTIFIER index_constraint { const VType*tmp = calculate_subtype_array(@1, $1, active_scope, $2); if (tmp == 0) { errormsg(@1, "Unable to calculate bounds for array of %s.\n", $1); } delete[]$1; delete $2; $$ = tmp; } | IDENTIFIER K_range simple_expression direction simple_expression { const VType*tmp = calculate_subtype_range(@1, $1, active_scope, $3, $4, $5); if (tmp == 0) { errormsg(@1, "Unable to calculate bounds for range of %s.\n", $1); } delete[]$1; $$ = tmp; } ; suffix : IDENTIFIER { $$ = $1; } | CHARACTER_LITERAL { $$ = $1; } | K_all { //do not have now better idea than using char constant $$ = strdup("all"); } ; term : factor { $$ = $1; } | factor '*' factor { ExpArithmetic*tmp = new ExpArithmetic(ExpArithmetic::MULT, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | factor '/' factor { ExpArithmetic*tmp = new ExpArithmetic(ExpArithmetic::DIV, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | factor K_mod factor { ExpArithmetic*tmp = new ExpArithmetic(ExpArithmetic::MOD, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } | factor K_rem factor { ExpArithmetic*tmp = new ExpArithmetic(ExpArithmetic::REM, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; type_declaration : K_type IDENTIFIER K_is type_definition ';' { perm_string name = lex_strings.make($2); if ($4 == 0) { errormsg(@1, "Failed to declare type name %s.\n", name.str()); } else { VTypeDef*tmp; map::iterator cur = active_scope->incomplete_types.find(name); if (cur == active_scope->incomplete_types.end()) { tmp = new VTypeDef(name, $4); active_scope->bind_name(name, tmp); } else { tmp = cur->second; tmp->set_definition($4); active_scope->incomplete_types.erase(cur); } if(const VTypeEnum*enum_type = dynamic_cast($4)) { active_scope->use_enum(enum_type); } } delete[]$2; } | K_type IDENTIFIER ';' { perm_string name = lex_strings.make($2); VTypeDef*tmp = new VTypeDef(name); active_scope->incomplete_types[name] = tmp; active_scope->bind_name(name, tmp); delete[]$2; } | K_type IDENTIFIER K_is error ';' { errormsg(@4, "Error in type definition for %s\n", $2); yyerrok; delete[]$2; } | K_type error ';' { errormsg(@1, "Error in type definition\n"); yyerrok; } ; type_definition : '(' enumeration_literal_list ')' { VTypeEnum*tmp = new VTypeEnum($2); delete $2; $$ = tmp; } | composite_type_definition { $$ = $1; } ; use_clause : K_use selected_names ';' { $$ = $2; } | K_use error ';' { errormsg(@1, "Syntax error in use clause.\n"); yyerrok; } ; use_clause_lib : K_use selected_names_lib ';' | K_use error ';' { errormsg(@1, "Syntax error in use clause.\n"); yyerrok; } ; use_clauses_lib : use_clauses_lib use_clause_lib | use_clause_lib ; use_clauses_opt : use_clauses_lib | ; variable_assignment_statement /* IEEE 1076-2008 P10.6.1 */ : variable_assignment | IDENTIFIER ':' variable_assignment { delete[] $1; $$ = $3; } variable_assignment : name VASSIGN expression ';' { VariableSeqAssignment*tmp = new VariableSeqAssignment($1, $3); FILE_NAME(tmp, @1); $$ = tmp; } | name VASSIGN error ';' { errormsg(@3, "Syntax error in r-value expression of assignment.\n"); yyerrok; delete $1; $$ = 0; } | error VASSIGN expression ';' { errormsg(@1, "Syntax error in l-value expression of assignment.\n"); yyerrok; delete $3; $$ = 0; } ; variable_declaration /* IEEE 1076-2008 P6.4.2.4 */ : K_shared_opt K_variable identifier_list ':' subtype_indication variable_declaration_assign_opt ';' { /* Save the signal declaration in the block_signals map. */ for (std::list::iterator cur = $3->begin() ; cur != $3->end() ; ++cur) { Variable*sig = new Variable(*cur, $5, $6); FILE_NAME(sig, @2); active_scope->bind_name(*cur, sig); } delete $3; } | K_shared_opt K_variable error ';' { errormsg(@2, "Syntax error in variable declaration.\n"); yyerrok; } ; variable_declaration_assign_opt : VASSIGN expression { $$ = $2; } | { $$ = 0; } ; wait_statement : K_wait K_for expression ';' { WaitForStmt*tmp = new WaitForStmt($3); FILE_NAME(tmp, @1); $$ = tmp; } | K_wait K_on expression ';' { WaitStmt*tmp = new WaitStmt(WaitStmt::ON, $3); FILE_NAME(tmp, @1); $$ = tmp; } | K_wait K_until expression ';' { WaitStmt*tmp = new WaitStmt(WaitStmt::UNTIL, $3); FILE_NAME(tmp, @1); $$ = tmp; } ; waveform : waveform_elements { $$ = $1; } | K_unaffected { $$ = 0; } ; waveform_elements : waveform_elements ',' waveform_element { std::list*tmp = $1; tmp->push_back($3); $$ = tmp; } | waveform_element { std::list*tmp = new std::list; tmp->push_back($1); $$ = tmp; } ; waveform_element : expression { $$ = $1; } | K_null { $$ = 0; } ; /* Some keywords are optional in some contexts. In all such cases, a similar rule is used, as described here. */ K_architecture_opt : K_architecture | ; K_configuration_opt: K_configuration| ; K_entity_opt : K_entity | ; K_is_opt : K_is | ; K_package_opt : K_package | ; K_postponed_opt : K_postponed | ; K_shared_opt : K_shared | ; %% static void yyerror(YYLTYPE*loc, yyscan_t, const char*, bool, const char*msg) { fprintf(stderr, "%s:%u: %s\n", loc->text, loc->first_line, msg); parse_errors += 1; } void errormsg(const YYLTYPE&loc, const char*fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%u: error: ", loc.text, loc.first_line); vfprintf(stderr, fmt, ap); va_end(ap); parse_errors += 1; } void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%u: sorry: ", loc.text, loc.first_line); vfprintf(stderr, fmt, ap); va_end(ap); parse_sorrys += 1; } /* * The reset_lexor function takes the fd and makes it the input file * for the lexor. The path argument is used in lexor/parser error messages. */ extern yyscan_t prepare_lexor(FILE*fd); extern void destroy_lexor(yyscan_t scanner); int parse_source_file(const char*file_path, perm_string parse_library_name) { FILE*fd = fopen(file_path, "r"); if (fd == 0) { perror(file_path); return -1; } yyscan_t scanner = prepare_lexor(fd); int rc = yyparse(scanner, file_path, parse_library_name); fclose(fd); destroy_lexor(scanner); return rc; } iverilog-10_1/vhdlpp/parse_api.h000066400000000000000000000047761265551621300167710ustar00rootroot00000000000000#ifndef IVL_parse_api_H #define IVL_parse_api_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "entity.h" typedef void*yyscan_t; /* * The yyltype supports the passing of detailed source file location * information between the lexical analyzer and the parser. Defining * YYLTYPE compels the lexor to use this type and not something other. */ struct yyltype { unsigned first_line; const char*text; yyltype() { first_line = 1; text = ""; } }; # define YYLTYPE struct yyltype /* * This calls the bison-generated parser with the given file path as * the input stream. If the file cannot be opened, this returns -1. * The "library_name" argument is the name of the library that is * being parsed. If this is a regular source file, then this name is * nil. Note that the "work" library is handled specially. */ extern int parse_source_file(const char*file_path, perm_string library_name); /* * Use this function during parse to generate error messages. The "loc" * is the location of the token that triggered the error, and the fmt * is printf-style format. */ extern void errormsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); extern void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); /* * Set this to a non-zero value to enable parser debug output. */ extern int yydebug; /* * The parser counts the errors that is handed in the parse_errors * variable. For a clean compile, this value should not change. (The * caller sets its initial value.) The sorrys are the count of * unsupported constructs that are encountered. */ extern int parse_errors; extern int parse_sorrys; #endif /* IVL_parse_api_H */ iverilog-10_1/vhdlpp/parse_misc.cc000066400000000000000000000121141265551621300172720ustar00rootroot00000000000000/* * Copyright (c) 2011,2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Picture Elements, Inc., 777 Panoramic Way, Berkeley, CA 94704. */ # include "parse_misc.h" # include "parse_types.h" # include "parse_api.h" # include "entity.h" # include "architec.h" # include "expression.h" # include "vtype.h" # include "compiler.h" # include # include using namespace std; void bind_entity_to_active_scope(const char*ename, ActiveScope*scope) { perm_string ekey = lex_strings.make(ename); std::map::const_iterator idx = design_entities.find(ekey); if (idx == design_entities.end()) { return; } scope->bind(idx->second); } void bind_architecture_to_entity(const char*ename, Architecture*arch) { perm_string ekey = lex_strings.make(ename); std::map::const_iterator idx = design_entities.find(ekey); if (idx == design_entities.end()) { cerr << arch->get_fileline() << ": error: No entity " << ekey << " for architecture " << arch->get_name() << "." << endl; parse_errors += 1; return; } /* FIXME: entities can have multiple architectures attached to them This is to be configured by VHDL's configurations (not yet implemented) */ Architecture*old_arch = idx->second->add_architecture(arch); if (old_arch != arch) { cerr << arch->get_fileline() << ": warning: " << "Architecture " << arch->get_name() << " for entity " << idx->first << " is already defined here: " << old_arch->get_fileline() << endl; parse_errors += 1; } } static const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, ScopeBase* /* scope */, Expression*array_left, bool downto, Expression*array_right) { const VType*base_type = parse_type_by_name(lex_strings.make(base_name)); if (base_type == 0) { errormsg(loc, "Unable to find base type %s of array.\n", base_name); return 0; } assert(array_left==0 || array_right!=0); // unfold typedef, there might be VTypeArray inside const VType*origin_type = base_type; const VTypeDef*type_def = dynamic_cast (base_type); if (type_def) { base_type = type_def->peek_definition(); } const VTypeArray*base_array = dynamic_cast (base_type); if (base_array) { assert(array_left && array_right); vector range (base_array->dimensions()); // For now, I only know how to handle 1 dimension assert(base_array->dimensions() == 1); range[0] = VTypeArray::range_t(array_left, array_right, downto); // use typedef as the element type if possible const VType*element = type_def ? origin_type : base_array->element_type(); VTypeArray*subtype = new VTypeArray(element, range, base_array->signed_vector()); subtype->set_parent_type(base_array); return subtype; } return base_type; } const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, list*ranges) { if (ranges->size() == 1) { prange_t*tmpr = ranges->front(); Expression*lef = tmpr->expr_left(); Expression*rig = tmpr->expr_right(); return calculate_subtype_array(loc, base_name, scope, lef, tmpr->is_downto(), rig); } sorrymsg(loc, "Don't know how to handle multiple ranges here.\n"); return 0; } const VType* calculate_subtype_range(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, Expression*range_left, bool /* downto*/ , Expression*range_right) { const VType*base_type = parse_type_by_name(lex_strings.make(base_name)); if (base_type == 0) { errormsg(loc, "Unable to find base type %s of range.\n", base_name); return 0; } assert(range_left && range_right); int64_t left_val, right_val; bool rc = range_left->evaluate(scope, left_val); if (rc == false) return 0; rc = range_right->evaluate(scope, right_val); if (rc == false) return 0; VTypeRange*sub_type = new VTypeRange(base_type, left_val, right_val); return sub_type; } iverilog-10_1/vhdlpp/parse_misc.h000066400000000000000000000047011265551621300171370ustar00rootroot00000000000000#ifndef IVL_parse_misc_H #define IVL_parse_misc_H /* * Copyright (c) 2011,2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "parse_api.h" class ActiveScope; class Architecture; class Expression; class Package; class prange_t; class ScopeBase; class VType; extern void bind_entity_to_active_scope(const char*ename, ActiveScope*scope); extern void bind_architecture_to_entity(const char*ename, Architecture*arch); extern const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, std::list*ranges); extern const VType* calculate_subtype_range(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, Expression*range_left, bool downto, Expression*range_right); /* * This function searches the currently active scope, or the global * scope, for the named type. */ extern const VType* parse_type_by_name(perm_string name); /* * The parser calls the library_save_package function when it parses a * package. The library_parse_name is the name of the library that is * currently being processed (by a recursive call to the parser to * load a package from a library) or a nil name to indicate that this * is from the live parser. */ extern void library_save_package(perm_string library_parse_name, Package*pack); extern Package*library_recall_package(perm_string library_parse_name, perm_string name); extern void library_import(const YYLTYPE&loc, const std::list*names); extern void library_use(const YYLTYPE&loc, ActiveScope*res, const char*libname, const char*pack, const char*ident); #endif /* IVL_parse_misc_H */ iverilog-10_1/vhdlpp/parse_types.h000066400000000000000000000066601265551621300173560ustar00rootroot00000000000000#ifndef IVL_parse_types_H #define IVL_parse_types_H /* * Copyright (c) 2011,2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include "expression.h" class named_expr_t { public: named_expr_t (perm_string n, Expression*e) : name_(n), expr_(e) { } perm_string name() const { return name_; } Expression* expr() const { return expr_; } void dump(ostream&out, int indent) const; private: perm_string name_; Expression* expr_; private: // Not implemented named_expr_t(const named_expr_t&); named_expr_t& operator = (const named_expr_t&); }; class entity_aspect_t { public: typedef enum { ENTITY = 0, CONFIGURATION, OPEN } entity_aspect_type_t; entity_aspect_t(entity_aspect_type_t t, ExpName* n) : type_(t), name_(n) {} ~entity_aspect_t() { delete name_; } ExpName* name() const { return name_; } entity_aspect_type_t type() const {return type_; } entity_aspect_type_t type_; ExpName* name_; }; class instant_list_t { public: typedef enum { ALL = 0, OTHERS, NONE } application_domain_t; instant_list_t(application_domain_t d, std::list* l) : domain_(d), labels_(l) {} ~instant_list_t() { delete labels_; } std::list* labels() const { return labels_; } application_domain_t domain() const { return domain_; } application_domain_t domain_; std::list* labels_; }; class prange_t { public: prange_t(Expression* left, Expression* right, bool dir) : left_(left), right_(right), direction_(dir), auto_dir_(false) {} prange_t(const prange_t&other) : left_(other.left_->clone()), right_(other.right_->clone()), direction_(other.direction_), auto_dir_(other.auto_dir_) {} ~prange_t() { delete left_; delete right_; } void dump(ostream&out, int indent) const; inline Expression*msb() { return direction_? left_ : right_; } inline Expression*lsb() { return direction_? right_: left_; } inline bool is_downto() const { return direction_; } inline void set_auto_dir(bool enabled = true) { auto_dir_ = enabled; }; inline bool is_auto_dir() const { return auto_dir_; } inline Expression*expr_left() { return left_; } inline Expression*expr_right() { return right_; } private: Expression *left_, *right_; bool direction_; bool auto_dir_; private: //not implemented prange_t operator=(const prange_t&); }; struct adding_term { ExpArithmetic::fun_t op; Expression*term; }; #endif /* IVL_parse_types_H */ iverilog-10_1/vhdlpp/parse_wrap.h000066400000000000000000000025031265551621300171530ustar00rootroot00000000000000#ifndef IVL_parse_wrap_H #define IVL_parse_wrap_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This header wraps the parse.h header file that is generated from * the parse.y source file. This is used to include definitions that * are needed by the parse type, etc. */ # include # include "vhdlint.h" # include "vhdlreal.h" # include "architec.h" # include "expression.h" # include "sequential.h" # include "subprogram.h" # include "parse_types.h" class VType; # include "parse.h" #endif /* IVL_parse_wrap_H */ iverilog-10_1/vhdlpp/scope.cc000066400000000000000000000233421265551621300162630ustar00rootroot00000000000000/* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "scope.h" # include "package.h" # include "subprogram.h" # include "entity.h" # include "std_funcs.h" # include "std_types.h" # include # include # include # include using namespace std; /* * If the merge_flag is passed in, then the new scope is a merge of * the parent scopes. This brings in all of the parent scopes into the * "old_*_" variables. This clears up the "new_*_" variables to * accumulate new scope values. */ ScopeBase::ScopeBase(const ActiveScope&ref) : use_constants_(ref.use_constants_), cur_constants_(ref.cur_constants_) { merge(ref.old_signals_.begin(), ref.old_signals_.end(), ref.new_signals_.begin(), ref.new_signals_.end(), insert_iterator >( old_signals_, old_signals_.end()) ); merge(ref.old_variables_.begin(), ref.old_variables_.end(), ref.new_variables_.begin(), ref.new_variables_.end(), insert_iterator >( old_variables_, old_variables_.end()) ); merge(ref.old_components_.begin(), ref.old_components_.end(), ref.new_components_.begin(), ref.new_components_.end(), insert_iterator >( old_components_, old_components_.end()) ); use_types_ = ref.use_types_; cur_types_ = ref.cur_types_; use_subprograms_ = ref.use_subprograms_; cur_subprograms_ = ref.cur_subprograms_; use_enums_ = ref.use_enums_; // This constructor is invoked when the parser is finished with // an active scope and is making the actual scope. At this point // we know that "this" is the parent scope for the subprograms, // so set it now. for (map::iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end(); ++cur) { cur->second->set_parent(this); } } ScopeBase::~ScopeBase() { //freeing of member objects is performed by child classes } void ScopeBase::cleanup() { /* * A parent scope is destroyed only if all child scopes * were previously destroyed. There for we can delete all * objects that were defined in this scope, leaving * objects from the other scopes untouched. */ delete_all(new_signals_); delete_all(new_components_); delete_all(cur_types_); delete_all(cur_constants_); delete_all(cur_subprograms_); } const VType*ScopeBase::find_type(perm_string by_name) { map::const_iterator cur = cur_types_.find(by_name); if (cur == cur_types_.end()) { cur = use_types_.find(by_name); if (cur == use_types_.end()) return 0; else return cur->second; } else return cur->second; } bool ScopeBase::find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const { typ = NULL; exp = NULL; map::const_iterator cur = cur_constants_.find(by_name); if (cur == cur_constants_.end()) { cur = use_constants_.find(by_name); if (cur == use_constants_.end()) return false; else { typ = cur->second->typ; exp = cur->second->val; return true; } } else { typ = cur->second->typ; exp = cur->second->val; return true; } return false; } Signal* ScopeBase::find_signal(perm_string by_name) const { map::const_iterator cur = new_signals_.find(by_name); if (cur == new_signals_.end()) { cur = old_signals_.find(by_name); if (cur == old_signals_.end()) return 0; else return cur->second; } else { return cur->second; } } Variable* ScopeBase::find_variable(perm_string by_name) const { map::const_iterator cur = new_variables_.find(by_name); if (cur == new_variables_.end()) { cur = old_variables_.find(by_name); if (cur == old_variables_.end()) return 0; else return cur->second; } else { return cur->second; } } const InterfacePort* ScopeBase::find_param(perm_string) const { return NULL; } const InterfacePort* ScopeBase::find_param_all(perm_string by_name) const { for(map::const_iterator it = use_subprograms_.begin(); it != use_subprograms_.end(); ++it) { if(const InterfacePort*port = it->second->find_param(by_name)) return port; } for(map::const_iterator it = cur_subprograms_.begin(); it != cur_subprograms_.end(); ++it) { if(const InterfacePort*port = it->second->find_param(by_name)) return port; } return NULL; } SubprogramHeader* ScopeBase::find_subprogram(perm_string name) const { map::const_iterator cur; cur = cur_subprograms_.find(name); if (cur != cur_subprograms_.end()) return cur->second; cur = use_subprograms_.find(name); if (cur != use_subprograms_.end()) return cur->second; return find_std_subprogram(name); } const VTypeEnum* ScopeBase::is_enum_name(perm_string name) const { for(list::const_iterator it = use_enums_.begin(); it != use_enums_.end(); ++it) { if((*it)->has_name(name)) return *it; } return find_std_enum_name(name); } /* * This method is only used by the ActiveScope derived class to import * definition from another scope. */ void ScopeBase::do_use_from(const ScopeBase*that) { for (map::const_iterator cur = that->old_components_.begin() ; cur != that->old_components_.end() ; ++ cur) { if (cur->second == 0) continue; old_components_[cur->first] = cur->second; } for (map::const_iterator cur = that->new_components_.begin() ; cur != that->new_components_.end() ; ++ cur) { if (cur->second == 0) continue; old_components_[cur->first] = cur->second; } for (map::const_iterator cur = that->cur_subprograms_.begin() ; cur != that->cur_subprograms_.end() ; ++ cur) { if (cur->second == 0) continue; use_subprograms_[cur->first] = cur->second; } for (map::const_iterator cur = that->cur_types_.begin() ; cur != that->cur_types_.end() ; ++ cur) { if (cur->second == 0) continue; use_types_[cur->first] = cur->second; } for (map::const_iterator cur = that->cur_constants_.begin() ; cur != that->cur_constants_.end() ; ++ cur) { use_constants_[cur->first] = cur->second; } use_enums_ = that->use_enums_; } void ScopeBase::transfer_from(ScopeBase&ref, transfer_type_t what) { if(what & SIGNALS) { std::copy(ref.new_signals_.begin(), ref.new_signals_.end(), insert_iterator >( new_signals_, new_signals_.end()) ); ref.new_signals_.clear(); } if(what & VARIABLES) { std::copy(ref.new_variables_.begin(), ref.new_variables_.end(), insert_iterator >( new_variables_, new_variables_.end()) ); ref.new_variables_.clear(); } if(what & COMPONENTS) { std::copy(ref.new_components_.begin(), ref.new_components_.end(), insert_iterator >( new_components_, new_components_.end()) ); ref.new_components_.clear(); } } void ActiveScope::set_package_header(Package*pkg) { assert(package_header_ == 0); package_header_ = pkg; } SubprogramHeader* ActiveScope::recall_subprogram(perm_string name) const { if (SubprogramHeader*tmp = find_subprogram(name)) return tmp; if (package_header_) return package_header_->find_subprogram(name); return 0; } bool ActiveScope::is_vector_name(perm_string name) const { if (find_signal(name)) return true; if (find_variable(name)) return true; const VType*dummy_type; Expression*dummy_exp; if (find_constant(name, dummy_type, dummy_exp)) return true; if (context_entity_ && context_entity_->find_port(name)) return true; return false; } Scope::Scope(const ActiveScope&ref) : ScopeBase(ref) { } Scope::~Scope() { } ComponentBase* Scope::find_component(perm_string by_name) { map::const_iterator cur = new_components_.find(by_name); if (cur == new_components_.end()) { cur = old_components_.find(by_name); if (cur == old_components_.end()) return 0; else return cur->second; } else return cur->second; } iverilog-10_1/vhdlpp/scope.h000066400000000000000000000214251265551621300161250ustar00rootroot00000000000000#ifndef IVL_scope_H #define IVL_scope_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include "StringHeap.h" # include "entity.h" # include "expression.h" # include "vsignal.h" class ActiveScope; class Architecture; class ComponentBase; class Package; class SubprogramHeader; class VType; template struct delete_object{ void operator()(T* item) { delete item; } }; template struct delete_pair_second{ void operator()(pair item){ delete item.second; } }; class ScopeBase { public: ScopeBase() { } explicit ScopeBase(const ActiveScope&ref); virtual ~ScopeBase() =0; const VType* find_type(perm_string by_name); virtual bool find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const; Signal* find_signal(perm_string by_name) const; Variable* find_variable(perm_string by_name) const; virtual const InterfacePort* find_param(perm_string by_name) const; const InterfacePort* find_param_all(perm_string by_name) const; SubprogramHeader* find_subprogram(perm_string by_name) const; // Checks if a string is one of possible enum values. If so, the enum // type is returned, otherwise NULL. const VTypeEnum* is_enum_name(perm_string name) const; // Moves signals, variables and components from another scope to // this one. After the transfer new_* maps are cleared in the source scope. enum transfer_type_t { SIGNALS = 1, VARIABLES = 2, COMPONENTS = 4, ALL = 0xffff }; void transfer_from(ScopeBase&ref, transfer_type_t what = ALL); inline void bind_subprogram(perm_string name, SubprogramHeader*obj) { map::iterator it; if((it = use_subprograms_.find(name)) != use_subprograms_.end() ) use_subprograms_.erase(it); cur_subprograms_[name] = obj; } protected: void cleanup(); //containers' cleaning helper functions template void delete_all(list& c) { for_each(c.begin(), c.end(), ::delete_object()); } template void delete_all(map& c) { for_each(c.begin(), c.end(), ::delete_pair_second()); } // The new_*_ maps below are managed only by the ActiveScope // derived class. When any scope is constructed from the // ActiveScope, the new_*_ and old_*_ maps are merged and // installed into the old_*_ maps. Thus, all other derived // classes should only use the old_*_ maps. // Signal declarations... std::map old_signals_; //previous scopes std::map new_signals_; //current scope // Variable declarations... std::map old_variables_; //previous scopes std::map new_variables_; //current scope // Component declarations... std::map old_components_; //previous scopes std::map new_components_; //current scope // Type declarations... std::map use_types_; //imported types std::map cur_types_; //current types // Constant declarations... struct const_t { ~const_t() {delete val;} const_t(const VType*t, Expression* v) : typ(t), val(v) {}; const VType*typ; Expression*val; }; std::map use_constants_; //imported constants std::map cur_constants_; //current constants std::map use_subprograms_; //imported std::map cur_subprograms_; //current std::list use_enums_; void do_use_from(const ScopeBase*that); }; class Scope : public ScopeBase { public: explicit Scope(const ActiveScope&ref); ~Scope(); ComponentBase* find_component(perm_string by_name); public: void dump_scope(ostream&out) const; protected: // Helper method for emitting signals in the scope. int emit_signals(ostream&out, Entity*ent, Architecture*arc); int emit_variables(ostream&out, Entity*ent, Architecture*arc); }; /* * The active_scope object accumulates declarations for the scope that * is in the process of being parsed. When the declarations are over, * they are transferred over to the specific scope. The ActiveScope is * used by the parser to build up scopes. */ class ActiveScope : public ScopeBase { public: ActiveScope() : package_header_(0), context_entity_(0) { } ActiveScope(ActiveScope*par) : ScopeBase(*par), package_header_(0), context_entity_(0) { } ~ActiveScope() { } void set_package_header(Package*); // Pull items from "that" scope into "this" scope as is // defined by a "use" directive. The parser uses this method // to implement the "use ::*" directive. void use_from(const Scope*that) { do_use_from(that); } // This function returns true if the name is a vectorable // name. The parser uses this to distinguish between function // calls and array index operations. bool is_vector_name(perm_string name) const; // Locate the subprogram by name. The subprogram body uses // this to locate the subprogram declaration. Note that the // subprogram may be in a package header. SubprogramHeader* recall_subprogram(perm_string name) const; /* All bind_name function check if the given name was present * in previous scopes. If it is found, it is erased (but the pointer * is not freed), in order to implement name shadowing. The pointer * be freed only in the scope where the object was defined. This is * done in ScopeBase::cleanup() function .*/ void bind_name(perm_string name, Signal*obj) { map::iterator it; if((it = old_signals_.find(name)) != old_signals_.end() ) old_signals_.erase(it); new_signals_[name] = obj; } void bind_name(perm_string name, Variable*obj) { map::iterator it; if((it = old_variables_.find(name)) != old_variables_.end() ) old_variables_.erase(it); new_variables_[name] = obj; } void bind_name(perm_string name, ComponentBase*obj) { map::iterator it; if((it = old_components_.find(name)) != old_components_.end() ) old_components_.erase(it); new_components_[name] = obj; } void bind_name(perm_string name, const VType* t) { map::iterator it; if((it = use_types_.find(name)) != use_types_.end() ) use_types_.erase(it); cur_types_[name] = t; } inline void use_enum(const VTypeEnum* t) { use_enums_.push_back(t); } inline void use_name(perm_string name, const VType* t) { use_types_[name] = t; } void bind_name(perm_string name, const VType*obj, Expression*val) { map::iterator it; if((it = use_constants_.find(name)) != use_constants_.end() ) use_constants_.erase(it); cur_constants_[name] = new const_t(obj, val); } inline void bind_name(perm_string name, SubprogramHeader*obj) { map::iterator it; if((it = use_subprograms_.find(name)) != use_subprograms_.end() ) use_subprograms_.erase(it); cur_subprograms_[name] = obj; } void bind(Entity*ent) { context_entity_ = ent; } void destroy_global_scope() { cleanup(); } // Keep track of incomplete types until their proper // definition shows up. std::map incomplete_types; private: // If this is a package body, then there is a Package header // already declared. Package*package_header_; Entity*context_entity_; }; #endif /* IVL_scope_H */ iverilog-10_1/vhdlpp/sequential.cc000066400000000000000000000156211265551621300173250ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sequential.h" # include "expression.h" # include template inline static void visit_stmt_list(std::list& stmts, SeqStmtVisitor& func) { for(typename std::list::iterator it = stmts.begin(); it != stmts.end(); ++it) { (*it)->visit(func); } } SequentialStmt::SequentialStmt() { } SequentialStmt::~SequentialStmt() { } IfSequential::IfSequential(Expression*cond, std::list*tr, std::list*el, std::list*fa) { cond_ = cond; if (tr) if_.splice(if_.end(), *tr); if (el) elsif_.splice(elsif_.end(), *el); if (fa) else_.splice(else_.end(), *fa); } IfSequential::~IfSequential() { delete cond_; while (if_.size() > 0) { SequentialStmt*cur = if_.front(); if_.pop_front(); delete cur; } while (elsif_.size() > 0) { IfSequential::Elsif*cur = elsif_.front(); elsif_.pop_front(); delete cur; } while (else_.size() > 0) { SequentialStmt*cur = else_.front(); else_.pop_front(); delete cur; } } void IfSequential::extract_true(std::list&that) { while (! if_.empty()) { that.push_back(if_.front()); if_.pop_front(); } } void IfSequential::extract_false(std::list&that) { while (! else_.empty()) { that.push_back(else_.front()); else_.pop_front(); } } void IfSequential::visit(SeqStmtVisitor& func) { visit_stmt_list(if_, func); visit_stmt_list(elsif_, func); visit_stmt_list(else_, func); func(this); } IfSequential::Elsif::Elsif(Expression*cond, std::list*tr) : cond_(cond) { if (tr) if_.splice(if_.end(), *tr); } IfSequential::Elsif::~Elsif() { delete cond_; while (if_.size() > 0) { SequentialStmt*cur = if_.front(); if_.pop_front(); delete cur; } } void IfSequential::Elsif::visit(SeqStmtVisitor& func) { visit_stmt_list(if_, func); } SignalSeqAssignment::SignalSeqAssignment(Expression*sig, std::list*wav) { lval_ = sig; if (wav) waveform_.splice(waveform_.end(), *wav); } SignalSeqAssignment::~SignalSeqAssignment() { delete lval_; } CaseSeqStmt::CaseSeqStmt(Expression*cond, list* ap) : cond_(cond) { if (ap) alt_.splice(alt_.end(), *ap); } CaseSeqStmt::~CaseSeqStmt() { delete cond_; while(alt_.size() > 0) { CaseSeqStmt::CaseStmtAlternative* cur = alt_.front(); alt_.pop_front(); delete cur; } } void CaseSeqStmt::visit(SeqStmtVisitor& func) { visit_stmt_list(alt_, func); func(this); } CaseSeqStmt::CaseStmtAlternative::CaseStmtAlternative(std::list*exp, list*stmts) : exp_(exp) { if (stmts) stmts_.splice(stmts_.end(), *stmts); } CaseSeqStmt::CaseStmtAlternative::~CaseStmtAlternative() { delete exp_; while(stmts_.size() > 0) { SequentialStmt* cur = stmts_.front(); stmts_.pop_front(); delete cur; } } void CaseSeqStmt::CaseStmtAlternative::visit(SeqStmtVisitor& func) { visit_stmt_list(stmts_, func); } ProcedureCall::ProcedureCall(perm_string name) : name_(name), param_list_(NULL), def_(NULL) { } ProcedureCall::ProcedureCall(perm_string name, std::list* param_list) : name_(name), param_list_(param_list), def_(NULL) { } ProcedureCall::ProcedureCall(perm_string name, std::list* param_list) : name_(name), def_(NULL) { param_list_ = new std::list; for(std::list::const_iterator it = param_list->begin(); it != param_list->end(); ++it) { param_list_->push_back(new named_expr_t(empty_perm_string, (*it)->clone())); } } ProcedureCall::~ProcedureCall() { if(!param_list_) return; while(param_list_->size() > 0) { named_expr_t* cur = param_list_->front(); param_list_->pop_front(); delete cur; } } ReturnStmt::ReturnStmt(Expression*val) : val_(val) { } ReturnStmt::~ReturnStmt() { delete val_; } void ReturnStmt::cast_to(const VType*type) { assert(val_); val_ = new ExpCast(val_, type); } LoopStatement::LoopStatement(perm_string name, list* stmts) : name_(name) { if (stmts) stmts_.splice(stmts_.end(), *stmts); } LoopStatement::~LoopStatement() { while(stmts_.size() > 0) { SequentialStmt* cur = stmts_.front(); stmts_.pop_front(); delete cur; } } void LoopStatement::visit(SeqStmtVisitor& func) { visit_stmt_list(stmts_, func); func(this); } ForLoopStatement::ForLoopStatement(perm_string scope_name, perm_string it, prange_t* range, list* stmts) : LoopStatement(scope_name, stmts), it_(it), range_(range) { } ForLoopStatement::~ForLoopStatement() { delete range_; } VariableSeqAssignment::VariableSeqAssignment(Expression*lval, Expression*rval) : lval_(lval), rval_(rval) { } VariableSeqAssignment::~VariableSeqAssignment() { delete lval_; delete rval_; } WhileLoopStatement::WhileLoopStatement(perm_string lname, ExpLogical* cond, list* stmts) : LoopStatement(lname, stmts), cond_(cond) { } WhileLoopStatement::~WhileLoopStatement() { delete cond_; } BasicLoopStatement::BasicLoopStatement(perm_string lname, list* stmts) : LoopStatement(lname, stmts) { } BasicLoopStatement::~BasicLoopStatement() { } ReportStmt::ReportStmt(const char*msg, severity_t sev) : msg_(msg), severity_(sev) { if(sev == ReportStmt::UNSPECIFIED) severity_ = ReportStmt::NOTE; } AssertStmt::AssertStmt(Expression*condition, const char*msg, ReportStmt::severity_t sev) : ReportStmt("", sev), cond_(condition) { if(msg == NULL) msg_ = default_msg_; else msg_ = std::string(msg); if(sev == ReportStmt::UNSPECIFIED) severity_ = ReportStmt::ERROR; } const std::string AssertStmt::default_msg_ = std::string("Assertion violation."); WaitForStmt::WaitForStmt(Expression*delay) : delay_(delay) { } WaitStmt::WaitStmt(wait_type_t type, Expression*expr) : type_(type), expr_(expr) { } iverilog-10_1/vhdlpp/sequential.h000066400000000000000000000244261265551621300171720ustar00rootroot00000000000000#ifndef IVL_sequential_H #define IVL_sequential_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "LineInfo.h" # include "parse_types.h" # include class ScopeBase; class Entity; class Expression; class SequentialStmt; struct SeqStmtVisitor { virtual ~SeqStmtVisitor() {}; virtual void operator() (SequentialStmt*s) = 0; }; class SequentialStmt : public LineInfo { public: SequentialStmt(); virtual ~SequentialStmt() =0; public: virtual int elaborate(Entity*ent, ScopeBase*scope); virtual int emit(ostream&out, Entity*entity, ScopeBase*scope); virtual void dump(ostream&out, int indent) const; virtual void write_to_stream(std::ostream&fd); // Recursively visits a tree of sequential statements. virtual void visit(SeqStmtVisitor& func) { func(this); } }; /* * The LoopStatement is an abstract base class for the various loop * statements. */ class LoopStatement : public SequentialStmt { public: LoopStatement(perm_string block_name, list*); virtual ~LoopStatement(); inline perm_string loop_name() const { return name_; } void dump(ostream&out, int indent) const; void visit(SeqStmtVisitor& func); protected: int elaborate_substatements(Entity*ent, ScopeBase*scope); int emit_substatements(std::ostream&out, Entity*ent, ScopeBase*scope); void write_to_stream_substatements(ostream&fd); private: perm_string name_; std::list stmts_; }; class IfSequential : public SequentialStmt { public: class Elsif : public LineInfo { public: Elsif(Expression*cond, std::list*tr); ~Elsif(); int elaborate(Entity*entity, ScopeBase*scope); int condition_emit(ostream&out, Entity*entity, ScopeBase*scope); int statement_emit(ostream&out, Entity*entity, ScopeBase*scope); void condition_write_to_stream(ostream&fd); void statement_write_to_stream(ostream&fd); void dump(ostream&out, int indent) const; void visit(SeqStmtVisitor& func); private: Expression*cond_; std::listif_; private: // not implemented Elsif(const Elsif&); Elsif& operator =(const Elsif&); }; public: IfSequential(Expression*cond, std::list*tr, std::list*elsif, std::list*fa); ~IfSequential(); public: int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; void visit(SeqStmtVisitor& func); const Expression*peek_condition() const { return cond_; } size_t false_size() const { return else_.size(); } // These method extract (and remove) the sub-statements from // the true or false clause. void extract_true(std::list&that); void extract_false(std::list&that); private: Expression*cond_; std::list if_; std::list elsif_; std::list else_; }; class ReturnStmt : public SequentialStmt { public: ReturnStmt(Expression*val); ~ReturnStmt(); public: int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; const Expression*peek_expr() const { return val_; }; void cast_to(const VType*type); private: Expression*val_; }; class SignalSeqAssignment : public SequentialStmt { public: SignalSeqAssignment(Expression*sig, std::list*wav); ~SignalSeqAssignment(); public: int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: Expression*lval_; std::list waveform_; }; class CaseSeqStmt : public SequentialStmt { public: class CaseStmtAlternative : public LineInfo { public: CaseStmtAlternative(std::list*exp, std::list*stmts); ~CaseStmtAlternative(); void dump(std::ostream& out, int indent) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void visit(SeqStmtVisitor& func); private: std::list*exp_; std::list stmts_; private: // not implemented CaseStmtAlternative(const CaseStmtAlternative&); CaseStmtAlternative& operator =(const CaseStmtAlternative&); }; public: CaseSeqStmt(Expression*cond, std::list*sp); ~CaseSeqStmt(); public: void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void visit(SeqStmtVisitor& func); private: Expression* cond_; std::list alt_; }; class ProcedureCall : public SequentialStmt { public: ProcedureCall(perm_string name); ProcedureCall(perm_string name, std::list* param_list); ProcedureCall(perm_string name, std::list* param_list); ~ProcedureCall(); int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void dump(ostream&out, int indent) const; private: perm_string name_; std::list* param_list_; SubprogramHeader*def_; }; class VariableSeqAssignment : public SequentialStmt { public: VariableSeqAssignment(Expression*sig, Expression*rval); ~VariableSeqAssignment(); public: int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: Expression*lval_; Expression*rval_; }; class WhileLoopStatement : public LoopStatement { public: WhileLoopStatement(perm_string loop_name, ExpLogical*, list*); ~WhileLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent) const; private: ExpLogical* cond_; }; class ForLoopStatement : public LoopStatement { public: ForLoopStatement(perm_string loop_name, perm_string index, prange_t*, list*); ~ForLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*ent, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: // Emits for-loop which direction is determined at run-time. // It is used for 'range & 'reverse_range attributes. int emit_runtime_(ostream&out, Entity*ent, ScopeBase*scope); perm_string it_; prange_t* range_; }; class BasicLoopStatement : public LoopStatement { public: BasicLoopStatement(perm_string lname, list*); ~BasicLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); void dump(ostream&out, int indent) const; }; class ReportStmt : public SequentialStmt { public: typedef enum { UNSPECIFIED, NOTE, WARNING, ERROR, FAILURE } severity_t; ReportStmt(const char*message, severity_t severity = NOTE); virtual ~ReportStmt() {} void dump(ostream&out, int indent) const; int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); inline const std::string& message() const { return msg_; } inline severity_t severity() const { return severity_; } protected: std::string msg_; severity_t severity_; }; class AssertStmt : public ReportStmt { public: AssertStmt(Expression*condition, const char*message, ReportStmt::severity_t severity = ReportStmt::ERROR); void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); private: Expression*cond_; // Message displayed when there is no report assigned. static const std::string default_msg_; }; class WaitForStmt : public SequentialStmt { public: WaitForStmt(Expression*delay); void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); private: Expression*delay_; }; class WaitStmt : public SequentialStmt { public: typedef enum { ON, UNTIL } wait_type_t; WaitStmt(wait_type_t type, Expression*expression); void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); private: wait_type_t type_; Expression*expr_; // Sensitivity list for 'wait until' statement std::set sens_list_; }; #endif /* IVL_sequential_H */ iverilog-10_1/vhdlpp/sequential_debug.cc000066400000000000000000000150221265551621300204660ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sequential.h" # include "expression.h" # include # include # include using namespace std; void SequentialStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "SequentialStmt[" << typeid(*this).name() << "]" << " at file=" << get_fileline() << endl; } void IfSequential::dump(ostream&out, int indent) const { out << setw(indent) << "" << "IfSequential at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "Condition:" << endl; cond_->dump(out, indent+4); out << setw(indent+3) << "" << "TRUE clause (" << if_.size() << "):" << endl; for (list::const_iterator cur = if_.begin() ; cur != if_.end() ; ++cur) (*cur)->dump(out, indent+4); for (list::const_iterator cur = elsif_.begin() ; cur != elsif_.end() ; ++cur) (*cur)->dump(out, indent); out << setw(indent+3) << "" << "FALSE clause (" << else_.size() << "):" << endl; for (list::const_iterator cur = else_.begin() ; cur != else_.end() ; ++cur) (*cur)->dump(out, indent+4); } void IfSequential::Elsif::dump(ostream&out, int indent) const { out << setw(indent+3) << "" << "Elsif Condition at " << get_fileline() << ":" << endl; cond_->dump(out, indent+4); out << setw(indent+3) << "" << "ELSIF TRUE clause (" << if_.size() << "):" << endl; for (list::const_iterator cur = if_.begin() ; cur != if_.end() ; ++cur) (*cur)->dump(out, indent+4); } void ReturnStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ReturnStmt at file=" << get_fileline() << endl; if (val_) val_->dump(out, indent+4); else out << setw(indent+4) << "" << "()" << endl; } void SignalSeqAssignment::dump(ostream&out, int indent) const { out << setw(indent) << "" << "SignalSeqAssignment at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "l-value:" << endl; lval_->dump(out, indent+4); out << setw(indent+3) << "" << "r-values (" << waveform_.size() << "):" << endl; for (list::const_iterator cur = waveform_.begin() ; cur != waveform_.end() ; ++cur) (*cur)->dump(out, indent+4); } void CaseSeqStmt::dump(ostream& out, int indent) const { out << setw(indent) << "" << "CaseSeqStmt at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "Case: " << endl; cond_->dump(out, indent+4); for (list::const_iterator cur = alt_.begin() ; cur != alt_.end() ; ++cur) (*cur)->dump(out, indent+4); } void CaseSeqStmt::CaseStmtAlternative::dump(ostream& out, int indent) const { out << setw(indent) << "" << "CaseStmtAlternative at file=" << get_fileline() << endl; out << setw(indent) << "" << "when "; if (exp_) for (list::iterator it = exp_->begin(); it != exp_->end(); ++it) { (*it)->dump(out, 0); } else out << "others" << endl; for (list::const_iterator cur = stmts_.begin() ; cur != stmts_.end(); ++cur) (*cur)->dump(out, indent+1); } void ProcedureCall::dump(ostream& out, int indent) const { out << setw(indent) << "" << "ProcedureCall at file=" << get_fileline() << endl; out << setw(indent+2) << "" << name_ << "("; for(list::const_iterator it = param_list_->begin(); it != param_list_->end(); ++it) (*it)->dump(out, indent); out << ")" << endl; } void LoopStatement::dump(ostream&out, int indent) const { for(list::const_iterator it = stmts_.begin(); it != stmts_.end(); ++it) (*it)->dump(out, indent); } void ForLoopStatement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ForLoopStatement at file=" << get_fileline() << endl; out << setw(indent) << "" << it_ << " in "; range_->dump(out, indent); LoopStatement::dump(out, indent+2); } void VariableSeqAssignment::dump(ostream&out, int indent) const { out << setw(indent) << "" << "VariableSeqAssignment at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "l-value:" << endl; lval_->dump(out, indent+4); out << setw(indent+3) << "" << "r-value:" << endl; rval_->dump(out, indent+4); } void WhileLoopStatement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "WhileLoopStatement at file=" << get_fileline() << endl; out << setw(indent) << "" << "condition: "; cond_->dump(out, indent); LoopStatement::dump(out, indent+2); } void BasicLoopStatement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "BasicLoopStatement at file=" << get_fileline() << endl; LoopStatement::dump(out, indent+2); } void ReportStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ReportStmt at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "severity: " << severity_ << endl; out << setw(indent+3) << "" << "message: " << msg_ << endl; } void AssertStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "AssertStmt at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "condition: "; cond_->dump(out, indent+3); ReportStmt::dump(out, indent+3); } void WaitForStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "WaitForStmt at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "delay: "; delay_->dump(out, indent+3); } void WaitStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "WaitStmt at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "expression: "; expr_->dump(out, indent+3); } iverilog-10_1/vhdlpp/sequential_elaborate.cc000066400000000000000000000153541265551621300213460ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sequential.h" # include "expression.h" # include "scope.h" # include "library.h" # include "subprogram.h" int SequentialStmt::elaborate(Entity*, ScopeBase*) { return 0; } int LoopStatement::elaborate_substatements(Entity*ent, ScopeBase*scope) { int errors = 0; for (list::iterator cur = stmts_.begin() ; cur != stmts_.end() ; ++cur) { errors += (*cur)->elaborate(ent, scope); } return errors; } int CaseSeqStmt::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; const VType*ctype = cond_->probe_type(ent, scope); errors += cond_->elaborate_expr(ent, scope, ctype); for (list::iterator cur = alt_.begin() ; cur != alt_.end() ; ++cur) { CaseStmtAlternative*curp = *cur; errors += curp->elaborate_expr(ent, scope, ctype); errors += curp->elaborate(ent, scope); } return errors; } /* * This method elaborates the case expression for the alternative. The * ltype is the probed type for the main case condition. The * expression needs to elaborate itself in that context. */ int CaseSeqStmt::CaseStmtAlternative::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; if (exp_) { for (list::iterator it = exp_->begin(); it != exp_->end(); ++it) { errors += (*it)->elaborate_expr(ent, scope, ltype); } } return errors; } int CaseSeqStmt::CaseStmtAlternative::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; for (list::iterator cur = stmts_.begin() ; cur != stmts_.end() ; ++cur) { SequentialStmt*curp = *cur; errors += curp->elaborate(ent, scope); } return errors; } int ForLoopStatement::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; errors += elaborate_substatements(ent, scope); return errors; } int IfSequential::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; errors += cond_->elaborate_expr(ent, scope, 0); for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) { errors += (*cur)->elaborate(ent, scope); } for (list::iterator cur = elsif_.begin() ; cur != elsif_.end() ; ++cur) { errors += (*cur)->elaborate(ent, scope); } for (list::iterator cur = else_.begin() ; cur != else_.end() ; ++cur) { errors += (*cur)->elaborate(ent, scope); } return errors; } int IfSequential::Elsif::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; errors += cond_->elaborate_expr(ent, scope, 0); for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) { errors += (*cur)->elaborate(ent, scope); } return errors; } int SignalSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; // Elaborate the l-value expression. errors += lval_->elaborate_lval(ent, scope, true); // The elaborate_lval should have resolved the type of the // l-value expression. We'll use that type to elaborate the // r-value. const VType*lval_type = lval_->peek_type(); if (lval_type == 0) { if (errors == 0) errors += 1; return errors; } // Elaborate the r-value expressions. for (list::iterator cur = waveform_.begin() ; cur != waveform_.end() ; ++cur) { errors += (*cur)->elaborate_expr(ent, scope, lval_type); } return errors; } int ProcedureCall::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; def_ = scope->find_subprogram(name_); if(!def_) def_ = library_find_subprogram(name_); assert(def_); // Elaborate arguments size_t idx = 0; if(param_list_) { for(list::iterator cur = param_list_->begin() ; cur != param_list_->end() ; ++cur) { const VType*tmp = (*cur)->expr()->probe_type(ent, scope); const VType*param_type = def_ ? def_->peek_param_type(idx) : NULL; if(!tmp && param_type) tmp = param_type; errors += (*cur)->expr()->elaborate_expr(ent, scope, tmp); } } return errors; } int VariableSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; // Elaborate the l-value expression. errors += lval_->elaborate_lval(ent, scope, true); // The elaborate_lval should have resolved the type of the // l-value expression. We'll use that type to elaborate the // r-value. const VType*lval_type = lval_->peek_type(); if (lval_type == 0) { if (errors == 0) errors += 1; return errors; } // Elaborate the r-value expression. errors += rval_->elaborate_expr(ent, scope, lval_type); return errors; } int WhileLoopStatement::elaborate(Entity*, ScopeBase*) { //TODO:check whether there is any wait statement in the statements (there should be) return 0; } int BasicLoopStatement::elaborate(Entity*, ScopeBase*) { return 0; } int AssertStmt::elaborate(Entity*ent, ScopeBase*scope) { return cond_->elaborate_expr(ent, scope, 0); } int WaitForStmt::elaborate(Entity*ent, ScopeBase*scope) { return delay_->elaborate_expr(ent, scope, 0); } int WaitStmt::elaborate(Entity*ent, ScopeBase*scope) { if(type_ == UNTIL) { struct fill_sens_list_t : public ExprVisitor { fill_sens_list_t(set& sig_list) : sig_list_(sig_list) {}; void operator() (Expression*s) { if(ExpName*name = dynamic_cast(s)) sig_list_.insert(name); } private: set& sig_list_; } fill_sens_list(sens_list_); // Fill the sensitivity list expr_->visit(fill_sens_list); } return expr_->elaborate_expr(ent, scope, 0); } iverilog-10_1/vhdlpp/sequential_emit.cc000066400000000000000000000357641265551621300203550ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sequential.h" # include "expression.h" # include "architec.h" # include "package.h" # include "compiler.h" # include "subprogram.h" # include # include # include # include int SequentialStmt::emit(ostream&out, Entity*, ScopeBase*) { out << " // " << get_fileline() << ": internal error: " << "I don't know how to emit this sequential statement! " << "type=" << typeid(*this).name() << endl; return 1; } void SequentialStmt::write_to_stream(std::ostream&fd) { fd << " // " << get_fileline() << ": internal error: " << "I don't know how to write_to_stream this sequential statement! " << "type=" << typeid(*this).name() << endl; } int IfSequential::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "if ("; errors += cond_->emit(out, ent, scope); out << ") begin" << endl; for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) errors += (*cur)->emit(out, ent, scope); for (list::iterator cur = elsif_.begin() ; cur != elsif_.end() ; ++cur) { out << "end else if ("; errors += (*cur)->condition_emit(out, ent, scope); out << ") begin" << endl; errors += (*cur)->statement_emit(out, ent, scope); } if (! else_.empty()) { out << "end else begin" << endl; for (list::iterator cur = else_.begin() ; cur != else_.end() ; ++cur) errors += (*cur)->emit(out, ent, scope); } out << "end" << endl; return errors; } void IfSequential::write_to_stream(std::ostream&fd) { fd << "if "; cond_->write_to_stream(fd); fd << " then " << endl; for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) (*cur)->write_to_stream(fd); for (list::iterator cur = elsif_.begin() ; cur != elsif_.end() ; ++cur) { fd << "elsif "; (*cur)->condition_write_to_stream(fd); fd << " " << endl; (*cur)->statement_write_to_stream(fd); } if (! else_.empty()) { fd << " else " << endl; for (list::iterator cur = else_.begin() ; cur != else_.end() ; ++cur) (*cur)->write_to_stream(fd); } fd << "end if;" << endl; } int IfSequential::Elsif::condition_emit(ostream&out, Entity*ent, ScopeBase*scope) { return cond_->emit(out, ent, scope); } int IfSequential::Elsif::statement_emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) errors += (*cur)->emit(out, ent, scope); return errors; } void IfSequential::Elsif::condition_write_to_stream(ostream&fd) { cond_->write_to_stream(fd); } void IfSequential::Elsif::statement_write_to_stream(ostream&fd) { for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) (*cur)->write_to_stream(fd); } int ReturnStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "return "; errors += val_->emit(out, ent, scope); out << ";" << endl; return errors; } void ReturnStmt::write_to_stream(ostream&fd) { fd << "return "; val_->write_to_stream(fd); fd << ";" << endl; } int SignalSeqAssignment::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += lval_->emit(out, ent, scope); if (waveform_.size() != 1) { out << "/* Confusing waveform? */;" << endl; errors += 1; } else { Expression*tmp = waveform_.front(); out << " <= "; errors += tmp->emit(out, ent, scope); out << ";" << endl; } return errors; } void SignalSeqAssignment::write_to_stream(ostream&fd) { lval_->write_to_stream(fd); if (waveform_.size() != 1) { fd << "-- Confusing waveform?" << endl; } else { Expression*tmp = waveform_.front(); fd << " <= "; tmp->write_to_stream(fd); fd << ";" << endl; } } int VariableSeqAssignment::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; errors += lval_->emit(out, ent, scope); out << " = "; errors += rval_->emit(out, ent, scope); out << ";" << endl; return errors; } void VariableSeqAssignment::write_to_stream(ostream&fd) { lval_->write_to_stream(fd); fd << " := "; rval_->write_to_stream(fd); fd << ";" << endl; } int ProcedureCall::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; std::vectorparams(param_list_->size()); int i = 0; for(std::list::iterator it = param_list_->begin(); it != param_list_->end(); ++it) params[i++] = (*it)->expr(); const Package*pkg = dynamic_cast (def_->get_parent()); if (pkg != 0) out << "\\" << pkg->name() << " ::"; errors += def_->emit_name(params, out, ent, scope); out << " ("; if(param_list_) { errors += def_->emit_args(params, out, ent, scope); } out << ");" << endl; return errors; } int LoopStatement::emit_substatements(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; for (list::iterator cur = stmts_.begin() ; cur != stmts_.end() ; ++cur) { SequentialStmt*tmp = *cur; errors += tmp->emit(out, ent, scope); } return errors; } void LoopStatement::write_to_stream_substatements(ostream&fd) { for (list::iterator cur = stmts_.begin() ; cur != stmts_.end() ; ++cur) { SequentialStmt*tmp = *cur; tmp->write_to_stream(fd); } } int CaseSeqStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "case ("; errors += cond_->emit(out, ent, scope); out << ")" << endl; for (list::iterator cur = alt_.begin() ; cur != alt_.end() ; ++cur) { CaseStmtAlternative*curp = *cur; errors += curp ->emit(out, ent, scope); } out << "endcase" << endl; return errors; } void CaseSeqStmt::write_to_stream(ostream&fd) { fd << "case "; cond_->write_to_stream(fd); fd << " is" << endl; for (list::iterator cur = alt_.begin() ; cur != alt_.end() ; ++cur) { CaseStmtAlternative*curp = *cur; curp ->write_to_stream(fd); } fd << "end case;" << endl; } int CaseSeqStmt::CaseStmtAlternative::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; bool first = true; if (exp_) { for (list::iterator it = exp_->begin(); it != exp_->end(); ++it) { if(first) first = false; else out << ","; errors += (*it)->emit(out, ent, scope); } } else { out << "default"; } out << ":" << endl; SequentialStmt*curp; switch (stmts_.size()) { case 0: out << "/* no op */;" << endl; break; case 1: curp = stmts_.front(); errors += curp->emit(out, ent, scope); break; default: out << "begin" << endl; for (list::iterator cur = stmts_.begin() ; cur != stmts_.end() ; ++cur) { curp = *cur; errors += curp->emit(out, ent, scope); } out << "end" << endl; break; } return errors; } void CaseSeqStmt::CaseStmtAlternative::write_to_stream(ostream&fd) { fd << "when "; if (exp_) { bool first = true; for (list::iterator it = exp_->begin(); it != exp_->end(); ++it) { if(first) first = false; else fd << "|"; (*it)->write_to_stream(fd); } } else { fd << "others" << endl; } fd << "=>" << endl; for (list::iterator cur = stmts_.begin() ; cur != stmts_.end() ; ++cur) { (*cur)->write_to_stream(fd); } } int ForLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; ivl_assert(*this, range_); int64_t start_val; bool start_rc = range_->msb()->evaluate(ent, scope, start_val); int64_t finish_val; bool finish_rc = range_->lsb()->evaluate(ent, scope, finish_val); perm_string scope_name = loop_name(); if (scope_name.nil()) { char buf[80]; snprintf(buf, sizeof buf, "__%p", this); scope_name = lex_strings.make(buf); } out << "begin : " << scope_name << endl; out << "longint \\" << it_ << " ;" << endl; if(!start_rc || !finish_rc) { // Could not evaluate one of the loop boundaries, it has to be // determined during the run-time errors += emit_runtime_(out, ent, scope); } else { bool dir = range_->is_downto(); if (!dir) { int64_t tmp = start_val; start_val = finish_val; finish_val = tmp; } if (dir && (start_val < finish_val)) { if(range_->is_auto_dir()) { dir = false; } else { out << "begin /* Degenerate loop at " << get_fileline() << ": " << start_val << " downto " << finish_val << " */ end" << endl << "end" << endl; return errors; } } else if (!dir && start_val > finish_val) { if(range_->is_auto_dir()) { dir = true; } else { out << "begin /* Degenerate loop at " << get_fileline() << ": " << start_val << " to " << finish_val << " */ end" << endl << "end" << endl; return errors; } } out << "for (\\" << it_ << " = " << start_val << " ; "; if (dir) out << "\\" << it_ << " >= " << finish_val; else out << "\\" << it_ << " <= " << finish_val; out << "; \\" << it_ << " = \\" << it_; if (dir) out << " - 1)"; else out << " + 1)"; } out << " begin" << endl; errors += emit_substatements(out, ent, scope); out << "end" << endl; out << "end /* " << scope_name << " */" << endl; return errors; } void ForLoopStatement::write_to_stream(ostream&fd) { fd << "for " << it_ << " in "; range_->expr_left()->write_to_stream(fd); fd << " to "; range_->expr_right()->write_to_stream(fd); fd << " loop" << endl; write_to_stream_substatements(fd); fd << "end loop;" << endl; } int ForLoopStatement::emit_runtime_(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "for (\\" << it_ << " = "; errors += range_->expr_left()->emit(out, ent, scope); // Twisted way of determining the loop direction at runtime out << " ;\n("; errors += range_->expr_left()->emit(out, ent, scope); out << " < "; errors += range_->expr_right()->emit(out, ent, scope); out << " ? \\" << it_ << " <= "; errors += range_->expr_right()->emit(out, ent, scope); out << " : \\" << it_ << " >= "; errors += range_->expr_right()->emit(out, ent, scope); out << ");\n\\" << it_ << " = \\" << it_ << " + ("; errors += range_->expr_left()->emit(out, ent, scope); out << " < "; errors += range_->expr_right()->emit(out, ent, scope); out << " ? 1 : -1))"; return errors; } int ReportStmt::emit(ostream&out, Entity*, ScopeBase*) { out << "$display(\""; switch(severity_) { case NOTE: out << "** Note: "; break; case WARNING: out << "** Warning: "; break; case ERROR: out << "** Error: "; break; case FAILURE: out << "** Failure: "; break; case UNSPECIFIED: ivl_assert(*this, false); break; } out << msg_; out << " (" << get_fileline() << ")\");"; if(severity_ == FAILURE) out << "$finish();"; out << std::endl; return 0; } void ReportStmt::write_to_stream(std::ostream&fd) { fd << "report \"" << msg_ << "\"" << std::endl; fd << "severity "; switch(severity_) { case NOTE: fd << "NOTE"; break; case WARNING: fd << "WARNING"; break; case ERROR: fd << "ERROR"; break; case FAILURE: fd << "FAILURE"; break; case UNSPECIFIED: break; } fd << ";" << std::endl; } int AssertStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "if(!("; errors += cond_->emit(out, ent, scope); out << ")) begin" << std::endl; errors += ReportStmt::emit(out, ent, scope); out << "end" << std::endl; return errors; } void AssertStmt::write_to_stream(std::ostream&fd) { fd << "assert "; cond_->write_to_stream(fd); fd << std::endl; ReportStmt::write_to_stream(fd); } int WaitForStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; out << "#("; errors += delay_->emit(out, ent, scope); out << ")"; return errors; } void WaitForStmt::write_to_stream(std::ostream&fd) { fd << "wait for "; delay_->write_to_stream(fd); } int WaitStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; switch(type_) { case ON: out << "@("; break; case UNTIL: if(!sens_list_.empty()) { out << "@("; for(std::set::iterator it = sens_list_.begin(); it != sens_list_.end(); ++it) { if(it != sens_list_.begin()) out << ","; (*it)->emit(out, ent, scope); } out << ");"; } out << "wait("; break; } errors += expr_->emit(out, ent, scope); out << ");" << endl; return errors; } void WaitStmt::write_to_stream(std::ostream&fd) { switch(type_) { case ON: fd << "wait on "; break; case UNTIL: fd << "wait until "; break; } expr_->write_to_stream(fd); } iverilog-10_1/vhdlpp/std_funcs.cc000066400000000000000000000226561265551621300171510ustar00rootroot00000000000000/* * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "std_funcs.h" #include "std_types.h" #include "scope.h" static std::map std_subprograms; // Special case: to_integer function static class SubprogramToInteger : public SubprogramHeader { public: SubprogramToInteger() : SubprogramHeader(perm_string::literal("to_integer"), NULL, &primitive_REAL) { ports_ = new std::list(); ports_->push_back(new InterfacePort(&primitive_INTEGER)); } bool is_std() const { return true; } int emit_name(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { bool signed_flag = false; // to_integer converts unsigned to natural // signed to integer // try to determine the converted type const VType*type = argv[0]->probe_type(ent, scope); const VTypeArray*array = dynamic_cast(type); if(array) { signed_flag = array->signed_vector(); } else { cerr << get_fileline() << ": sorry: Could not determine the " << "expression sign. Output may be erroneous." << endl; return 1; } out << (signed_flag ? "$signed" : "$unsigned"); return 0; } }*fn_to_integer; // Special case: size casting (e.g. conv_std_logic_vector() / resize()). static class SubprogramSizeCast : public SubprogramHeader { public: SubprogramSizeCast(perm_string nam) : SubprogramHeader(nam, NULL, &primitive_STDLOGIC_VECTOR) { ports_ = new std::list(); ports_->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); ports_->push_back(new InterfacePort(&primitive_INTEGER)); } bool is_std() const { return true; } int emit_name(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { int64_t use_size; bool rc = argv[1]->evaluate(ent, scope, use_size); if(!rc) { cerr << get_fileline() << ": sorry: Could not evaluate the " << "expression size. Size casting impossible." << endl; return 1; } out << use_size << "'"; return 0; } int emit_args(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { return argv[0]->emit(out, ent, scope); } }*fn_conv_std_logic_vector, *fn_resize; static SubprogramBuiltin*fn_std_logic_vector; static SubprogramBuiltin*fn_to_unsigned; static SubprogramBuiltin*fn_unsigned; static SubprogramBuiltin*fn_integer; static SubprogramBuiltin*fn_rising_edge; static SubprogramBuiltin*fn_falling_edge; static SubprogramBuiltin*fn_and_reduce; static SubprogramBuiltin*fn_or_reduce; void preload_std_funcs(void) { /* numeric_std library * function unsigned */ std::list*fn_unsigned_args = new std::list(); fn_unsigned_args->push_back(new InterfacePort(&primitive_INTEGER)); fn_unsigned = new SubprogramBuiltin(perm_string::literal("unsigned"), perm_string::literal("$unsigned"), fn_unsigned_args, &primitive_UNSIGNED); std_subprograms[fn_unsigned->name()] = fn_unsigned; /* function integer */ std::list*fn_integer_args = new std::list(); fn_integer_args->push_back(new InterfacePort(&primitive_INTEGER)); fn_integer = new SubprogramBuiltin(perm_string::literal("integer"), perm_string::literal("$signed"), fn_integer_args, &primitive_INTEGER); std_subprograms[fn_integer->name()] = fn_integer; /* function std_logic_vector Special case: The std_logic_vector function casts its argument to std_logic_vector. Internally, we don't have to do anything for that to work. */ std::list*fn_std_logic_vector_args = new std::list(); fn_std_logic_vector_args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); fn_std_logic_vector = new SubprogramBuiltin(perm_string::literal("std_logic_vector"), empty_perm_string, fn_std_logic_vector_args, &primitive_STDLOGIC_VECTOR); std_subprograms[fn_std_logic_vector->name()] = fn_std_logic_vector; /* function resize */ fn_resize = new SubprogramSizeCast(perm_string::literal("resize")); std_subprograms[fn_resize->name()] = fn_resize; /* function conv_std_logic_vector */ fn_conv_std_logic_vector = new SubprogramSizeCast(perm_string::literal("conv_std_logic_vector")); std_subprograms[fn_conv_std_logic_vector->name()] = fn_conv_std_logic_vector; /* numeric_bit library * function to_integer (arg: unsigned) return natural; * function to_integer (arg: signed) return integer; */ fn_to_integer = new SubprogramToInteger(); std_subprograms[fn_to_integer->name()] = fn_to_integer; /* std_logic_1164 library * function rising_edge (signal s : std_ulogic) return boolean; */ std::list*fn_rising_edge_args = new std::list(); fn_rising_edge_args->push_back(new InterfacePort(&primitive_STDLOGIC)); fn_rising_edge = new SubprogramBuiltin(perm_string::literal("rising_edge"), perm_string::literal("$ivlh_rising_edge"), fn_rising_edge_args, &type_BOOLEAN); std_subprograms[fn_rising_edge->name()] = fn_rising_edge; /* std_logic_1164 library * function falling_edge (signal s : std_ulogic) return boolean; */ std::list*fn_falling_edge_args = new std::list(); fn_falling_edge_args->push_back(new InterfacePort(&primitive_STDLOGIC)); fn_falling_edge = new SubprogramBuiltin(perm_string::literal("falling_edge"), perm_string::literal("$ivlh_falling_edge"), fn_falling_edge_args, &type_BOOLEAN); std_subprograms[fn_falling_edge->name()] = fn_falling_edge; /* reduce_pack library * function or_reduce(arg : std_logic_vector) return std_logic; */ std::list*fn_or_reduce_args = new std::list(); fn_or_reduce_args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); fn_or_reduce = new SubprogramBuiltin(perm_string::literal("or_reduce"), perm_string::literal("|"), fn_or_reduce_args, &primitive_STDLOGIC); std_subprograms[fn_or_reduce->name()] = fn_or_reduce; /* reduce_pack library * function and_reduce(arg : std_logic_vector) return std_logic; */ std::list*fn_and_reduce_args = new std::list(); fn_and_reduce_args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); fn_and_reduce = new SubprogramBuiltin(perm_string::literal("and_reduce"), perm_string::literal("&"), fn_and_reduce_args, &primitive_STDLOGIC); std_subprograms[fn_and_reduce->name()] = fn_and_reduce; /* fixed_pkg library * function to_unsigned ( * arg : ufixed; -- fixed point input * constant size : natural) -- length of output * return unsigned; */ std::list*fn_to_unsigned_args = new std::list(); fn_to_unsigned_args->push_back(new InterfacePort(&primitive_REAL)); fn_to_unsigned_args->push_back(new InterfacePort(&primitive_NATURAL)); fn_to_unsigned = new SubprogramBuiltin(perm_string::literal("to_unsigned"), perm_string::literal("$ivlh_to_unsigned"), fn_to_unsigned_args, &primitive_UNSIGNED); std_subprograms[fn_to_unsigned->name()] = fn_to_unsigned; } void delete_std_funcs() { for(std::map::iterator it = std_subprograms.begin(); it != std_subprograms.end(); ++it) { delete it->second; } } SubprogramHeader*find_std_subprogram(perm_string name) { map::const_iterator cur = std_subprograms.find(name); if (cur != std_subprograms.end()) return cur->second; return NULL; } iverilog-10_1/vhdlpp/std_funcs.h000066400000000000000000000024251265551621300170030ustar00rootroot00000000000000#ifndef IVL_std_funcs_H #define IVL_std_funcs_H /* * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "subprogram.h" // Generates subprogram headers for standard VHDL library functions. void preload_std_funcs(); // Destroys subprogram headers for standard VHDL library functions. void delete_std_funcs(); // Returns subprogram header for a requested function or NULL if it does not exist. SubprogramHeader*find_std_subprogram(perm_string name); #endif /* IVL_std_funcs_H */ iverilog-10_1/vhdlpp/std_types.cc000066400000000000000000000115401265551621300171650ustar00rootroot00000000000000/* * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "std_types.h" #include "scope.h" static std::map std_types; // this list contains enums used by typedefs in the std_types map static std::list std_enums; const VTypePrimitive primitive_BIT(VTypePrimitive::BIT, true); const VTypePrimitive primitive_INTEGER(VTypePrimitive::INTEGER); const VTypePrimitive primitive_NATURAL(VTypePrimitive::NATURAL); const VTypePrimitive primitive_REAL(VTypePrimitive::REAL); const VTypePrimitive primitive_STDLOGIC(VTypePrimitive::STDLOGIC, true); const VTypePrimitive primitive_CHARACTER(VTypePrimitive::CHARACTER); const VTypePrimitive primitive_TIME(VTypePrimitive::TIME); VTypeDef type_BOOLEAN(perm_string::literal("boolean")); const VTypeArray primitive_BIT_VECTOR(&primitive_BIT, vector (1)); const VTypeArray primitive_BOOL_VECTOR(&type_BOOLEAN, vector (1)); const VTypeArray primitive_STDLOGIC_VECTOR(&primitive_STDLOGIC, vector (1)); const VTypeArray primitive_STRING(&primitive_CHARACTER, vector (1)); const VTypeArray primitive_SIGNED(&primitive_STDLOGIC, vector (1), true); const VTypeArray primitive_UNSIGNED(&primitive_STDLOGIC, vector (1), false); void generate_global_types(ActiveScope*res) { // boolean std::list*enum_BOOLEAN_vals = new std::list; enum_BOOLEAN_vals->push_back(perm_string::literal("false")); enum_BOOLEAN_vals->push_back(perm_string::literal("true")); VTypeEnum*enum_BOOLEAN = new VTypeEnum(enum_BOOLEAN_vals); type_BOOLEAN.set_definition(enum_BOOLEAN); std_types[type_BOOLEAN.peek_name()] = &type_BOOLEAN; std_enums.push_back(enum_BOOLEAN); res->use_name(type_BOOLEAN.peek_name(), &type_BOOLEAN); res->use_name(perm_string::literal("bit"), &primitive_BIT); res->use_name(perm_string::literal("bit_vector"), &primitive_BIT_VECTOR); res->use_name(perm_string::literal("integer"), &primitive_INTEGER); res->use_name(perm_string::literal("real"), &primitive_REAL); res->use_name(perm_string::literal("std_logic"), &primitive_STDLOGIC); res->use_name(perm_string::literal("character"), &primitive_CHARACTER); res->use_name(perm_string::literal("string"), &primitive_STRING); res->use_name(perm_string::literal("natural"), &primitive_NATURAL); res->use_name(perm_string::literal("time"), &primitive_TIME); } void delete_global_types() { typedef_context_t typedef_ctx; for(map::iterator cur = std_types.begin(); cur != std_types.end() ; ++ cur) { delete cur->second->peek_definition(); delete cur->second; } } const VTypeEnum*find_std_enum_name(perm_string name) { for(list::const_iterator it = std_enums.begin(); it != std_enums.end(); ++it) { if((*it)->has_name(name)) return *it; } return NULL; } void emit_std_types(ostream&fd) { fd << "`ifndef __VHDL_STD_TYPES" << endl; fd << "`define __VHDL_STD_TYPES" << endl; typedef_context_t typedef_ctx; for(map::iterator cur = std_types.begin(); cur != std_types.end() ; ++ cur) { cur->second->emit_typedef(fd, typedef_ctx); } fd << "`endif" << endl; } bool is_global_type(perm_string name) { if (name == "boolean") return true; if (name == "bit") return true; if (name == "bit_vector") return true; if (name == "integer") return true; if (name == "real") return true; if (name == "std_logic") return true; if (name == "std_logic_vector") return true; if (name == "character") return true; if (name == "string") return true; if (name == "natural") return true; if (name == "signed") return true; if (name == "unsigned") return true; if (name == "time") return true; return std_types.count(name) > 0; } iverilog-10_1/vhdlpp/std_types.h000066400000000000000000000034241265551621300170310ustar00rootroot00000000000000/* * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vtype.h" class ActiveScope; void emit_std_types(ostream&out); int emit_packages(void); void generate_global_types(ActiveScope*res); bool is_global_type(perm_string type_name); void delete_global_types(); const VTypeEnum*find_std_enum_name(perm_string name); extern const VTypePrimitive primitive_BOOLEAN; extern const VTypePrimitive primitive_BIT; extern const VTypePrimitive primitive_INTEGER; extern const VTypePrimitive primitive_NATURAL; extern const VTypePrimitive primitive_REAL; extern const VTypePrimitive primitive_STDLOGIC; extern const VTypePrimitive primitive_CHARACTER; extern const VTypePrimitive primitive_TIME; extern VTypeDef type_BOOLEAN; extern const VTypeArray primitive_BIT_VECTOR; extern const VTypeArray primitive_BOOL_VECTOR; extern const VTypeArray primitive_STDLOGIC_VECTOR; extern const VTypeArray primitive_STRING; extern const VTypeArray primitive_SIGNED; extern const VTypeArray primitive_UNSIGNED; iverilog-10_1/vhdlpp/subprogram.cc000066400000000000000000000213621265551621300173330ustar00rootroot00000000000000/* * Copyright (c) 2013-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "subprogram.h" # include "entity.h" # include "vtype.h" # include "sequential.h" # include "ivl_assert.h" # include "compiler.h" # include using namespace std; SubprogramBody::SubprogramBody() : statements_(NULL), header_(NULL) { } SubprogramBody::~SubprogramBody() { } const InterfacePort*SubprogramBody::find_param(perm_string nam) const { if(!header_) return NULL; return header_->find_param(nam); } void SubprogramBody::set_statements(list*stmt) { ivl_assert(*this, statements_==0); statements_ = stmt; } void SubprogramBody::write_to_stream(ostream&fd) const { for (map::const_iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { cur->second->write_to_stream(fd); } fd << "begin" << endl; if (statements_) { for (list::const_iterator cur = statements_->begin() ; cur != statements_->end() ; ++cur) { (*cur)->write_to_stream(fd); } } else { fd << "--empty body" << endl; } fd << "end function;" << endl; } SubprogramHeader::SubprogramHeader(perm_string nam, list*ports, const VType*return_type) : name_(nam), ports_(ports), return_type_(return_type), body_(NULL), parent_(NULL) { } SubprogramHeader::~SubprogramHeader() { delete body_; if(ports_) { for(list::iterator it = ports_->begin(); it != ports_->end(); ++it) { delete *it; } delete ports_; } } bool SubprogramHeader::compare_specification(SubprogramHeader*that) const { if (name_ != that->name_) return false; if (return_type_==0) { if (that->return_type_!=0) return false; } else { if (that->return_type_==0) return false; if (! return_type_->type_match(that->return_type_)) return false; } if (ports_==0) { if (that->ports_!=0) return false; } else { if (that->ports_==0) return false; if (ports_->size() != that->ports_->size()) return false; } return true; } const InterfacePort*SubprogramHeader::find_param(perm_string nam) const { if(!ports_) return NULL; for (std::list::const_iterator it = ports_->begin() ; it != ports_->end(); ++it) { if((*it)->name == nam) return *it; } return NULL; } const VType*SubprogramHeader::peek_param_type(int idx) const { if(!ports_ || idx < 0 || (size_t)idx >= ports_->size()) return NULL; std::list::const_iterator p = ports_->begin(); std::advance(p, idx); return (*p)->type; } void SubprogramHeader::set_parent(const ScopeBase*par) { ivl_assert(*this, !parent_); parent_ = par; } bool SubprogramHeader::unbounded() const { if(return_type_ && return_type_->is_unbounded()) return true; if(ports_) { for(std::list::const_iterator it = ports_->begin(); it != ports_->end(); ++it) { if((*it)->type->is_unbounded()) return true; } } return false; } void SubprogramHeader::set_body(SubprogramBody*bdy) { ivl_assert(*this, !body_); body_ = bdy; ivl_assert(*this, !bdy->header_); bdy->header_ = this; } SubprogramHeader*SubprogramHeader::make_instance(std::vector arguments, ScopeBase*scope) const { assert(arguments.size() == ports_->size()); std::list*ports = new std::list; int i = 0; // Change the argument types to match the ones that were used during // the function call for(std::list::iterator it = ports_->begin(); it != ports_->end(); ++it) { InterfacePort*p = new InterfacePort(**it); p->type = arguments[i++]->peek_type()->clone(); assert(p->type); ports->push_back(p); } char buf[80]; snprintf(buf, sizeof(buf), "__%s_%p", name_.str(), ports); perm_string new_name = lex_strings.make(buf); SubprogramHeader*instance = new SubprogramHeader(new_name, ports, return_type_); if(body_) { SubprogramBody*body_inst = new SubprogramBody(); // Copy variables for(std::map::iterator it = body_->new_variables_.begin(); it != body_->new_variables_.end(); ++it) { Variable*v = new Variable(it->first, it->second->peek_type()->clone()); body_inst->new_variables_[it->first] = v; } body_inst->set_statements(body_->statements_); instance->set_parent(scope); instance->set_body(body_inst); instance->fix_return_type(); } scope->bind_subprogram(new_name, instance); return instance; } struct check_return_type : public SeqStmtVisitor { check_return_type(const SubprogramBody*subp) : subp_(subp), ret_type_(NULL) {} void operator() (SequentialStmt*s) { ReturnStmt*ret; if((ret = dynamic_cast(s))) { const Expression*expr = ret->peek_expr(); const VType*t = NULL; if(const ExpName*n = dynamic_cast(expr)) { if(Variable*v = subp_->find_variable(n->peek_name())) t = v->peek_type(); } else { t = expr->peek_type(); } if(!t) { // cannot determine the type at least in one case ret_type_ = NULL; return; } if(!ret_type_) { // this is first processed return statement ret_type_ = t; } else if(!t->type_match(ret_type_)) { // the function can return different types, // we cannot have fixed width ret_type_ = NULL; return; } } } const VType*get_type() const { return ret_type_; } private: const SubprogramBody*subp_; const VType*ret_type_; }; void SubprogramHeader::fix_return_type() { if(!body_ || !body_->statements_) return; check_return_type r(body_); for (std::list::iterator s = body_->statements_->begin() ; s != body_->statements_->end(); ++s) { (*s)->visit(r); } VType*return_type = const_cast(r.get_type()); if(return_type && !return_type->is_unbounded()) { // Let's check if the variable length can be evaluated without any scope. // If not, then it depends on information about e.g. function params if(return_type->is_variable_length(NULL)) { if(VTypeArray*arr = dynamic_cast(return_type)) arr->evaluate_ranges(body_); } return_type_ = return_type; } } void SubprogramHeader::write_to_stream(ostream&fd) const { if(return_type_) fd << "function "; else fd << "procedure "; fd << name_; if (ports_ && ! ports_->empty()) { fd << "("; list::const_iterator cur = ports_->begin(); InterfacePort*curp = *cur; fd << curp->name << " : "; curp->type->write_to_stream(fd); for (++cur ; cur != ports_->end() ; ++cur) { curp = *cur; fd << "; " << curp->name << " : "; curp->type->write_to_stream(fd); } fd << ")"; } if( return_type_) { fd << " return "; return_type_->write_to_stream(fd); } } SubprogramBuiltin::SubprogramBuiltin(perm_string vhdl_name, perm_string sv_name, std::list*ports, const VType*return_type) : SubprogramHeader(vhdl_name, ports, return_type), sv_name_(sv_name) { } SubprogramBuiltin::~SubprogramBuiltin() { } iverilog-10_1/vhdlpp/subprogram.h000066400000000000000000000121411265551621300171700ustar00rootroot00000000000000#ifndef IVL_subprogram_H #define IVL_subprogram_H /* * Copyright (c) 2013-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2015 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include "LineInfo.h" # include "scope.h" # include # include # include class InterfacePort; class SequentialStmt; class VType; class SubprogramHeader; class SubprogramBody : public LineInfo, public ScopeBase { public: SubprogramBody(); ~SubprogramBody(); const InterfacePort*find_param(perm_string nam) const; void set_statements(std::list*statements); inline bool empty_statements() const { return !statements_ || statements_->empty(); } int emit(ostream&out, Entity*ent, ScopeBase*scope); // Emit body as it would show up in a package. int emit_package(std::ostream&fd) const; void write_to_stream(std::ostream&fd) const; void dump(std::ostream&fd) const; private: std::list*statements_; SubprogramHeader*header_; friend class SubprogramHeader; }; class SubprogramHeader : public LineInfo { public: SubprogramHeader(perm_string name, std::list*ports, const VType*return_type); virtual ~SubprogramHeader(); // Return true if the specification (name, types, ports) // matches this subprogram and that subprogram. bool compare_specification(SubprogramHeader*that) const; const InterfacePort*find_param(perm_string nam) const; const VType*peek_param_type(int idx) const; const VType*peek_return_type() const { return return_type_; } void set_parent(const ScopeBase*par); inline const ScopeBase*get_parent() const { return parent_; } // Checks if either return type or parameters are unbounded vectors. bool unbounded() const; // Is the subprogram coming from the standard library? virtual bool is_std() const { return false; } inline SubprogramBody*body() const { return body_; } void set_body(SubprogramBody*bdy); inline perm_string name() const { return name_; } // Function name used in the emission step. The main purpose of this // method is to handle functions offered by standard VHDL libraries. // Allows to return different function names depending on the arguments // (think of size casting or signed/unsigned functions). virtual int emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const; // Emit arguments for a specific call. It allows to reorder or skip // some of the arguments if function signature is different in // SystemVerilog compared to VHDL. virtual int emit_args(const std::vector&argv, std::ostream&out, Entity*, ScopeBase*) const; // Creates a new instance of the function that takes arguments of // a different type. It is used to allow VHDL functions that work with // unbounded std_logic_vectors, so there can be a separate instance // for limited length logic vector. SubprogramHeader*make_instance(std::vector arguments, ScopeBase*scope) const; // Emit header as it would show up in a package. int emit_package(std::ostream&fd) const; void write_to_stream(std::ostream&fd) const; void dump(std::ostream&fd) const; protected: // Tries to set the return type to a fixed type. VHDL functions that // return std_logic_vectors do not specify its length, as SystemVerilog // demands. void fix_return_type(); // Procedure/function name perm_string name_; std::list*ports_; const VType*return_type_; SubprogramBody*body_; const ScopeBase*parent_; }; // Class to define functions headers defined in the standard VHDL libraries. class SubprogramBuiltin : public SubprogramHeader { public: SubprogramBuiltin(perm_string vhdl_name, perm_string sv_name, std::list*ports, const VType*return_type); ~SubprogramBuiltin(); bool is_std() const { return true; } int emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const; private: // SystemVerilog counterpart function name perm_string sv_name_; }; #endif /* IVL_subprogram_H */ iverilog-10_1/vhdlpp/subprogram_emit.cc000066400000000000000000000066101265551621300203500ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "subprogram.h" # include "sequential.h" # include "vtype.h" # include using namespace std; int SubprogramBody::emit_package(ostream&fd) const { int errors = 0; for (map::const_iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { // Workaround to enable reg_flag for variables cur->second->count_ref_sequ(); errors += cur->second->emit(fd, NULL, NULL); } if (statements_) { for (list::const_iterator cur = statements_->begin() ; cur != statements_->end() ; ++cur) { errors += (*cur)->emit(fd, NULL, const_cast(this)); } } else { fd << " begin /* empty body */ end" << endl; } return errors; } int SubprogramHeader::emit_package(ostream&fd) const { int errors = 0; if (return_type_) { fd << "function "; return_type_->emit_def(fd, empty_perm_string); } else { fd << "task"; } fd << " \\" << name_ << " ("; for (list::const_iterator cur = ports_->begin() ; cur != ports_->end() ; ++cur) { if (cur != ports_->begin()) fd << ", "; InterfacePort*curp = *cur; switch (curp->mode) { case PORT_IN: fd << "input "; break; case PORT_OUT: fd << "output "; break; case PORT_INOUT: fd << "inout "; break; case PORT_NONE: fd << "inout /* PORT_NONE? */ "; break; } errors += curp->type->emit_def(fd, curp->name); } fd << ");" << endl; if (body_) body_->emit_package(fd); if (return_type_) fd << "endfunction" << endl; else fd << "endtask" << endl; return errors; } int SubprogramHeader::emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const { out << "\\" << name_; return 0; } int SubprogramHeader::emit_args(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; for (size_t idx = 0; idx < argv.size() ; idx += 1) { if (idx > 0) out << ", "; errors += argv[idx]->emit(out, ent, scope); } return errors; } int SubprogramBuiltin::emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const { // do not escape the names for builtin functions out << sv_name_; return 0; } iverilog-10_1/vhdlpp/vhdlint.cc000066400000000000000000000036211265551621300166200ustar00rootroot00000000000000 /* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vhdlint.h" #include #include #include using namespace std; bool vhdlint::is_negative() const { return value_ < 0L; } bool vhdlint::is_positive() const { return value_ > 0L; } bool vhdlint::is_zero() const { return value_ == 0L; } vhdlint::vhdlint(const char* text) { unsigned text_length = strlen(text); if(text_length == 0) { value_ = 0L; return; } char* new_text = new char[text_length + 1]; const char* ptr; char* new_ptr; for(ptr = text, new_ptr = new_text; *ptr != 0; ++ptr) { if(*ptr == '_') continue; else { *new_ptr = *ptr; ++new_ptr; } } *new_ptr = 0; istringstream str(new_text); delete[] new_text; //TODO: check if numbers greater than MAX_INT are handled correctly str >> value_; } vhdlint::vhdlint(const int64_t& val) { value_ = val; } vhdlint::vhdlint(const vhdlint& val) { value_ = val.as_long(); } int64_t vhdlint::as_long() const { return value_; } iverilog-10_1/vhdlpp/vhdlint.h000066400000000000000000000026571265551621300164720ustar00rootroot00000000000000#ifndef IVL_vhdlint_H #define IVL_vhdlint_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include class vhdlint { public: explicit vhdlint(const char* text); explicit vhdlint(const int64_t& val); explicit vhdlint(const vhdlint& val); bool is_negative() const; bool is_positive() const; bool is_zero() const; int64_t as_long() const; //vhdlv get(const unsigned index) const; //void set(const unsigned index, const unsigned val); // unsigned short operator[](const unsigned index); private: int64_t value_; }; #endif /* IVL_vhdlint_H */ iverilog-10_1/vhdlpp/vhdlnum.h000066400000000000000000000002561265551621300164700ustar00rootroot00000000000000#ifndef IVL_vhdlnum_H #define IVL_vhdlnum_H #include "config.h" #warning vhdlnum class vhdlnum { public: vhdlnum(char* text) {} }; #endif /* IVL_vhdlnum_H */ iverilog-10_1/vhdlpp/vhdlpp_config.h.in000066400000000000000000000024361265551621300202440ustar00rootroot00000000000000#ifndef IVL_vhdlpp_config_H /* -*- c++ -*- */ #define IVL_vhdlpp_config_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined(__cplusplus) # if !defined(__GNUC__) using namespace std; # elif (__GNUC__ == 3) using namespace std; # endif #endif # undef HAVE_GETOPT_H # undef HAVE_INTTYPES_H # undef HAVE_LIBIBERTY_H # undef HAVE_FCHMOD # undef HAVE_SYS_WAIT_H #ifdef HAVE_INTTYPES_H # include #endif #endif /* IVL_vhdlpp_config_H */ iverilog-10_1/vhdlpp/vhdlreal.cc000066400000000000000000000044761265551621300167620ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vhdlreal.h" #include #include #include using namespace std; vhdlreal::vhdlreal() { value_ = 0.0; } vhdlreal::vhdlreal(const double& r) { value_ = r; } vhdlreal::vhdlreal(const vhdlreal& val) { value_ = val.as_double(); } vhdlreal::vhdlreal(const char* text) { assert(strlen(text) != 0); char* buffer = new char[strlen(text)+1]; char* buf_ptr; for(buf_ptr = buffer; *text != 0; ++buf_ptr, ++text) { if(*text == '_') continue; *buf_ptr = *text; } *buf_ptr = '\0'; value_ = strtod(buffer, NULL); delete[] buffer; } ostream& operator<< (ostream& str, const vhdlreal& r) { return (str << r.as_double()); } vhdlreal operator+ (const vhdlreal& r1, const vhdlreal& r2) { return vhdlreal(r1.as_double() + r2.as_double()); } vhdlreal operator- (const vhdlreal& r1, const vhdlreal& r2) { return vhdlreal(r1.as_double() - r2.as_double()); } vhdlreal operator* (const vhdlreal& r1, const vhdlreal& r2) { return vhdlreal(r1.as_double() * r2.as_double()); } vhdlreal operator/ (const vhdlreal& r1, const vhdlreal& r2) { return vhdlreal(r1.as_double() / r2.as_double()); } vhdlreal operator% (const vhdlreal& r1, const vhdlreal& r2) { return vhdlreal(fmod(r1.as_double(), r2.as_double())); } vhdlreal pow(const vhdlreal& r1, const vhdlreal& r2) { return vhdlreal(pow(r1.as_double(), r2.as_double())); } vhdlreal operator- (const vhdlreal& r) { return vhdlreal(-r.as_double()); } iverilog-10_1/vhdlpp/vhdlreal.h000066400000000000000000000037451265551621300166220ustar00rootroot00000000000000#ifndef IVL_vhdlreal_h #define IVL_vhdlreal_h /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include /* * This class holds a floating point decimal number. The number is * stored as double. All based numbers are converted by an external * function to a double and then stored as class instance. */ class vhdlreal { public: friend std::ostream& operator<< (std::ostream&, const vhdlreal&); friend vhdlreal operator+ (const vhdlreal&, const vhdlreal&); friend vhdlreal operator- (const vhdlreal&, const vhdlreal&); friend vhdlreal operator* (const vhdlreal&, const vhdlreal&); friend vhdlreal operator/ (const vhdlreal&, const vhdlreal&); friend vhdlreal operator% (const vhdlreal&, const vhdlreal&); friend vhdlreal pow(const vhdlreal&, const vhdlreal&); // Unary minus. friend vhdlreal operator- (const vhdlreal&); explicit vhdlreal(); explicit vhdlreal(const char*text); explicit vhdlreal(const double& val); vhdlreal(const vhdlreal& val); virtual ~vhdlreal() {}; double as_double() const { return value_; } private: double value_; }; #endif /* IVL_vhdlreal_h */ iverilog-10_1/vhdlpp/vsignal.cc000066400000000000000000000047671265551621300166270ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * Copyright CERN 2014 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vsignal.h" # include "expression.h" # include "vtype.h" # include using namespace std; SigVarBase::SigVarBase(perm_string nam, const VType*typ, Expression*exp) : name_(nam), type_(typ), init_expr_(exp), refcnt_sequ_(0) { } SigVarBase::~SigVarBase() { } void SigVarBase::elaborate_init_expr(Entity*ent, ScopeBase*scope) { if(init_expr_) { init_expr_->elaborate_expr(ent, scope, peek_type()); } } void SigVarBase::type_elaborate_(VType::decl_t&decl) { decl.type = type_; } int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; VType::decl_t decl; type_elaborate_(decl); if (peek_refcnt_sequ_() > 0 || !peek_type()->can_be_packed()) decl.reg_flag = true; errors += decl.emit(out, peek_name()); Expression*init_expr = peek_init_expr(); if (init_expr) { out << " = "; init_expr->emit(out, ent, scope); } out << ";" << endl; return errors; } int Variable::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; VType::decl_t decl; type_elaborate_(decl); if (peek_refcnt_sequ_() > 0 || !peek_type()->can_be_packed()) decl.reg_flag = true; errors += decl.emit(out, peek_name()); Expression*init_expr = peek_init_expr(); if (init_expr) { out << " = "; init_expr->emit(out, ent, scope); } out << ";" << endl; return errors; } void Variable::write_to_stream(std::ostream&fd) { fd << "variable " << peek_name() << " : "; peek_type()->write_to_stream(fd); fd << ";" << endl; } iverilog-10_1/vhdlpp/vsignal.h000066400000000000000000000054031265551621300164550ustar00rootroot00000000000000#ifndef IVL_vsignal_H #define IVL_vsignal_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "StringHeap.h" # include "LineInfo.h" # include "vtype.h" class Architecture; class ScopeBase; class Entity; class Expression; class SigVarBase : public LineInfo { public: SigVarBase(perm_string name, const VType*type, Expression*init_expr); virtual ~SigVarBase(); const VType* peek_type(void) const { return type_; } // Call this method for each occasion where this signal is the // l-value of a sequential assignment. void count_ref_sequ(); void dump(ostream&out, int indent = 0) const; // Elaborates initializer expressions if needed. void elaborate_init_expr(Entity*ent, ScopeBase*scope); perm_string peek_name() const { return name_; } protected: unsigned peek_refcnt_sequ_() const { return refcnt_sequ_; } void type_elaborate_(VType::decl_t&decl); Expression* peek_init_expr() const { return init_expr_; } private: perm_string name_; const VType*type_; Expression*init_expr_; unsigned refcnt_sequ_; private: // Not implemented SigVarBase(const SigVarBase&); SigVarBase& operator = (const SigVarBase&); }; class Signal : public SigVarBase { public: Signal(perm_string name, const VType*type, Expression*init_expr); int emit(ostream&out, Entity*ent, ScopeBase*scope); }; class Variable : public SigVarBase { public: Variable(perm_string name, const VType*type, Expression*init_expr = NULL); int emit(ostream&out, Entity*ent, ScopeBase*scope); void write_to_stream(std::ostream&fd); }; inline void SigVarBase::count_ref_sequ() { refcnt_sequ_ += 1; } inline Signal::Signal(perm_string name, const VType*type, Expression*init_expr) : SigVarBase(name, type, init_expr) { } inline Variable::Variable(perm_string name, const VType*type, Expression*init_expr) : SigVarBase(name, type, init_expr) { } #endif /* IVL_vsignal_H */ iverilog-10_1/vhdlpp/vtype.cc000066400000000000000000000220721265551621300163200ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vtype.h" # include "parse_types.h" # include "compiler.h" # include # include # include # include using namespace std; VType::~VType() { } void VType::show(ostream&out) const { write_to_stream(out); } perm_string VType::get_generic_typename() const { char buf[16] = {0,}; snprintf(buf, 16, "type_%p", this); return lex_strings.make(buf); } VTypePrimitive::VTypePrimitive(VTypePrimitive::type_t tt, bool packed) : type_(tt), packed_(packed) { } VTypePrimitive::~VTypePrimitive() { } void VTypePrimitive::show(ostream&out) const { switch (type_) { case BIT: out << "BIT"; break; case CHARACTER: out << "CHARACTER"; break; case INTEGER: out << "INTEGER"; break; case NATURAL: out << "NATURAL"; break; case REAL: out << "REAL"; break; case STDLOGIC: out << "STD_LOGIC"; break; case TIME: out << "TIME"; break; } } int VTypePrimitive::get_width(ScopeBase*) const { switch(type_) { case BIT: case STDLOGIC: return 1; case INTEGER: case NATURAL: return 32; case CHARACTER: return 8; default: std::cerr << "sorry: primitive type " << type_ << " has no get_width() implementation." << std::endl; break; } return -1; } VTypeArray::range_t*VTypeArray::range_t::clone() const { return new VTypeArray::range_t(safe_clone(msb_), safe_clone(lsb_), direction_); } VTypeArray::VTypeArray(const VType*element, const vector&r, bool sv) : etype_(element), ranges_(r), signed_flag_(sv), parent_(NULL) { } /* * Create a VTypeArray range set from a list of parsed ranges. * FIXME: We are copying pointers from the prange_t object into the * range_t. This means that we cannot delete the prange_t object * unless we invent a way to remove the pointers from that object. So * this is a memory leak. Something to fix. */ VTypeArray::VTypeArray(const VType*element, std::list*r, bool sv) : etype_(element), ranges_(r->size()), signed_flag_(sv), parent_(NULL) { for (size_t idx = 0 ; idx < ranges_.size() ; idx += 1) { prange_t*curp = r->front(); r->pop_front(); Expression*msb = curp->msb(); Expression*lsb = curp->lsb(); bool dir = curp->is_downto(); ranges_[idx] = range_t(msb, lsb, dir); } } VTypeArray::VTypeArray(const VType*element, int msb, int lsb, bool sv) : etype_(element), ranges_(1), signed_flag_(sv), parent_(NULL) { bool down_to = msb > lsb; ranges_[0] = range_t(new ExpInteger(msb), new ExpInteger(lsb), down_to); } VTypeArray::~VTypeArray() { } VType*VTypeArray::clone() const { std::vector new_ranges; new_ranges.reserve(ranges_.size()); for(std::vector::const_iterator it = ranges_.begin(); it != ranges_.end(); ++it) { new_ranges.push_back(*(it->clone())); } VTypeArray*a = new VTypeArray(etype_->clone(), new_ranges, signed_flag_); a->set_parent_type(parent_); return a; } const VType* VTypeArray::basic_type(bool typedef_allowed) const { const VType*t = etype_; const VTypeDef*tdef = NULL; bool progress = false; do { progress = false; if((tdef = dynamic_cast(t))) { t = tdef->peek_definition(); } if(const VTypeArray*arr = dynamic_cast(t)) { t = arr->element_type(); progress = true; } else if(tdef) { // return the typedef if it does not define an array t = typedef_allowed ? tdef : tdef->peek_definition(); } } while(progress); return t; } void VTypeArray::show(ostream&out) const { out << "array "; for (vector::const_iterator cur = ranges_.begin() ; cur != ranges_.end() ; ++cur) { out << "("; if (cur->msb()) cur->msb()->write_to_stream(out); else out << "<>"; out << " downto "; if (cur->lsb()) cur->lsb()->write_to_stream(out); else out << "<>"; out << ")"; } out << " of "; if (signed_flag_) out << "signed "; if (etype_) etype_->show(out); else out << ""; } int VTypeArray::get_width(ScopeBase*scope) const { int64_t size = 1; for(vector::const_iterator it = ranges_.begin(); it != ranges_.end(); ++it) { const VTypeArray::range_t&dim = *it; int64_t msb_val, lsb_val; if(dim.is_box()) return -1; if(!dim.msb()->evaluate(scope, msb_val)) return -1; if(!dim.lsb()->evaluate(scope, lsb_val)) return -1; size *= 1 + labs(msb_val - lsb_val); } return element_type()->get_width(scope) * size; } bool VTypeArray::is_unbounded() const { for(std::vector::const_iterator it = ranges_.begin(); it != ranges_.end(); ++it) { if(it->is_box()) return true; } return etype_->is_unbounded(); } bool VTypeArray::is_variable_length(ScopeBase*scope) const { int64_t dummy; if(is_unbounded()) return true; for(std::vector::const_iterator it = ranges_.begin(); it != ranges_.end(); ++it) { if(!it->lsb()->evaluate(scope, dummy)) return true; if(!it->msb()->evaluate(scope, dummy)) return true; } return etype_->is_variable_length(scope); } void VTypeArray::evaluate_ranges(ScopeBase*scope) { for(std::vector::iterator it = ranges_.begin(); it != ranges_.end(); ++it ) { int64_t lsb_val = -1, msb_val = -1; if(it->msb()->evaluate(scope, msb_val) && it->lsb()->evaluate(scope, lsb_val)) { assert(lsb_val >= 0); assert(msb_val >= 0); *it = range_t(new ExpInteger(msb_val), new ExpInteger(lsb_val), msb_val > lsb_val); } } } VTypeRange::VTypeRange(const VType*base, int64_t end_val, int64_t start_val) : base_(base), end_(end_val), start_(start_val) { } VTypeRange::~VTypeRange() { } VTypeEnum::VTypeEnum(const std::list*names) : names_(names->size()) { size_t idx = 0; for (list::const_iterator cur = names->begin() ; cur != names->end() ; ++cur, ++idx) { names_[idx] = *cur; } } VTypeEnum::~VTypeEnum() { } void VTypeEnum::show(ostream&out) const { out << "("; if (names_.size() >= 1) out << names_[0]; for (size_t idx = 1 ; idx < names_.size() ; idx += 1) out << ", " << names_[idx]; out << ")"; } bool VTypeEnum::has_name(perm_string name) const { return std::find(names_.begin(), names_.end(), name) != names_.end(); } VTypeRecord::VTypeRecord(std::list*elements) : elements_(elements->size()) { for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) { elements_[idx] = elements->front(); elements->pop_front(); } delete elements; } VTypeRecord::~VTypeRecord() { for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) delete elements_[idx]; } void VTypeRecord::show(ostream&out) const { write_to_stream(out); } int VTypeRecord::get_width(ScopeBase*scope) const { int width = 0; for(vector::const_iterator it = elements_.begin(); it != elements_.end(); ++it) { int w = (*it)->peek_type()->get_width(scope); if(w < 0) return -1; width += w; } return width; } const VTypeRecord::element_t* VTypeRecord::element_by_name(perm_string name, int*index) const { for (vector::const_iterator cur = elements_.begin() ; cur != elements_.end() ; ++cur) { element_t*curp = *cur; if (curp->peek_name() == name) { if (index) *index = std::distance(elements_.begin(), cur); return curp; } } return 0; } VTypeRecord::element_t::element_t(perm_string name, const VType*typ) : name_(name), type_(typ) { } VTypeDef::VTypeDef(perm_string nam) : name_(nam), type_(0) { } VTypeDef::VTypeDef(perm_string nam, const VType*typ) : name_(nam), type_(typ) { } VTypeDef::~VTypeDef() { } void VTypeDef::set_definition(const VType*typ) { assert(type_ == 0); type_ = typ; } iverilog-10_1/vhdlpp/vtype.h000066400000000000000000000302251265551621300161610ustar00rootroot00000000000000#ifndef IVL_vtype_H #define IVL_vtype_H /* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * Copyright CERN 2014 / Stephen Williams (steve@icarus.com), * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include # include # include # include # include # include "StringHeap.h" class Architecture; class ScopeBase; class Entity; class Expression; class prange_t; class VTypeDef; class ScopeBase; typedef enum typedef_topo_e { NONE=0, PENDING, MARKED } typedef_topo_t; typedef std::map typedef_context_t; /* * A description of a VHDL type consists of a graph of VType * objects. Derived types are specific kinds of types, and those that * are compound may in turn reference other types. */ class VType { public: VType() { } virtual ~VType() =0; virtual VType*clone() const =0; // This is rarely used, but some types may have expressions // that need to be elaborated. virtual int elaborate(Entity*end, ScopeBase*scope) const; // This virtual method returns true if that is equivalent to // this type. This method is used for example to compare // function prototypes. virtual bool type_match(const VType*that) const; // This virtual method writes a VHDL-accurate representation // of this type to the designated stream. This is used for // writing parsed types to library files. virtual void write_to_stream(std::ostream&fd) const; // This is like the above, but is the root function called // directly after the "type is..." when writing type // definitions. Most types accept the default definition of this. virtual void write_type_to_stream(std::ostream&fd) const; // This virtual method writes a human-readable version of the // type to a given file for debug purposes. (Question: is this // really necessary given the write_to_stream method?) virtual void show(std::ostream&) const; // This virtual method emits a definition for the specific // type. It is used to emit typedef's. virtual int emit_def(std::ostream&out, perm_string name) const =0; // This virtual method causes VTypeDef types to emit typedefs // of themselves. The VTypeDef implementation of this method // uses this method recursively to do a depth-first emit of // all the types that it emits. virtual int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; // Determines if a type can be used in Verilog packed array. virtual bool can_be_packed() const { return false; } // Returns true if the type has an undefined dimension. virtual bool is_unbounded() const { return false; } // Checks if the variable length is dependent on other expressions, that // cannot be evaluated (e.g. 'length, 'left, 'right). virtual bool is_variable_length(ScopeBase*) const { return false; } // Returns a perm_string that can be used in automatically created // typedefs (i.e. not ones defined by the user). perm_string get_generic_typename() const; // Returns the type width in bits or negative number if it is impossible // to evaluate. virtual int get_width(ScopeBase*) const { return -1; } private: friend struct decl_t; // This virtual method is called to emit the declaration. This // is used by the decl_t object to emit variable/wire/port declarations. virtual int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; public: // A couple places use the VType along with a few // per-declaration details, so provide a common structure for // holding that stuff together. struct decl_t { decl_t() : type(0), reg_flag(false) { } int emit(std::ostream&out, perm_string name) const; const VType*type; bool reg_flag; }; protected: inline void emit_name(std::ostream&out, perm_string name) const { if(name != empty_perm_string) out << " \\" << name << " "; } }; inline std::ostream&operator << (std::ostream&out, const VType&item) { item.show(out); return out; } extern void preload_global_types(void); /* * This type is a placeholder for ERROR types. */ class VTypeERROR : public VType { VType*clone() const { return NULL; } public: int emit_def(std::ostream&out, perm_string name) const; }; /* * This class represents the primitive types that are available to the * type subsystem. */ class VTypePrimitive : public VType { public: enum type_t { BIT, INTEGER, NATURAL, REAL, STDLOGIC, CHARACTER, TIME }; public: VTypePrimitive(type_t tt, bool packed = false); ~VTypePrimitive(); VType*clone() const { return new VTypePrimitive(*this); } void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; type_t type() const { return type_; } int emit_primitive_type(std::ostream&fd) const; int emit_def(std::ostream&out, perm_string name) const; bool can_be_packed() const { return packed_; } private: type_t type_; bool packed_; }; /* * An array is a compound N-dimensional array of element type. The * construction of the array is from an element type and a vector of * ranges. The array type can be left incomplete by leaving some * ranges as "box" ranges, meaning present but not defined. */ class VTypeArray : public VType { public: class range_t { public: range_t(Expression*m = NULL, Expression*l = NULL, bool down_to = true) : msb_(m), lsb_(l), direction_(down_to) { } range_t*clone() const; inline bool is_box() const { return msb_==0 && lsb_==0; } inline bool is_downto() const { return direction_; } inline Expression* msb() const { return msb_; } inline Expression* lsb() const { return lsb_; } private: Expression* msb_; Expression* lsb_; bool direction_; }; public: VTypeArray(const VType*etype, const std::vector&r, bool signed_vector = false); VTypeArray(const VType*etype, std::list*r, bool signed_vector = false); VTypeArray(const VType*etype, int msb, int lsb, bool signed_vector = false); ~VTypeArray(); VType*clone() const; int elaborate(Entity*ent, ScopeBase*scope) const; void write_to_stream(std::ostream&fd) const; void write_type_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; inline size_t dimensions() const { return ranges_.size(); }; const range_t&dimension(size_t idx) const { return ranges_[idx]; } inline bool signed_vector() const { return signed_flag_; } // returns the type of element held in the array inline const VType* element_type() const { return parent_ ? parent_->element_type() : etype_; } // returns the basic type of element held in the array // (unfolds typedefs and multidimensional arrays) // typedef_allowed decides if VTypeDef can be returned or should // it be unfolded const VType* basic_type(bool typedef_allowed = true) const; int emit_def(std::ostream&out, perm_string name) const; int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; bool can_be_packed() const { return etype_->can_be_packed(); } bool is_unbounded() const; bool is_variable_length(ScopeBase*scope) const; // To handle subtypes inline void set_parent_type(const VTypeArray*parent) { parent_ = parent; } // Wherever it is possible, replaces range lsb & msb expressions with // constant integers. void evaluate_ranges(ScopeBase*scope); private: int emit_with_dims_(std::ostream&out, bool packed, perm_string name) const; // Handles a few special types of array (*_vector, string types). bool write_special_case(std::ostream&out) const; void write_range_to_stream_(std::ostream&fd) const; const VType*etype_; std::vector ranges_; bool signed_flag_; const VTypeArray*parent_; }; class VTypeRange : public VType { public: VTypeRange(const VType*base, int64_t end, int64_t start); ~VTypeRange(); VType*clone() const { return new VTypeRange(base_->clone(), start_, end_); } // Get the type that is limited by the range. inline const VType* base_type() const { return base_; } public: // Virtual methods void write_to_stream(std::ostream&fd) const; int emit_def(std::ostream&out, perm_string name) const; private: const VType*base_; int64_t end_, start_; }; class VTypeEnum : public VType { public: VTypeEnum(const std::list*names); ~VTypeEnum(); VType*clone() const { return new VTypeEnum(*this); } void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*) const { return 32; } int emit_def(std::ostream&out, perm_string name) const; // Checks if the name is stored in the enum. bool has_name(perm_string name) const; private: std::vectornames_; }; class VTypeRecord : public VType { public: class element_t { public: element_t(perm_string name, const VType*type); void write_to_stream(std::ostream&) const; inline perm_string peek_name() const { return name_; } inline const VType* peek_type() const { return type_; } private: perm_string name_; const VType*type_; private:// Not implement element_t(const element_t&); element_t& operator= (const element_t); }; public: explicit VTypeRecord(std::list*elements); ~VTypeRecord(); VType*clone() const { return new VTypeRecord(*this); } void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; int emit_def(std::ostream&out, perm_string name) const; bool can_be_packed() const { return true; } const element_t* element_by_name(perm_string name, int*index = NULL) const; inline const std::vector get_elements() const { return elements_; } private: std::vector elements_; }; class VTypeDef : public VType { public: explicit VTypeDef(perm_string name); explicit VTypeDef(perm_string name, const VType*is); ~VTypeDef(); VType*clone() const { return new VTypeDef(*this); } bool type_match(const VType*that) const; inline perm_string peek_name() const { return name_; } // If the type is not given a definition in the constructor, // then this must be used to set the definition later. void set_definition(const VType*is); // In some situations, we only need the definition of the // type, and this method gets it for us. inline const VType* peek_definition(void) const { return type_; } void write_to_stream(std::ostream&fd) const; void write_type_to_stream(std::ostream&fd) const; int get_width(ScopeBase*scope) const { return type_->get_width(scope); } int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; int emit_def(std::ostream&out, perm_string name) const; bool can_be_packed() const { return type_->can_be_packed(); } bool is_unbounded() const { return type_->is_unbounded(); } private: int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; private: perm_string name_; const VType*type_; }; #endif /* IVL_vtype_H */ iverilog-10_1/vhdlpp/vtype_elaborate.cc000066400000000000000000000026711265551621300203410ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vtype.h" # include "expression.h" int VType::elaborate(Entity*, ScopeBase*) const { return 0; } int VTypeArray::elaborate(Entity*ent, ScopeBase*scope) const { int errors = 0; etype_->elaborate(ent, scope); for (vector::const_iterator cur = ranges_.begin() ; cur != ranges_.end() ; ++ cur) { Expression*tmp = cur->msb(); if (tmp) errors += tmp->elaborate_expr(ent, scope, 0); tmp = cur->lsb(); if (tmp) errors += tmp->elaborate_expr(ent, scope, 0); } return errors; } iverilog-10_1/vhdlpp/vtype_emit.cc000066400000000000000000000155141265551621300173410ustar00rootroot00000000000000/* * Copyright (c) 2011-2012 Stephen Williams (steve@icarus.com) * Copyright (c) 2014 CERN * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vtype.h" # include "expression.h" # include # include # include using namespace std; int VType::decl_t::emit(ostream&out, perm_string name) const { return type->emit_decl(out, name, reg_flag); } int VType::emit_decl(ostream&out, perm_string name, bool reg_flag) const { int errors = 0; if (!reg_flag) out << "wire "; errors += emit_def(out, name); out << " "; return errors; } int VType::emit_typedef(std::ostream&, typedef_context_t&) const { return 0; } int VTypeERROR::emit_def(ostream&out, perm_string) const { out << "/* ERROR */"; return 1; } int VTypeArray::emit_def(ostream&out, perm_string name) const { int errors = 0; const VType*raw_base = basic_type(); const VTypePrimitive*base = dynamic_cast (raw_base); if (base) { assert(dimensions() == 1); base->emit_def(out, empty_perm_string); if (signed_flag_) out << " signed"; } else { raw_base->emit_def(out, empty_perm_string); } errors += emit_with_dims_(out, raw_base->can_be_packed(), name); return errors; } int VTypeArray::emit_typedef(std::ostream&out, typedef_context_t&ctx) const { return etype_->emit_typedef(out, ctx); } int VTypeArray::emit_with_dims_(std::ostream&out, bool packed, perm_string name) const { int errors = 0; list dims; const VTypeArray*cur = this; while (const VTypeArray*sub = dynamic_cast (cur->element_type())) { dims.push_back(cur); cur = sub; } dims.push_back(cur); bool name_emitted = false; while (! dims.empty()) { cur = dims.front(); dims.pop_front(); if(!packed) { emit_name(out, name); name_emitted = true; } for(unsigned i = 0; i < cur->dimensions(); ++i) { if(cur->dimension(i).is_box() && !name_emitted) { emit_name(out, name); name_emitted = true; } out << "["; if (!cur->dimension(i).is_box()) { // if not unbounded { errors += cur->dimension(i).msb()->emit(out, 0, 0); out << ":"; errors += cur->dimension(i).lsb()->emit(out, 0, 0); } out << "]"; } } if(!name_emitted) { emit_name(out, name); } return errors; } int VTypeEnum::emit_def(ostream&out, perm_string name) const { int errors = 0; out << "enum integer {"; assert(names_.size() >= 1); out << "\\" << names_[0] << " "; for (size_t idx = 1 ; idx < names_.size() ; idx += 1) out << ", \\" << names_[idx] << " "; out << "}"; emit_name(out, name); return errors; } int VTypePrimitive::emit_primitive_type(ostream&out) const { int errors = 0; switch (type_) { case BIT: out << "bit"; break; case STDLOGIC: out << "logic"; break; case NATURAL: out << "int unsigned"; break; case INTEGER: out << "int"; break; case REAL: out << "real"; break; case CHARACTER: out << "byte"; break; case TIME: out << "time"; break; default: assert(0); break; } return errors; } int VTypePrimitive::emit_def(ostream&out, perm_string name) const { int errors = 0; errors += emit_primitive_type(out); emit_name(out, name); return errors; } int VTypeRange::emit_def(ostream&out, perm_string name) const { int errors = 0; out << "/* Internal error: Don't know how to emit range */"; errors += base_->emit_def(out, name); return errors; } int VTypeRecord::emit_def(ostream&out, perm_string name) const { int errors = 0; out << "struct packed {"; for (vector::const_iterator cur = elements_.begin() ; cur != elements_.end() ; ++cur) { perm_string element_name = (*cur)->peek_name(); const VType*element_type = (*cur)->peek_type(); element_type->emit_def(out, empty_perm_string); out << " \\" << element_name << " ; "; } out << "}"; emit_name(out, name); return errors; } /* * For VTypeDef objects, use the name of the defined type as the * type. (We are defining a variable here, not the type itself.) The * emit_typedef() method was presumably called to define type already. */ int VTypeDef::emit_def(ostream&out, perm_string name) const { int errors = 0; emit_name(out, name_); emit_name(out, name); return errors; } int VTypeDef::emit_decl(ostream&out, perm_string name, bool reg_flag) const { int errors = 0; if (!dynamic_cast(type_)) out << (reg_flag ? "reg " : "wire "); if(dynamic_cast(type_)) { errors += type_->emit_def(out, name); } else { assert(name_ != empty_perm_string); cout << "\\" << name_; emit_name(out, name); } return errors; } int VTypeDef::emit_typedef(ostream&out, typedef_context_t&ctx) const { // The typedef_context_t is used by me to determine if this // typedef has already been emitted in this architecture. If // it has, then it is MARKED, give up. Otherwise, recurse the // emit_typedef to make sure all sub-types that I use have // been emitted, then emit my typedef. typedef_topo_t&flag = ctx[this]; switch (flag) { case MARKED: return 0; case PENDING: out << "typedef \\" << name_ << " ; /* typedef cycle? */" << endl; return 0; case NONE: break; } flag = PENDING; int errors = type_->emit_typedef(out, ctx); flag = MARKED; // Array types are used directly anyway and typedefs for unpacked // arrays do not work currently if(dynamic_cast(type_)) out << "// "; out << "typedef "; errors += type_->emit_def(out, name_); out << " ;" << endl; return errors; } iverilog-10_1/vhdlpp/vtype_match.cc000066400000000000000000000025171265551621300174760ustar00rootroot00000000000000/* * Copyright (c) 2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vtype.h" bool VType::type_match(const VType*that) const { if(this == that) return true; if(const VTypeDef*tdef = dynamic_cast(that)) { if(type_match(tdef->peek_definition())) return true; } return false; } bool VTypeDef::type_match(const VType*that) const { if(VType::type_match(that)) return true; return VType::type_match(type_); } iverilog-10_1/vhdlpp/vtype_stream.cc000066400000000000000000000121271265551621300176730ustar00rootroot00000000000000/* * Copyright (c) 2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # define __STDC_LIMIT_MACROS # include "std_types.h" # include "expression.h" # include # include # include using namespace std; void VType::write_to_stream(ostream&fd) const { fd << "/* UNKNOWN TYPE: " << typeid(*this).name() << " */"; } void VType::write_type_to_stream(ostream&fd) const { write_to_stream(fd); } void VTypeArray::write_to_stream(ostream&fd) const { if(write_special_case(fd)) return; bool typedefed = false; if(const VTypeDef*tdef = dynamic_cast(etype_)) { tdef->write_to_stream(fd); typedefed = true; } else { fd << "array "; } if (! ranges_.empty()) { assert(ranges_.size() < 2); if (ranges_[0].is_box()) { fd << "(INTEGER range <>) "; } else { write_range_to_stream_(fd); } } if(!typedefed) { fd << "of "; etype_->write_to_stream(fd); } } void VTypeArray::write_range_to_stream_(std::ostream&fd) const { assert(ranges_.size() < 2); assert(ranges_[0].msb() && ranges_[0].lsb()); fd << "("; if (ranges_[0].msb()) ranges_[0].msb()->write_to_stream(fd); else fd << "<>"; fd << (ranges_[0].is_downto() ? " downto " : " to "); if (ranges_[0].lsb()) ranges_[0].lsb()->write_to_stream(fd); else fd << "<>"; fd << ") "; } bool VTypeArray::write_special_case(std::ostream&fd) const { if(this == &primitive_SIGNED) { fd << "signed"; } else if(this == &primitive_UNSIGNED) { fd << "unsigned"; } else if(etype_ == &primitive_STDLOGIC) { fd << "std_logic_vector"; } else if(etype_ == &primitive_BIT) { fd << "bit_vector"; } else if(etype_ == &primitive_CHARACTER) { fd << "string"; } else { return false; } if(!ranges_.empty() && !ranges_[0].is_box()) { write_range_to_stream_(fd); } return true; } void VTypeArray::write_type_to_stream(ostream&fd) const { if(write_special_case(fd)) return; fd << "array "; // Unbounded array if (! ranges_.empty()) { assert(ranges_.size() < 2); if (ranges_[0].is_box()) { fd << "(INTEGER range <>) "; } else { write_range_to_stream_(fd); } } fd << "of "; if(const VTypeDef*tdef = dynamic_cast(etype_)) { tdef->write_to_stream(fd); } else { etype_->write_to_stream(fd); } } void VTypeDef::write_type_to_stream(ostream&fd) const { type_->write_type_to_stream(fd); } void VTypeDef::write_to_stream(ostream&fd) const { fd << name_; } void VTypePrimitive::write_to_stream(ostream&fd) const { switch (type_) { case BIT: fd << "bit"; break; case INTEGER: fd << "integer"; break; case NATURAL: fd << "natural"; break; case REAL: fd << "real"; break; case STDLOGIC: fd << "std_logic"; break; case CHARACTER: fd << "character"; break; case TIME: fd << "time"; break; default: assert(0); fd << "/* PRIMITIVE: " << type_ << " */"; break; } } void VTypeRange::write_to_stream(ostream&fd) const { // Detect some special cases that can be written as ieee or // standard types. if (const VTypePrimitive*tmp = dynamic_cast (base_)) { if (tmp->type()==VTypePrimitive::NATURAL) { fd << "natural"; return; } } base_->write_to_stream(fd); fd << " range " << start_; fd << (start_ < end_ ? " to " : " downto "); fd << end_; } void VTypeRecord::write_to_stream(ostream&fd) const { fd << "record "; for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) { elements_[idx]->write_to_stream(fd); fd << "; "; } fd << "end record"; } void VTypeRecord::element_t::write_to_stream(ostream&fd) const { fd << name_ << ": "; type_->write_to_stream(fd); } void VTypeEnum::write_to_stream(std::ostream&fd) const { fd << "("; for (vector::const_iterator it = names_.begin(); it != names_.end(); ++it) { if(it != names_.begin()) fd << ","; fd << *it; } fd << ")"; } iverilog-10_1/vpi.txt000066400000000000000000000032501265551621300147010ustar00rootroot00000000000000 HOW IT WORKS The VPI interface for Icarus Verilog works by creating from a collection of PLI applications a single vpi module. The vpi module includes compiled code for the applications linked together (with any other libraries that the applications need) into a module with a single exported symbol, the vlog_startup_routines array. The product that wishes to invoke the module (normally at run time) loads the module, locates the vlog_startup_routines table, and calls all the startup routines contained in that table. It is possible for a product to link with many modules. In that case, all the modules are linked in and startup routines are called in order. The product that uses vpi modules uses the environment variable VPI_MODULE_PATH as a ':' separated list of directories. This is the module search path. When a module is specified by name (using whatever means the product supports) the module search path is scanned until the module is located. The special module name "system.vpi" is part of the core Icarus Verilog distribution and includes implementations of the standard system tasks/functions. COMPILING A VPI MODULE See the iverilog-vpi documentation. TRACING VPI USE The vvp command includes the ability to trace VPI calls. This is useful if you are trying to debug a problem with your code. To activate tracing simply set the VPI_TRACE environment variable, with the path to a file where trace text gets written. For example: setenv VPI_TRACE /tmp/foo.txt This tracing is pretty verbose, so you don't want to run like this normally. Also, the format of the tracing messages will change according to my needs (and whim) so don't expect to be able to parse it in software. iverilog-10_1/vpi/000077500000000000000000000000001265551621300141405ustar00rootroot00000000000000iverilog-10_1/vpi/Makefile.in000066400000000000000000000162551265551621300162160ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ includedir = $(prefix)/include vpidir = $(libdir)/ivl$(suffix) CC = @CC@ CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ YACC = @YACC@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @file64_support@ @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ # Object files for system.vpi O = sys_table.o sys_convert.o sys_countdrivers.o sys_darray.o sys_deposit.o sys_display.o \ sys_fileio.o sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o \ sys_random.o sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o \ sys_sdf.o sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o \ sys_priv.o sdf_parse.o sdf_lexor.o stringheap.o vams_simparam.o \ table_mod.o table_mod_parse.o table_mod_lexor.o OPP = vcd_priv2.o ifeq (@HAVE_LIBZ@,yes) ifeq (@HAVE_LIBBZ2@,yes) O += sys_lxt.o lxt_write.o endif O += sys_lxt2.o lxt2_write.o O += sys_fst.o fstapi.o fastlz.o lz4.o endif # Object files for v2005_math.vpi M = sys_clog2.o v2005_math.o # Object files for va_math.vpi V = va_math.o V2009 = v2009_table.o v2009_array.o v2009_enum.o v2009_string.o VHDL_SYS = vhdl_table.o VPI_DEBUG = vpi_debug.o all: dep system.vpi va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vpi_debug.vpi $(ALL32) check: all clean: rm -rf *.o sys_readmem_lex.c dep system.vpi rm -f sdf_lexor.c sdf_parse.c sdf_parse.output sdf_parse.h rm -f table_mod_parse.c table_mod_parse.h table_mod_parse.output rm -f table_mod_lexor.c rm -f va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vpi_debug.vpi distclean: clean rm -f Makefile config.log rm -f vpi_config.h stamp-vpi_config-h # The -U flag is used to skip checking paths that depend on that define having # an explicit value (i.e. the define is expected to be real code). cppcheck: $(O:.o=.c) $(OPP:.o=.cc) $(M:.o=.c) $(V:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=vpi/$@ dep: mkdir dep %.o: %.c vpi_config.h $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep %.o: %.cc vpi_config.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep LIBS = @LIBS@ SYSTEM_VPI_LDFLAGS = $(LIBS) VA_MATH_LDFLAGS = ifeq (@MINGW32@,yes) SYSTEM_VPI_LDFLAGS += @EXTRALIBS@ VA_MATH_LDFLAGS += @EXTRALIBS@ endif system.vpi: $O $(OPP) ../vvp/libvpi.a $(CXX) @shared@ -o $@ $O $(OPP) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) sys_readmem_lex.c: $(srcdir)/sys_readmem_lex.lex $(LEX) -t $< > $@ sdf_lexor.o: sdf_lexor.c sdf_parse.h sdf_lexor.c: $(srcdir)/sdf_lexor.lex $(LEX) -t $< > $@ # Build this in two steps to avoid parallel build issues (see pr3462585) sdf_parse.c: $(srcdir)/sdf_parse.y $(YACC) --verbose -t -p sdf -d -o $@ $< sdf_parse.h: sdf_parse.c table_mod_lexor.o: table_mod_lexor.c table_mod_parse.h table_mod_lexor.c: $(srcdir)/table_mod_lexor.lex $(LEX) -t $< > $@ # Build this in two steps to avoid parallel build issues (see pr3462585) table_mod_parse.c: $(srcdir)/table_mod_parse.y $(YACC) --verbose -t -p tblmod -d -o $@ $< table_mod_parse.h: table_mod_parse.c v2005_math.vpi: $M ../vvp/libvpi.a $(CC) @shared@ -o $@ $M -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) v2009.vpi: $(V2009) ../vvp/libvpi.a $(CC) @shared@ -o $@ $(V2009) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) va_math.vpi: $V ../vvp/libvpi.a $(CC) @shared@ -o $@ $V -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) vhdl_sys.vpi: $(VHDL_SYS) ../vvp/libvpi.a $(CC) @shared@ -o $@ $(VHDL_SYS) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) vpi_debug.vpi: $(VPI_DEBUG) ../vvp/libvpi.a $(CC) @shared@ -o $@ $(VPI_DEBUG) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) stamp-vpi_config-h: $(srcdir)/vpi_config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=vpi/vpi_config.h vpi_config.h: stamp-vpi_config-h install: all installdirs \ $(vpidir)/system.vpi $(vpidir)/system.sft \ $(vpidir)/va_math.vpi $(vpidir)/va_math.sft \ $(vpidir)/v2005_math.vpi $(vpidir)/v2005_math.sft \ $(vpidir)/v2009.vpi $(vpidir)/v2009.sft \ $(vpidir)/vhdl_sys.vpi $(vpidir)/vhdl_sys.sft \ $(vpidir)/vpi_debug.vpi $(vpidir)/system.vpi: ./system.vpi $(INSTALL_PROGRAM) ./system.vpi "$(DESTDIR)$(vpidir)/system.vpi" $(vpidir)/system.sft: system.sft $(INSTALL_DATA) $< "$(DESTDIR)$@" $(vpidir)/va_math.vpi: ./va_math.vpi $(INSTALL_PROGRAM) ./va_math.vpi "$(DESTDIR)$(vpidir)/va_math.vpi" $(vpidir)/va_math.sft: va_math.sft $(INSTALL_DATA) $< "$(DESTDIR)$@" $(vpidir)/v2005_math.vpi: ./v2005_math.vpi $(INSTALL_PROGRAM) ./v2005_math.vpi "$(DESTDIR)$(vpidir)/v2005_math.vpi" $(vpidir)/v2005_math.sft: v2005_math.sft $(INSTALL_DATA) $< "$(DESTDIR)$@" $(vpidir)/v2009.vpi: ./v2009.vpi $(INSTALL_PROGRAM) ./v2009.vpi "$(DESTDIR)$(vpidir)/v2009.vpi" $(vpidir)/v2009.sft: v2009.sft $(INSTALL_DATA) $< "$(DESTDIR)$@" $(vpidir)/vhdl_sys.vpi: ./vhdl_sys.vpi $(INSTALL_PROGRAM) ./vhdl_sys.vpi "$(DESTDIR)$(vpidir)/vhdl_sys.vpi" $(vpidir)/vhdl_sys.sft: vhdl_sys.sft $(INSTALL_DATA) $< "$(DESTDIR)$@" $(vpidir)/vpi_debug.vpi: ./vpi_debug.vpi $(INSTALL_PROGRAM) ./vpi_debug.vpi "$(DESTDIR)$(vpidir)/vpi_debug.vpi" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)" "$(DESTDIR)$(vpidir)" uninstall: rm -f "$(DESTDIR)$(vpidir)/system.vpi" rm -f "$(DESTDIR)$(vpidir)/system.sft" rm -f "$(DESTDIR)$(vpidir)/va_math.vpi" rm -f "$(DESTDIR)$(vpidir)/va_math.sft" rm -f "$(DESTDIR)$(vpidir)/v2005_math.vpi" rm -f "$(DESTDIR)$(vpidir)/v2005_math.sft" rm -f "$(DESTDIR)$(vpidir)/v2009.vpi" rm -f "$(DESTDIR)$(vpidir)/v2009.sft" rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.vpi" rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.sft" rm -f "$(DESTDIR)$(vpidir)/vpi_debug.vpi" -include $(patsubst %.o, dep/%.d, $O) -include $(patsubst %.o, dep/%.d, $(OPP)) -include $(patsubst %.o, dep/%.d, $M) -include $(patsubst %.o, dep/%.d, $V) iverilog-10_1/vpi/cppcheck.sup000066400000000000000000000212101265551621300164450ustar00rootroot00000000000000// The following three files are copied directly from GTKWave and we do not // have control over them. Tony has a specific programming style so these // problems will not be fixed. // fstapi.c from GTKWave obsoleteFunctionsasctime:fstapi.c:930 obsoleteFunctionsalloca:fstapi.c:2305 unreadVariable:fstapi.c:1648 variableScope:fstapi.c:666 variableScope:fstapi.c:1338 variableScope:fstapi.c:1339 variableScope:fstapi.c:1396 variableScope:fstapi.c:1979 variableScope:fstapi.c:2108 variableScope:fstapi.c:2541 variableScope:fstapi.c:2542 variableScope:fstapi.c:2733 variableScope:fstapi.c:2734 variableScope:fstapi.c:2738 variableScope:fstapi.c:2854 variableScope:fstapi.c:2893 variableScope:fstapi.c:2894 variableScope:fstapi.c:3612 variableScope:fstapi.c:3786 variableScope:fstapi.c:3788 variableScope:fstapi.c:4230 variableScope:fstapi.c:4231 variableScope:fstapi.c:4240 variableScope:fstapi.c:4503 variableScope:fstapi.c:4774 variableScope:fstapi.c:4777 variableScope:fstapi.c:5271 variableScope:fstapi.c:5275 variableScope:fstapi.c:5276 variableScope:fstapi.c:5432 variableScope:fstapi.c:5490 variableScope:fstapi.c:5803 variableScope:fstapi.c:5806 variableScope:fstapi.c:6042 variableScope:fstapi.c:6147 variableScope:fstapi.c:6148 variableScope:fstapi.c:6179 variableScope:fstapi.c:6407 // These functions are not used by Icarus // fstReaderClrFacProcessMask() unusedFunction:fstapi.c:3344 // fstReaderClrFacProcessMaskAll() unusedFunction:fstapi.c:3373 // fstReaderGetAliasCount() unusedFunction:fstapi.c:3436 // fstReaderGetCurrentFlatScope() unusedFunction:fstapi.c:3182 // fstReaderGetAliasCount() unusedFunction:fstapi.c:3279 // fstReaderGetCurrentScopeUserInfo() unusedFunction:fstapi.c:3196 // fstReaderGetDateString() unusedFunction:fstapi.c:3464 // fstReaderGetDoubleEndianMatchState() unusedFunction:fstapi.c:3450 // fstReaderGetDumpActivityChangeTime() unusedFunction:fstapi.c:3492 // fstReaderGetDumpActivityChangeValue() unusedFunction:fstapi.c:3507 // fstReaderGetEndTime() unusedFunction:fstapi.c:3401 // fstReaderGetFacProcessMask() unusedFunction:fstapi.c:3307 // fstReaderGetFileType() unusedFunction:fstapi.c:3471 // fstReaderGetFseekFailed() unusedFunction:fstapi.c:3292 // fstReaderGetMaxHandle() unusedFunction:fstapi.c:3429 // fstReaderGetMemoryUsedByWriter() unusedFunction:fstapi.c:3408 // fstReaderGetNumberDumpActivityChanges() unusedFunction:fstapi.c:3485 // fstReaderGetScopeCount() unusedFunction:fstapi.c:3415 // fstReaderGetStartTime() unusedFunction:fstapi.c:3394 // fstReaderGetTimescale() unusedFunction:fstapi.c:3387 // fstReaderGetTimezero() unusedFunction:fstapi.c:3478 // fstReaderGetValueChangeSectionCount() unusedFunction:fstapi.c:3443 // fstReaderGetValueFromHandleAtTime() unusedFunction:fstapi.c:5685 // fstReaderGetVarCount() unusedFunction:fstapi.c:3422 // fstReaderGetVersionString() unusedFunction:fstapi.c:3457 // fstReaderIterBlocks() unusedFunction:fstapi.c:4652 // fstReaderIterBlocksSetNativeDoublesOnCallback() unusedFunction:fstapi.c:3557 // fstReaderIterateHier() unusedFunction:fstapi.c:3782 // fstReaderIterateHierRewind() unusedFunction:fstapi.c:3762 // fstReaderOpen() unusedFunction:fstapi.c:4550 // fstReaderOpenForUtilitiesOnly() unusedFunction:fstapi.c:4542 // fstReaderPushScope() unusedFunction:fstapi.c:3244 // fstReaderResetScope() unusedFunction:fstapi.c:3233 // fstReaderSetFacProcessMask() unusedFunction:fstapi.c:3326 // fstReaderSetFacProcessMaskAll() unusedFunction:fstapi.c:3362 // fstReaderSetLimitTimeRange() unusedFunction:fstapi.c:3522 // fstReaderSetUnlimitedTimeRange() unusedFunction:fstapi.c:3535 // fstReaderSetVcdExtensions() unusedFunction:fstapi.c:3546 // fstUtilityEscToBin() unusedFunction:fstapi.c:6485 // fstWriterCreateVar2() unusedFunction:fstapi.c:2528 // fstWriterEmitVariableLengthValueChange() unusedFunction:fstapi.c:2847 // fstWriterGetFseekFailed() unusedFunction:fstapi.c:2511 // fstWriterSetAttrEnd() unusedFunction:fstapi.c:2714 // fstWriterSetComment() unusedFunction:fstapi.c:2360 // fstWriterSetEnvVar() unusedFunction:fstapi.c:2372 // fstWriterSetFileType() unusedFunction:fstapi.c:2235 // fstWriterSetParallelMode() unusedFunction:fstapi.c:2471 // fstWriterSetTimezero() unusedFunction:fstapi.c:2436 // fstWriterSetValueList() unusedFunction:fstapi.c:2366 // These functions are not used by Icarus //unusedFunction:fstapi.c:226 // lxt2_write.c from GTKWave obsoleteFunctionsalloca:lxt2_write.c:1813 obsoleteFunctionsalloca:lxt2_write.c:1819 variableScope:lxt2_write.c:33 variableScope:lxt2_write.c:63 variableScope:lxt2_write.c:196 variableScope:lxt2_write.c:463 variableScope:lxt2_write.c:464 variableScope:lxt2_write.c:523 variableScope:lxt2_write.c:581 variableScope:lxt2_write.c:587 variableScope:lxt2_write.c:1157 variableScope:lxt2_write.c:1613 variableScope:lxt2_write.c:2060 // These functions are not used by Icarus // lxt2_wr_emit_value_int() unusedFunction:lxt2_write.c:1611 // lxt2_wr_inc_time_by_delta() unusedFunction:lxt2_write.c:997 // lxt2_wr_inc_time_by_delta64() unusedFunction:lxt2_write.c:1007 // lxt2_wr_set_checkpoint_off() unusedFunction:lxt2_write.c:835 // lxt2_wr_set_checkpoint_on() unusedFunction:lxt2_write.c:843 // lxt2_wr_set_maxgranule() unusedFunction:lxt2_write.c:1567 // lxt2_wr_set_partial_preference() unusedFunction:lxt2_write.c:812 // lxt2_wr_set_timezero() unusedFunction:lxt2_write.c:2198 // lxt2_wr_symbol_bracket_stripping() unusedFunction:lxt2_write.c:1581 // lxt2_wr_symbol_find() unusedFunction:lxt2_write.c:877 // lxt_write.c from GTKWave variableScope:lxt_write.c:31 variableScope:lxt_write.c:83 variableScope:lxt_write.c:527 variableScope:lxt_write.c:528 variableScope:lxt_write.c:587 variableScope:lxt_write.c:640 variableScope:lxt_write.c:780 variableScope:lxt_write.c:880 variableScope:lxt_write.c:1056 variableScope:lxt_write.c:1057 variableScope:lxt_write.c:1058 variableScope:lxt_write.c:1194 variableScope:lxt_write.c:1850 variableScope:lxt_write.c:2029 variableScope:lxt_write.c:2030 variableScope:lxt_write.c:2147 variableScope:lxt_write.c:2148 variableScope:lxt_write.c:2265 variableScope:lxt_write.c:2266 variableScope:lxt_write.c:2595 variableScope:lxt_write.c:2596 variableScope:lxt_write.c:2597 variableScope:lxt_write.c:2598 // These functions are not used by Icarus // lt_emit_value_int() unusedFunction:lxt_write.c:1661 // lt_emit_value_string() unusedFunction:lxt_write.c:2144 // lt_inc_time_by_delta() unusedFunction:lxt_write.c:1374 // lt_inc_time_by_delta64() unusedFunction:lxt_write.c:1384 // lt_set_chg_compress() unusedFunction:lxt_write.c:1457 // lt_set_dict_compress() unusedFunction:lxt_write.c:1474 // lt_set_time() unusedFunction:lxt_write.c:1379 // lt_set_timezero() unusedFunction:lxt_write.c:2820 // lt_symbol_bracket_stripping() unusedFunction:lxt_write.c:1585 // lt_symbol_find() unusedFunction:lxt_write.c:1277 // fastlz.c from GTKWave // These functions are not used by Icarus // fastlz_compress_level() unusedFunction:fastlz.c:150 // lz4.c from GTKWave // These functions are not used by Icarus // LZ4_compress_continue() unusedFunction:lz4.c:1460 // LZ4_compress_destSize() unusedFunction:lz4.c:912 // LZ4_compress_fast_force() unusedFunction:lz4.c:705 // LZ4_compress_forceExtDict() unusedFunction:lz4.c:1063 // LZ4_compress_limitedOutput() unusedFunction:lz4.c:1455 // LZ4_compress_limitedOutput_continue() unusedFunction:lz4.c:1459 // LZ4_compress_limitedOutput_withState() unusedFunction:lz4.c:1457 // LZ4_compress_withState() unusedFunction:lz4.c:1458 // LZ4_create() unusedFunction:lz4.c:1489 // LZ4_createStream() unusedFunction:lz4.c:935 // LZ4_createStreamDecode() unusedFunction:lz4.c:1319 // LZ4_decompress_fast_continue() unusedFunction:lz4.c:1384 // LZ4_decompress_fast_usingDict() unusedFunction:lz4.c:1439 // LZ4_decompress_fast_withPrefix64k() unusedFunction:lz4.c:1510 // LZ4_decompress_safe_continue() unusedFunction:lz4.c:1355 // LZ4_decompress_safe_forceExtDict() unusedFunction:lz4.c:1445 // LZ4_decompress_safe_usingDict() unusedFunction:lz4.c:1434 // LZ4_decompress_safe_withPrefix64k() unusedFunction:lz4.c:1505 // LZ4_freeStream() unusedFunction:lz4.c:948 // LZ4_freeStreamDecode() unusedFunction:lz4.c:1325 // LZ4_loadDict() unusedFunction:lz4.c:956 // LZ4_resetStreamState() unusedFunction:lz4.c:1482 // LZ4_setStreamDecode() unusedFunction:lz4.c:1338 // LZ4_sizeofState() unusedFunction:lz4.c:373 // LZ4_sizeofStreamState() unusedFunction:lz4.c:1474 // LZ4_slideInputBuffer() unusedFunction:lz4.c:1496 // LZ4_uncompress() unusedFunction:lz4.c:1468 // LZ4_uncompress_unknownOutputSize() unusedFunction:lz4.c:1469 // LZ4_versionNumber() unusedFunction:lz4.c:371 // The routines in sys_random.c are exact copies from IEEE1364-2005 and // they have scope warnings that we need to ignore. variableScope:sys_random.c:46 variableScope:sys_random.c:69 variableScope:sys_random.c:92 variableScope:sys_random.c:147 iverilog-10_1/vpi/fastlz.c000066400000000000000000000323311265551621300156110ustar00rootroot00000000000000/* FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "fastlz.h" #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) /* * Always check for bound when decompressing. * Generally it is best to leave it defined. */ #define FASTLZ_SAFE /* * Give hints to the compiler for branch prediction optimization. */ #if defined(__GNUC__) && (__GNUC__ > 2) #define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) #define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) #else #define FASTLZ_EXPECT_CONDITIONAL(c) (c) #define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) #define FASTLZ_INLINE inline #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) #define FASTLZ_INLINE __inline #else #define FASTLZ_INLINE #endif /* * Prevent accessing more than 8-bit at once, except on x86 architectures. */ #if !defined(FASTLZ_STRICT_ALIGN) #define FASTLZ_STRICT_ALIGN #if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ #undef FASTLZ_STRICT_ALIGN #elif defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__amd64) /* GNU C */ #undef FASTLZ_STRICT_ALIGN #elif defined(_M_IX86) /* Intel, MSVC */ #undef FASTLZ_STRICT_ALIGN #elif defined(__386) #undef FASTLZ_STRICT_ALIGN #elif defined(_X86_) /* MinGW */ #undef FASTLZ_STRICT_ALIGN #elif defined(__I86__) /* Digital Mars */ #undef FASTLZ_STRICT_ALIGN #endif #endif /* prototypes */ int fastlz_compress(const void* input, int length, void* output); int fastlz_compress_level(int level, const void* input, int length, void* output); int fastlz_decompress(const void* input, int length, void* output, int maxout); #define MAX_COPY 32 #define MAX_LEN 264 /* 256 + 8 */ #define MAX_DISTANCE 8192 #if !defined(FASTLZ_STRICT_ALIGN) #define FASTLZ_READU16(p) *((const flzuint16*)(p)) #else #define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) #endif #define HASH_LOG 13 #define HASH_SIZE (1<< HASH_LOG) #define HASH_MASK (HASH_SIZE-1) #define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } #undef FASTLZ_LEVEL #define FASTLZ_LEVEL 1 #undef FASTLZ_COMPRESSOR #undef FASTLZ_DECOMPRESSOR #define FASTLZ_COMPRESSOR fastlz1_compress #define FASTLZ_DECOMPRESSOR fastlz1_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c" #undef FASTLZ_LEVEL #define FASTLZ_LEVEL 2 #undef MAX_DISTANCE #define MAX_DISTANCE 8191 #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) #undef FASTLZ_COMPRESSOR #undef FASTLZ_DECOMPRESSOR #define FASTLZ_COMPRESSOR fastlz2_compress #define FASTLZ_DECOMPRESSOR fastlz2_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c" int fastlz_compress(const void* input, int length, void* output) { /* for short block, choose fastlz1 */ if(length < 65536) return fastlz1_compress(input, length, output); /* else... */ return fastlz2_compress(input, length, output); } int fastlz_decompress(const void* input, int length, void* output, int maxout) { /* magic identifier for compression level */ int level = ((*(const flzuint8*)input) >> 5) + 1; if(level == 1) return fastlz1_decompress(input, length, output, maxout); if(level == 2) return fastlz2_decompress(input, length, output, maxout); /* unknown level, trigger error */ return 0; } int fastlz_compress_level(int level, const void* input, int length, void* output) { if(level == 1) return fastlz1_compress(input, length, output); if(level == 2) return fastlz2_compress(input, length, output); return 0; } #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output) { const flzuint8* ip = (const flzuint8*) input; const flzuint8* ip_bound = ip + length - 2; const flzuint8* ip_limit = ip + length - 12; flzuint8* op = (flzuint8*) output; const flzuint8* htab[HASH_SIZE]; const flzuint8** hslot; flzuint32 hval; flzuint32 copy; /* sanity check */ if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) { if(length) { /* create literal copy only */ *op++ = length-1; ip_bound++; while(ip <= ip_bound) *op++ = *ip++; return length+1; } else return 0; } /* initializes hash table */ for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) *hslot = ip; /* we start with literal copy */ copy = 2; *op++ = MAX_COPY-1; *op++ = *ip++; *op++ = *ip++; /* main loop */ while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) { const flzuint8* ref; flzuint32 distance; /* minimum match length */ flzuint32 len = 3; /* comparison starting-point */ const flzuint8* anchor = ip; /* check for a run */ #if FASTLZ_LEVEL==2 if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) { distance = 1; /* ip += 3; */ /* scan-build, never used */ ref = anchor - 1 + 3; goto match; } #endif /* find potential match */ HASH_FUNCTION(hval,ip); hslot = htab + hval; ref = htab[hval]; /* calculate distance to the match */ distance = anchor - ref; /* update hash table */ *hslot = anchor; /* is this a match? check the first 3 bytes */ if(distance==0 || #if FASTLZ_LEVEL==1 (distance >= MAX_DISTANCE) || #else (distance >= MAX_FARDISTANCE) || #endif *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) goto literal; #if FASTLZ_LEVEL==2 /* far, needs at least 5-byte match */ if(distance >= MAX_DISTANCE) { if(*ip++ != *ref++ || *ip++!= *ref++) goto literal; len += 2; } match: #endif /* last matched byte */ ip = anchor + len; /* distance is biased */ distance--; if(!distance) { /* zero distance means a run */ flzuint8 x = ip[-1]; while(ip < ip_bound) if(*ref++ != x) break; else ip++; } else for(;;) { /* safe because the outer check against ip limit */ if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; while(ip < ip_bound) if(*ref++ != *ip++) break; break; } /* if we have copied something, adjust the copy count */ if(copy) /* copy is biased, '0' means 1 byte copy */ *(op-copy-1) = copy-1; else /* back, to overwrite the copy count */ op--; /* reset literal counter */ copy = 0; /* length is biased, '1' means a match of 3 bytes */ ip -= 3; len = ip - anchor; /* encode the match */ #if FASTLZ_LEVEL==2 if(distance < MAX_DISTANCE) { if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (7 << 5) + (distance >> 8); for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = (distance & 255); } } else { /* far away, but not yet in the another galaxy... */ if(len < 7) { distance -= MAX_DISTANCE; *op++ = (len << 5) + 31; *op++ = 255; *op++ = distance >> 8; *op++ = distance & 255; } else { distance -= MAX_DISTANCE; *op++ = (7 << 5) + 31; for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = 255; *op++ = distance >> 8; *op++ = distance & 255; } } #else if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) while(len > MAX_LEN-2) { *op++ = (7 << 5) + (distance >> 8); *op++ = MAX_LEN - 2 - 7 -2; *op++ = (distance & 255); len -= MAX_LEN-2; } if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (7 << 5) + (distance >> 8); *op++ = len - 7; *op++ = (distance & 255); } #endif /* update the hash at match boundary */ HASH_FUNCTION(hval,ip); htab[hval] = ip++; HASH_FUNCTION(hval,ip); htab[hval] = ip++; /* assuming literal copy */ *op++ = MAX_COPY-1; continue; literal: *op++ = *anchor++; ip = anchor; copy++; if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) { copy = 0; *op++ = MAX_COPY-1; } } /* left-over as literal copy */ ip_bound++; while(ip <= ip_bound) { *op++ = *ip++; copy++; if(copy == MAX_COPY) { copy = 0; *op++ = MAX_COPY-1; } } /* if we have copied something, adjust the copy length */ if(copy) *(op-copy-1) = copy-1; else op--; #if FASTLZ_LEVEL==2 /* marker for fastlz2 */ *(flzuint8*)output |= (1 << 5); #endif return op - (flzuint8*)output; } static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout) { const flzuint8* ip = (const flzuint8*) input; const flzuint8* ip_limit = ip + length; flzuint8* op = (flzuint8*) output; flzuint8* op_limit = op + maxout; flzuint32 ctrl = (*ip++) & 31; int loop = 1; do { const flzuint8* ref = op; flzuint32 len = ctrl >> 5; flzuint32 ofs = (ctrl & 31) << 8; if(ctrl >= 32) { #if FASTLZ_LEVEL==2 flzuint8 code; #endif len--; ref -= ofs; if (len == 7-1) #if FASTLZ_LEVEL==1 len += *ip++; ref -= *ip++; #else do { code = *ip++; len += code; } while (code==255); code = *ip++; ref -= code; /* match from 16-bit distance */ if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) { ofs = (*ip++) << 8; ofs += *ip++; ref = op - ofs - MAX_DISTANCE; } #endif #ifdef FASTLZ_SAFE if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) return 0; if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) return 0; #endif if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) ctrl = *ip++; else loop = 0; if(ref == op) { /* optimize copy for a run */ flzuint8 b = ref[-1]; *op++ = b; *op++ = b; *op++ = b; for(; len; --len) *op++ = b; } else { #if !defined(FASTLZ_STRICT_ALIGN) const flzuint16* p; flzuint16* q; #endif /* copy from reference */ ref--; *op++ = *ref++; *op++ = *ref++; *op++ = *ref++; #if !defined(FASTLZ_STRICT_ALIGN) /* copy a byte, so that now it's word aligned */ if(len & 1) { *op++ = *ref++; len--; } /* copy 16-bit at once */ q = (flzuint16*) op; op += len; p = (const flzuint16*) ref; for(len>>=1; len > 4; len-=4) { *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = *p++; } for(; len; --len) *q++ = *p++; #else for(; len; --len) *op++ = *ref++; #endif } } else { ctrl++; #ifdef FASTLZ_SAFE if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) return 0; if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) return 0; #endif *op++ = *ip++; for(--ctrl; ctrl; ctrl--) *op++ = *ip++; loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); if(loop) ctrl = *ip++; } } while(FASTLZ_EXPECT_CONDITIONAL(loop)); return op - (flzuint8*)output; } #endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ iverilog-10_1/vpi/fastlz.h000066400000000000000000000071051265551621300156170ustar00rootroot00000000000000/* FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FASTLZ_H #define FASTLZ_H #include #define flzuint8 uint8_t #define flzuint16 uint16_t #define flzuint32 uint32_t #define FASTLZ_VERSION 0x000100 #define FASTLZ_VERSION_MAJOR 0 #define FASTLZ_VERSION_MINOR 0 #define FASTLZ_VERSION_REVISION 0 #define FASTLZ_VERSION_STRING "0.1.0" #if defined (__cplusplus) extern "C" { #endif /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, the return value might be larger than length (input buffer size). The input buffer and the output buffer can not overlap. */ int fastlz_compress(const void* input, int length, void* output); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int fastlz_decompress(const void* input, int length, void* output, int maxout); /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, the return value might be larger than length (input buffer size). The input buffer and the output buffer can not overlap. Compression level can be specified in parameter level. At the moment, only level 1 and level 2 are supported. Level 1 is the fastest compression and generally useful for short data. Level 2 is slightly slower but it gives better compression ratio. Note that the compressed data, regardless of the level, can always be decompressed using the function fastlz_decompress above. */ int fastlz_compress_level(int level, const void* input, int length, void* output); #if defined (__cplusplus) } #endif #endif /* FASTLZ_H */ iverilog-10_1/vpi/fstapi.c000066400000000000000000007105111265551621300155770ustar00rootroot00000000000000/* * Copyright (c) 2009-2015 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * possible disables: * * FST_DYNAMIC_ALIAS_DISABLE : dynamic aliases are not processed * FST_DYNAMIC_ALIAS2_DISABLE : new encoding for dynamic aliases is not generated * FST_WRITEX_DISABLE : fast write I/O routines are disabled * * possible enables: * * FST_DEBUG : not for production use, only enable for development * FST_REMOVE_DUPLICATE_VC : glitch removal (has writer performance impact) * HAVE_LIBPTHREAD -> FST_WRITER_PARALLEL : enables inclusion of parallel writer code * FST_DO_MISALIGNED_OPS (defined automatically for x86 and some others) : CPU architecture can handle misaligned loads/stores * _WAVE_HAVE_JUDY : use Judy arrays instead of Jenkins (undefine if LGPL is not acceptable) * */ #include #include "fstapi.h" #include "fastlz.h" #include "lz4.h" #ifndef HAVE_LIBPTHREAD #undef FST_WRITER_PARALLEL #endif #ifdef FST_WRITER_PARALLEL #include #endif #ifdef __MINGW32__ #include #endif #ifdef HAVE_ALLOCA_H #include #elif defined(__GNUC__) #ifndef __MINGW32__ #ifndef alloca #define alloca __builtin_alloca #endif #else #include #endif #elif defined(_MSC_VER) #include #define alloca _alloca #endif #ifndef PATH_MAX #define PATH_MAX (4096) #endif /* note that Judy versus Jenkins requires more experimentation: they are */ /* functionally equivalent though it appears Jenkins is slightly faster. */ /* in addition, Jenkins is not bound by the LGPL. */ #ifdef _WAVE_HAVE_JUDY #include #else /* should be more than enough for fstWriterSetSourceStem() */ #define FST_PATH_HASHMASK ((1UL << 16) - 1) typedef const void *Pcvoid_t; typedef void *Pvoid_t; typedef void **PPvoid_t; #define JudyHSIns(a,b,c,d) JenkinsIns((a),(b),(c),(hashmask)) #define JudyHSFreeArray(a,b) JenkinsFree((a),(hashmask)) void JenkinsFree(void *base_i, uint32_t hashmask); void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint32_t hashmask); #endif #ifndef FST_WRITEX_DISABLE #define FST_WRITEX_MAX (64 * 1024) #else #define fstWritex(a,b,c) fstFwrite((b), (c), 1, fv) #endif /* these defines have a large impact on writer speed when a model has a */ /* huge number of symbols. as a default, use 128MB and increment when */ /* every 1M signals are defined. */ #define FST_BREAK_SIZE (1UL << 27) #define FST_BREAK_ADD_SIZE (1UL << 22) #define FST_BREAK_SIZE_MAX (1UL << 31) #define FST_ACTIVATE_HUGE_BREAK (1000000) #define FST_ACTIVATE_HUGE_INC (1000000) #define FST_WRITER_STR "fstWriter" #define FST_ID_NAM_SIZ (512) #define FST_ID_NAM_ATTR_SIZ (65536+4096) #define FST_DOUBLE_ENDTEST (2.7182818284590452354) #define FST_HDR_SIM_VERSION_SIZE (128) #define FST_HDR_DATE_SIZE (119) #define FST_HDR_FILETYPE_SIZE (1) #define FST_HDR_TIMEZERO_SIZE (8) #define FST_GZIO_LEN (32768) #define FST_HDR_FOURPACK_DUO_SIZE (4*1024*1024) #if defined(__i386__) || defined(__x86_64__) || defined(_AIX) #define FST_DO_MISALIGNED_OPS #endif #if defined(__APPLE__) && defined(__MACH__) #define FST_MACOSX #include #endif /***********************/ /*** ***/ /*** common function ***/ /*** ***/ /***********************/ #ifdef __MINGW32__ #include #ifndef HAVE_FSEEKO #define ftello ftell #define fseeko fseek #endif #endif /* * the recoded "extra" values... * note that FST_RCV_Q is currently unused and is for future expansion. * its intended use is as another level of escape such that any arbitrary * value can be stored as the value: { time_delta, 8 bits, FST_RCV_Q }. * this is currently not implemented so that the branchless decode is: * uint32_t shcnt = 2 << (vli & 1); tdelta = vli >> shcnt; */ #define FST_RCV_X (1 | (0<<1)) #define FST_RCV_Z (1 | (1<<1)) #define FST_RCV_H (1 | (2<<1)) #define FST_RCV_U (1 | (3<<1)) #define FST_RCV_W (1 | (4<<1)) #define FST_RCV_L (1 | (5<<1)) #define FST_RCV_D (1 | (6<<1)) #define FST_RCV_Q (1 | (7<<1)) #define FST_RCV_STR "xzhuwl-?" /* 01234567 */ /* * prevent old file overwrite when currently being read */ static FILE *unlink_fopen(const char *nam, const char *mode) { unlink(nam); return(fopen(nam, mode)); } /* * system-specific temp file handling */ #ifdef __MINGW32__ static FILE* tmpfile_open(char **nam) { char *fname = NULL; TCHAR szTempFileName[MAX_PATH]; TCHAR lpTempPathBuffer[MAX_PATH]; DWORD dwRetVal = 0; UINT uRetVal = 0; FILE *fh = NULL; if(nam) /* cppcheck warning fix: nam is always defined, so this is not needed */ { dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer); if((dwRetVal > MAX_PATH) || (dwRetVal == 0)) { fprintf(stderr, "GetTempPath() failed in "__FILE__" line %d, exiting.\n", __LINE__); exit(255); } else { uRetVal = GetTempFileName(lpTempPathBuffer, TEXT("FSTW"), 0, szTempFileName); if (uRetVal == 0) { fprintf(stderr, "GetTempFileName() failed in "__FILE__" line %d, exiting.\n", __LINE__); exit(255); } else { fname = strdup(szTempFileName); } } if(fname) { *nam = fname; fh = unlink_fopen(fname, "w+b"); } } return(fh); } #else static FILE* tmpfile_open(char **nam) { FILE *f = tmpfile(); /* replace with mkstemp() + fopen(), etc if this is not good enough */ if(nam) { *nam = NULL; } return(f); } #endif static void tmpfile_close(FILE **f, char **nam) { if(f) { if(*f) { fclose(*f); *f = NULL; } } if(nam) { if(*nam) { unlink(*nam); free(*nam); *nam = NULL; } } } /*****************************************/ /* * to remove warn_unused_result compile time messages * (in the future there needs to be results checking) */ static size_t fstFread(void *buf, size_t siz, size_t cnt, FILE *fp) { return(fread(buf, siz, cnt, fp)); } static size_t fstFwrite(const void *buf, size_t siz, size_t cnt, FILE *fp) { return(fwrite(buf, siz, cnt, fp)); } static int fstFtruncate(int fd, off_t length) { return(ftruncate(fd, length)); } /* * realpath compatibility */ static char *fstRealpath(const char *path, char *resolved_path) { #if defined __USE_BSD || defined __USE_XOPEN_EXTENDED || defined __CYGWIN__ || defined HAVE_REALPATH #if (defined(__MACH__) && defined(__APPLE__)) if(!resolved_path) { resolved_path = malloc(PATH_MAX+1); /* fixes bug on Leopard when resolved_path == NULL */ } #endif return(realpath(path, resolved_path)); #else #ifdef __MINGW32__ if(!resolved_path) { resolved_path = malloc(PATH_MAX+1); } return(_fullpath(resolved_path, path, PATH_MAX)); #else (void)path; (void)resolved_path; return(NULL); #endif #endif } /* * mmap compatibility */ #if defined __CYGWIN__ || defined __MINGW32__ #include #define fstMmap(__addr,__len,__prot,__flags,__fd,__off) fstMmap2((__len), (__fd), (__off)) #define fstMunmap(__addr,__len) free(__addr) static void *fstMmap2(size_t __len, int __fd, off_t __off) { (void)__off; unsigned char *pnt = malloc(__len); off_t cur_offs = lseek(__fd, 0, SEEK_CUR); size_t i; lseek(__fd, 0, SEEK_SET); for(i=0;i<__len;i+=SSIZE_MAX) { read(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i)); } lseek(__fd, cur_offs, SEEK_SET); return(pnt); } #else #include #if defined(__SUNPRO_C) #define FST_CADDR_T_CAST (caddr_t) #else #define FST_CADDR_T_CAST #endif #define fstMmap(__addr,__len,__prot,__flags,__fd,__off) (void*)mmap(FST_CADDR_T_CAST (__addr),(__len),(__prot),(__flags),(__fd),(__off)) #define fstMunmap(__addr,__len) { if(__addr) munmap(FST_CADDR_T_CAST (__addr),(__len)); } #endif /* * regular and variable-length integer access functions */ #ifdef FST_DO_MISALIGNED_OPS #define fstGetUint32(x) (*(uint32_t *)(x)) #else static uint32_t fstGetUint32(unsigned char *mem) { uint32_t u32; unsigned char *buf = (unsigned char *)(&u32); buf[0] = mem[0]; buf[1] = mem[1]; buf[2] = mem[2]; buf[3] = mem[3]; return(*(uint32_t *)buf); } #endif static int fstWriterUint64(FILE *handle, uint64_t v) { unsigned char buf[8]; int i; for(i=7;i>=0;i--) { buf[i] = v & 0xff; v >>= 8; } fstFwrite(buf, 8, 1, handle); return(8); } static uint64_t fstReaderUint64(FILE *f) { uint64_t val = 0; unsigned char buf[sizeof(uint64_t)]; unsigned int i; fstFread(buf, sizeof(uint64_t), 1, f); for(i=0;i>7)) /* determine len to avoid temp buffer copying to cut down on load-hit-store */ { cnt++; } pnt -= cnt; spnt = pnt; cnt--; for(i=0;i>7; *(spnt++) = ((unsigned char)v) | 0x80; v = nxt; } *spnt = (unsigned char)v; return(pnt); } static unsigned char *fstCopyVarint64ToRight(unsigned char *pnt, uint64_t v) { uint64_t nxt; while((nxt = v>>7)) { *(pnt++) = ((unsigned char)v) | 0x80; v = nxt; } *(pnt++) = (unsigned char)v; return(pnt); } static uint64_t fstGetVarint64(unsigned char *mem, int *skiplen) { unsigned char *mem_orig = mem; uint64_t rc = 0; while(*mem & 0x80) { mem++; } *skiplen = mem - mem_orig + 1; for(;;) { rc <<= 7; rc |= (uint64_t)(*mem & 0x7f); if(mem == mem_orig) { break; } mem--; } return(rc); } static uint32_t fstReaderVarint32(FILE *f) { unsigned char buf[5]; unsigned char *mem = buf; uint32_t rc = 0; int ch; do { ch = fgetc(f); *(mem++) = ch; } while(ch & 0x80); mem--; for(;;) { rc <<= 7; rc |= (uint32_t)(*mem & 0x7f); if(mem == buf) { break; } mem--; } return(rc); } static uint32_t fstReaderVarint32WithSkip(FILE *f, uint32_t *skiplen) { unsigned char buf[5]; unsigned char *mem = buf; uint32_t rc = 0; int ch; do { ch = fgetc(f); *(mem++) = ch; } while(ch & 0x80); *skiplen = mem - buf; mem--; for(;;) { rc <<= 7; rc |= (uint32_t)(*mem & 0x7f); if(mem == buf) { break; } mem--; } return(rc); } static uint64_t fstReaderVarint64(FILE *f) { unsigned char buf[16]; unsigned char *mem = buf; uint64_t rc = 0; int ch; do { ch = fgetc(f); *(mem++) = ch; } while(ch & 0x80); mem--; for(;;) { rc <<= 7; rc |= (uint64_t)(*mem & 0x7f); if(mem == buf) { break; } mem--; } return(rc); } static int fstWriterVarint(FILE *handle, uint64_t v) { uint64_t nxt; unsigned char buf[10]; /* ceil(64/7) = 10 */ unsigned char *pnt = buf; int len; while((nxt = v>>7)) { *(pnt++) = ((unsigned char)v) | 0x80; v = nxt; } *(pnt++) = (unsigned char)v; len = pnt-buf; fstFwrite(buf, len, 1, handle); return(len); } /* signed integer read/write routines are currently unused */ static int64_t fstGetSVarint64(unsigned char *mem, int *skiplen) { unsigned char *mem_orig = mem; int64_t rc = 0; const int64_t one = 1; const int siz = sizeof(int64_t) * 8; int shift = 0; unsigned char byt; do { byt = *(mem++); rc |= ((int64_t)(byt & 0x7f)) << shift; shift += 7; } while(byt & 0x80); if((shift>= 7; if (((!v) && (!(byt & 0x40))) || ((v == -1) && (byt & 0x40))) { more = 0; byt &= 0x7f; } *(pnt++) = byt; } while(more); len = pnt-buf; fstFwrite(buf, len, 1, handle); return(len); } /***********************/ /*** ***/ /*** writer function ***/ /*** ***/ /***********************/ /* * private structs */ struct fstBlackoutChain { struct fstBlackoutChain *next; uint64_t tim; unsigned active : 1; }; struct fstWriterContext { FILE *handle; FILE *hier_handle; FILE *geom_handle; FILE *valpos_handle; FILE *curval_handle; FILE *tchn_handle; unsigned char *vchg_mem; off_t hier_file_len; uint32_t *valpos_mem; unsigned char *curval_mem; char *filename; fstHandle maxhandle; fstHandle numsigs; uint32_t maxvalpos; unsigned vc_emitted : 1; unsigned is_initial_time : 1; unsigned fourpack : 1; unsigned fastpack : 1; int64_t timezero; off_t section_header_truncpos; uint32_t tchn_cnt, tchn_idx; uint64_t curtime; uint64_t firsttime; uint32_t vchg_siz; uint32_t vchg_alloc_siz; uint32_t secnum; off_t section_start; uint32_t numscopes; double nan; /* nan value for uninitialized doubles */ struct fstBlackoutChain *blackout_head; struct fstBlackoutChain *blackout_curr; uint32_t num_blackouts; uint64_t dump_size_limit; unsigned char filetype; /* default is 0, FST_FT_VERILOG */ unsigned compress_hier : 1; unsigned repack_on_close : 1; unsigned skip_writing_section_hdr : 1; unsigned size_limit_locked : 1; unsigned section_header_only : 1; unsigned flush_context_pending : 1; unsigned parallel_enabled : 1; unsigned parallel_was_enabled : 1; /* should really be semaphores, but are bytes to cut down on read-modify-write window size */ unsigned char already_in_flush; /* in case control-c handlers interrupt */ unsigned char already_in_close; /* in case control-c handlers interrupt */ #ifdef FST_WRITER_PARALLEL pthread_mutex_t mutex; pthread_t thread; pthread_attr_t thread_attr; struct fstWriterContext *xc_parent; #endif size_t fst_orig_break_size; size_t fst_orig_break_add_size; size_t fst_break_size; size_t fst_break_add_size; size_t fst_huge_break_size; fstHandle next_huge_break; Pvoid_t path_array; uint32_t path_array_count; unsigned fseek_failed : 1; char *geom_handle_nam; char *valpos_handle_nam; char *curval_handle_nam; char *tchn_handle_nam; }; static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, off_t offset, int whence) { int rc = fseeko(stream, offset, whence); if(rc<0) { xc->fseek_failed = 1; #ifdef FST_DEBUG fprintf(stderr, "Seek to #%"PRId64" (whence = %d) failed!\n", offset, whence); perror("Why"); #endif } return(rc); } static uint32_t fstWriterUint32WithVarint32(struct fstWriterContext *xc, uint32_t *u, uint32_t v, const void *dbuf, uint32_t siz) { unsigned char *buf = xc->vchg_mem + xc->vchg_siz; unsigned char *pnt = buf; uint32_t nxt; uint32_t len; #ifdef FST_DO_MISALIGNED_OPS (*(uint32_t *)(pnt)) = (*(uint32_t *)(u)); #else memcpy(pnt, u, sizeof(uint32_t)); #endif pnt += 4; while((nxt = v>>7)) { *(pnt++) = ((unsigned char)v) | 0x80; v = nxt; } *(pnt++) = (unsigned char)v; memcpy(pnt, dbuf, siz); len = pnt-buf + siz; return(len); } static uint32_t fstWriterUint32WithVarint32AndLength(struct fstWriterContext *xc, uint32_t *u, uint32_t v, const void *dbuf, uint32_t siz) { unsigned char *buf = xc->vchg_mem + xc->vchg_siz; unsigned char *pnt = buf; uint32_t nxt; uint32_t len; #ifdef FST_DO_MISALIGNED_OPS (*(uint32_t *)(pnt)) = (*(uint32_t *)(u)); #else memcpy(pnt, u, sizeof(uint32_t)); #endif pnt += 4; while((nxt = v>>7)) { *(pnt++) = ((unsigned char)v) | 0x80; v = nxt; } *(pnt++) = (unsigned char)v; v = siz; while((nxt = v>>7)) { *(pnt++) = ((unsigned char)v) | 0x80; v = nxt; } *(pnt++) = (unsigned char)v; memcpy(pnt, dbuf, siz); len = pnt-buf + siz; return(len); } /* * header bytes, write here so defines are set up before anything else * that needs to use them */ static void fstWriterEmitHdrBytes(struct fstWriterContext *xc) { char vbuf[FST_HDR_SIM_VERSION_SIZE]; char dbuf[FST_HDR_DATE_SIZE]; double endtest = FST_DOUBLE_ENDTEST; time_t walltime; #define FST_HDR_OFFS_TAG (0) fputc(FST_BL_HDR, xc->handle); /* +0 tag */ #define FST_HDR_OFFS_SECLEN (FST_HDR_OFFS_TAG + 1) fstWriterUint64(xc->handle, 329); /* +1 section length */ #define FST_HDR_OFFS_START_TIME (FST_HDR_OFFS_SECLEN + 8) fstWriterUint64(xc->handle, 0); /* +9 start time */ #define FST_HDR_OFFS_END_TIME (FST_HDR_OFFS_START_TIME + 8) fstWriterUint64(xc->handle, 0); /* +17 end time */ #define FST_HDR_OFFS_ENDIAN_TEST (FST_HDR_OFFS_END_TIME + 8) fstFwrite(&endtest, 8, 1, xc->handle); /* +25 endian test for reals */ #define FST_HDR_OFFS_MEM_USED (FST_HDR_OFFS_ENDIAN_TEST + 8) fstWriterUint64(xc->handle, xc->fst_break_size);/* +33 memory used by writer */ #define FST_HDR_OFFS_NUM_SCOPES (FST_HDR_OFFS_MEM_USED + 8) fstWriterUint64(xc->handle, 0); /* +41 scope creation count */ #define FST_HDR_OFFS_NUM_VARS (FST_HDR_OFFS_NUM_SCOPES + 8) fstWriterUint64(xc->handle, 0); /* +49 var creation count */ #define FST_HDR_OFFS_MAXHANDLE (FST_HDR_OFFS_NUM_VARS + 8) fstWriterUint64(xc->handle, 0); /* +57 max var idcode */ #define FST_HDR_OFFS_SECTION_CNT (FST_HDR_OFFS_MAXHANDLE + 8) fstWriterUint64(xc->handle, 0); /* +65 vc section count */ #define FST_HDR_OFFS_TIMESCALE (FST_HDR_OFFS_SECTION_CNT + 8) fputc((-9)&255, xc->handle); /* +73 timescale 1ns */ #define FST_HDR_OFFS_SIM_VERSION (FST_HDR_OFFS_TIMESCALE + 1) memset(vbuf, 0, FST_HDR_SIM_VERSION_SIZE); strcpy(vbuf, FST_WRITER_STR); fstFwrite(vbuf, FST_HDR_SIM_VERSION_SIZE, 1, xc->handle); /* +74 version */ #define FST_HDR_OFFS_DATE (FST_HDR_OFFS_SIM_VERSION + FST_HDR_SIM_VERSION_SIZE) memset(dbuf, 0, FST_HDR_DATE_SIZE); time(&walltime); strcpy(dbuf, asctime(localtime(&walltime))); fstFwrite(dbuf, FST_HDR_DATE_SIZE, 1, xc->handle); /* +202 date */ /* date size is deliberately overspecified at 119 bytes (originally 128) in order to provide backfill for new args */ #define FST_HDR_OFFS_FILETYPE (FST_HDR_OFFS_DATE + FST_HDR_DATE_SIZE) fputc(xc->filetype, xc->handle); /* +321 filetype */ #define FST_HDR_OFFS_TIMEZERO (FST_HDR_OFFS_FILETYPE + FST_HDR_FILETYPE_SIZE) fstWriterUint64(xc->handle, xc->timezero); /* +322 timezero */ #define FST_HDR_LENGTH (FST_HDR_OFFS_TIMEZERO + FST_HDR_TIMEZERO_SIZE) /* +330 next section starts here */ fflush(xc->handle); } /* * mmap functions */ static void fstWriterCreateMmaps(struct fstWriterContext *xc) { off_t curpos = ftello(xc->handle); fflush(xc->hier_handle); /* write out intermediate header */ fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_START_TIME, SEEK_SET); fstWriterUint64(xc->handle, xc->firsttime); fstWriterUint64(xc->handle, xc->curtime); fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_NUM_SCOPES, SEEK_SET); fstWriterUint64(xc->handle, xc->numscopes); fstWriterUint64(xc->handle, xc->numsigs); fstWriterUint64(xc->handle, xc->maxhandle); fstWriterUint64(xc->handle, xc->secnum); fstWriterFseeko(xc, xc->handle, curpos, SEEK_SET); fflush(xc->handle); /* do mappings */ if(!xc->valpos_mem) { fflush(xc->valpos_handle); xc->valpos_mem = fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->valpos_handle), 0); } if(!xc->curval_mem) { fflush(xc->curval_handle); xc->curval_mem = fstMmap(NULL, xc->maxvalpos, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->curval_handle), 0); } } static void fstDestroyMmaps(struct fstWriterContext *xc, int is_closing) { (void)is_closing; fstMunmap(xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); xc->valpos_mem = NULL; #if defined __CYGWIN__ || defined __MINGW32__ if(xc->curval_mem) { if(!is_closing) /* need to flush out for next emulated mmap() read */ { unsigned char *pnt = xc->curval_mem; int __fd = fileno(xc->curval_handle); off_t cur_offs = lseek(__fd, 0, SEEK_CUR); size_t i; size_t __len = xc->maxvalpos; lseek(__fd, 0, SEEK_SET); for(i=0;i<__len;i+=SSIZE_MAX) { write(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i)); } lseek(__fd, cur_offs, SEEK_SET); } } #endif fstMunmap(xc->curval_mem, xc->maxvalpos); xc->curval_mem = NULL; } /* * set up large and small memory usages * crossover point in model is FST_ACTIVATE_HUGE_BREAK number of signals */ static void fstDetermineBreakSize(struct fstWriterContext *xc) { #if defined(__linux__) || defined(FST_MACOSX) int was_set = 0; #ifdef __linux__ FILE *f = fopen("/proc/meminfo", "rb"); if(f) { char buf[257]; char *s; while(!feof(f)) { buf[0] = 0; s = fgets(buf, 256, f); if(s && *s) { if(!strncmp(s, "MemTotal:", 9)) { size_t v = atol(s+10); v *= 1024; /* convert to bytes */ v /= 8; /* chop down to 1/8 physical memory */ if(v > FST_BREAK_SIZE) { if(v > FST_BREAK_SIZE_MAX) { v = FST_BREAK_SIZE_MAX; } xc->fst_huge_break_size = v; was_set = 1; break; } } } } fclose(f); } if(!was_set) { xc->fst_huge_break_size = FST_BREAK_SIZE; } #else int mib[2]; int64_t v; size_t length; mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; length = sizeof(int64_t); if(!sysctl(mib, 2, &v, &length, NULL, 0)) { v /= 8; if(v > (int64_t)FST_BREAK_SIZE) { if(v > (int64_t)FST_BREAK_SIZE_MAX) { v = FST_BREAK_SIZE_MAX; } xc->fst_huge_break_size = v; was_set = 1; } } if(!was_set) { xc->fst_huge_break_size = FST_BREAK_SIZE; } #endif #else xc->fst_huge_break_size = FST_BREAK_SIZE; #endif xc->fst_break_size = xc->fst_orig_break_size = FST_BREAK_SIZE; xc->fst_break_add_size = xc->fst_orig_break_add_size = FST_BREAK_ADD_SIZE; xc->next_huge_break = FST_ACTIVATE_HUGE_BREAK; } /* * file creation and close */ void *fstWriterCreate(const char *nam, int use_compressed_hier) { struct fstWriterContext *xc = calloc(1, sizeof(struct fstWriterContext)); xc->compress_hier = use_compressed_hier; fstDetermineBreakSize(xc); if((!nam)|| (!(xc->handle=unlink_fopen(nam, "w+b")))) { free(xc); xc=NULL; } else { int flen = strlen(nam); char *hf = calloc(1, flen + 6); memcpy(hf, nam, flen); strcpy(hf + flen, ".hier"); xc->hier_handle = unlink_fopen(hf, "w+b"); xc->geom_handle = tmpfile_open(&xc->geom_handle_nam); /* .geom */ xc->valpos_handle = tmpfile_open(&xc->valpos_handle_nam); /* .offs */ xc->curval_handle = tmpfile_open(&xc->curval_handle_nam); /* .bits */ xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam); /* .tchn */ xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size; xc->vchg_mem = malloc(xc->vchg_alloc_siz); if(xc->hier_handle && xc->geom_handle && xc->valpos_handle && xc->curval_handle && xc->vchg_mem && xc->tchn_handle) { xc->filename = strdup(nam); xc->is_initial_time = 1; fstWriterEmitHdrBytes(xc); xc->nan = strtod("NaN", NULL); #ifdef FST_WRITER_PARALLEL pthread_mutex_init(&xc->mutex, NULL); pthread_attr_init(&xc->thread_attr); pthread_attr_setdetachstate(&xc->thread_attr, PTHREAD_CREATE_DETACHED); #endif } else { fclose(xc->handle); if(xc->hier_handle) { fclose(xc->hier_handle); unlink(hf); } tmpfile_close(&xc->geom_handle, &xc->geom_handle_nam); tmpfile_close(&xc->valpos_handle, &xc->valpos_handle_nam); tmpfile_close(&xc->curval_handle, &xc->curval_handle_nam); tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); free(xc->vchg_mem); free(xc); xc=NULL; } free(hf); } return(xc); } /* * generation and writing out of value change data sections */ static void fstWriterEmitSectionHeader(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { unsigned long destlen; unsigned char *dmem; int rc; destlen = xc->maxvalpos; dmem = malloc(compressBound(destlen)); rc = compress2(dmem, &destlen, xc->curval_mem, xc->maxvalpos, 4); /* was 9...which caused performance drag on traces with many signals */ fputc(FST_BL_SKIP, xc->handle); /* temporarily tag the section, use FST_BL_VCDATA on finalize */ xc->section_start = ftello(xc->handle); #ifdef FST_WRITER_PARALLEL if(xc->xc_parent) xc->xc_parent->section_start = xc->section_start; #endif xc->section_header_only = 1; /* indicates truncate might be needed */ fstWriterUint64(xc->handle, 0); /* placeholder = section length */ fstWriterUint64(xc->handle, xc->is_initial_time ? xc->firsttime : xc->curtime); /* begin time of section */ fstWriterUint64(xc->handle, xc->curtime); /* end time of section (placeholder) */ fstWriterUint64(xc->handle, 0); /* placeholder = amount of buffer memory required in reader for full vc traversal */ fstWriterVarint(xc->handle, xc->maxvalpos); /* maxvalpos = length of uncompressed data */ if((rc == Z_OK) && (destlen < xc->maxvalpos)) { fstWriterVarint(xc->handle, destlen); /* length of compressed data */ } else { fstWriterVarint(xc->handle, xc->maxvalpos); /* length of (unable to be) compressed data */ } fstWriterVarint(xc->handle, xc->maxhandle); /* max handle associated with this data (in case of dynamic facility adds) */ if((rc == Z_OK) && (destlen < xc->maxvalpos)) { fstFwrite(dmem, destlen, 1, xc->handle); } else /* comparison between compressed / decompressed len tells if compressed */ { fstFwrite(xc->curval_mem, xc->maxvalpos, 1, xc->handle); } free(dmem); } } /* * only to be called directly by fst code...otherwise must * be synced up with time changes */ #ifdef FST_WRITER_PARALLEL static void fstWriterFlushContextPrivate2(void *ctx) #else static void fstWriterFlushContextPrivate(void *ctx) #endif { #ifdef FST_DEBUG int cnt = 0; #endif unsigned int i; unsigned char *vchg_mem; FILE *f; off_t fpos, indxpos, endpos; uint32_t prevpos; int zerocnt; unsigned char *scratchpad; unsigned char *scratchpnt; unsigned char *tmem; off_t tlen; off_t unc_memreq = 0; /* for reader */ unsigned char *packmem; unsigned int packmemlen; uint32_t *vm4ip; struct fstWriterContext *xc = (struct fstWriterContext *)ctx; #ifdef FST_WRITER_PARALLEL struct fstWriterContext *xc2 = xc->xc_parent; #else struct fstWriterContext *xc2 = xc; #endif #ifndef FST_DYNAMIC_ALIAS_DISABLE Pvoid_t PJHSArray = (Pvoid_t) NULL; #ifndef _WAVE_HAVE_JUDY uint32_t hashmask = xc->maxhandle; hashmask |= hashmask >> 1; hashmask |= hashmask >> 2; hashmask |= hashmask >> 4; hashmask |= hashmask >> 8; hashmask |= hashmask >> 16; #endif #endif if((xc->vchg_siz <= 1)||(xc->already_in_flush)) return; xc->already_in_flush = 1; /* should really do this with a semaphore */ xc->section_header_only = 0; scratchpad = malloc(xc->vchg_siz); vchg_mem = xc->vchg_mem; f = xc->handle; fstWriterVarint(f, xc->maxhandle); /* emit current number of handles */ fputc(xc->fourpack ? '4' : (xc->fastpack ? 'F' : 'Z'), f); fpos = 1; packmemlen = 1024; /* maintain a running "longest" allocation to */ packmem = malloc(packmemlen); /* prevent continual malloc...free every loop iter */ for(i=0;imaxhandle;i++) { vm4ip = &(xc->valpos_mem[4*i]); if(vm4ip[2]) { uint32_t offs = vm4ip[2]; uint32_t next_offs; unsigned int wrlen; vm4ip[2] = fpos; scratchpnt = scratchpad + xc->vchg_siz; /* build this buffer backwards */ if(vm4ip[1] <= 1) { if(vm4ip[1] == 1) { wrlen = fstGetVarint32Length(vchg_mem + offs + 4); /* used to advance and determine wrlen */ #ifndef FST_REMOVE_DUPLICATE_VC xc->curval_mem[vm4ip[0]] = vchg_mem[offs + 4 + wrlen]; /* checkpoint variable */ #endif while(offs) { unsigned char val; uint32_t time_delta, rcv; next_offs = fstGetUint32(vchg_mem + offs); offs += 4; time_delta = fstGetVarint32(vchg_mem + offs, (int *)&wrlen); val = vchg_mem[offs+wrlen]; offs = next_offs; switch(val) { case '0': case '1': rcv = ((val&1)<<1) | (time_delta<<2); break; /* pack more delta bits in for 0/1 vchs */ case 'x': case 'X': rcv = FST_RCV_X | (time_delta<<4); break; case 'z': case 'Z': rcv = FST_RCV_Z | (time_delta<<4); break; case 'h': case 'H': rcv = FST_RCV_H | (time_delta<<4); break; case 'u': case 'U': rcv = FST_RCV_U | (time_delta<<4); break; case 'w': case 'W': rcv = FST_RCV_W | (time_delta<<4); break; case 'l': case 'L': rcv = FST_RCV_L | (time_delta<<4); break; default: rcv = FST_RCV_D | (time_delta<<4); break; } scratchpnt = fstCopyVarint32ToLeft(scratchpnt, rcv); } } else { /* variable length */ /* fstGetUint32 (next_offs) + fstGetVarint32 (time_delta) + fstGetVarint32 (len) + payload */ unsigned char *pnt; uint32_t record_len; uint32_t time_delta; while(offs) { next_offs = fstGetUint32(vchg_mem + offs); offs += 4; pnt = vchg_mem + offs; offs = next_offs; time_delta = fstGetVarint32(pnt, (int *)&wrlen); pnt += wrlen; record_len = fstGetVarint32(pnt, (int *)&wrlen); pnt += wrlen; scratchpnt -= record_len; memcpy(scratchpnt, pnt, record_len); scratchpnt = fstCopyVarint32ToLeft(scratchpnt, record_len); scratchpnt = fstCopyVarint32ToLeft(scratchpnt, (time_delta << 1)); /* reserve | 1 case for future expansion */ } } } else { wrlen = fstGetVarint32Length(vchg_mem + offs + 4); /* used to advance and determine wrlen */ #ifndef FST_REMOVE_DUPLICATE_VC memcpy(xc->curval_mem + vm4ip[0], vchg_mem + offs + 4 + wrlen, vm4ip[1]); /* checkpoint variable */ #endif while(offs) { unsigned int idx; char is_binary = 1; unsigned char *pnt; uint32_t time_delta; next_offs = fstGetUint32(vchg_mem + offs); offs += 4; time_delta = fstGetVarint32(vchg_mem + offs, (int *)&wrlen); pnt = vchg_mem+offs+wrlen; offs = next_offs; for(idx=0;idxvchg_siz - scratchpnt; unc_memreq += wrlen; if(wrlen > 32) { unsigned long destlen = wrlen; unsigned char *dmem; unsigned int rc; if(!xc->fastpack) { if(wrlen <= packmemlen) { dmem = packmem; } else { free(packmem); dmem = packmem = malloc(compressBound(packmemlen = wrlen)); } rc = compress2(dmem, &destlen, scratchpnt, wrlen, 4); if(rc == Z_OK) { #ifndef FST_DYNAMIC_ALIAS_DISABLE PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, destlen, NULL); if(*pv) { uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, wrlen); fpos += destlen; fstFwrite(dmem, destlen, 1, f); #ifndef FST_DYNAMIC_ALIAS_DISABLE } #endif } else { #ifndef FST_DYNAMIC_ALIAS_DISABLE PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); if(*pv) { uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, 0); fpos += wrlen; fstFwrite(scratchpnt, wrlen, 1, f); #ifndef FST_DYNAMIC_ALIAS_DISABLE } #endif } } else { /* this is extremely conservative: fastlz needs +5% for worst case, lz4 needs siz+(siz/255)+16 */ if(((wrlen * 2) + 2) <= packmemlen) { dmem = packmem; } else { free(packmem); dmem = packmem = malloc(packmemlen = (wrlen * 2) + 2); } rc = (xc->fourpack) ? LZ4_compress((char *)scratchpnt, (char *)dmem, wrlen) : fastlz_compress(scratchpnt, wrlen, dmem); if(rc < destlen) { #ifndef FST_DYNAMIC_ALIAS_DISABLE PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, rc, NULL); if(*pv) { uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, wrlen); fpos += rc; fstFwrite(dmem, rc, 1, f); #ifndef FST_DYNAMIC_ALIAS_DISABLE } #endif } else { #ifndef FST_DYNAMIC_ALIAS_DISABLE PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); if(*pv) { uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, 0); fpos += wrlen; fstFwrite(scratchpnt, wrlen, 1, f); #ifndef FST_DYNAMIC_ALIAS_DISABLE } #endif } } } else { #ifndef FST_DYNAMIC_ALIAS_DISABLE PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); if(*pv) { uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, 0); fpos += wrlen; fstFwrite(scratchpnt, wrlen, 1, f); #ifndef FST_DYNAMIC_ALIAS_DISABLE } #endif } /* vm4ip[3] = 0; ...redundant with clearing below */ #ifdef FST_DEBUG cnt++; #endif } } #ifndef FST_DYNAMIC_ALIAS_DISABLE JudyHSFreeArray(&PJHSArray, NULL); #endif free(packmem); packmem = NULL; /* packmemlen = 0; */ /* scan-build */ prevpos = 0; zerocnt = 0; free(scratchpad); scratchpad = NULL; indxpos = ftello(f); xc->secnum++; #ifndef FST_DYNAMIC_ALIAS2_DISABLE if(1) { uint32_t prev_alias = 0; for(i=0;imaxhandle;i++) { vm4ip = &(xc->valpos_mem[4*i]); if(vm4ip[2]) { if(zerocnt) { fpos += fstWriterVarint(f, (zerocnt << 1)); zerocnt = 0; } if(vm4ip[2] & 0x80000000) { if(vm4ip[2] != prev_alias) { fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1); } else { fpos += fstWriterSVarint(f, (0 << 1) | 1); } } else { fpos += fstWriterSVarint(f, ((vm4ip[2] - prevpos) << 1) | 1); prevpos = vm4ip[2]; } vm4ip[2] = 0; vm4ip[3] = 0; /* clear out tchn idx */ } else { zerocnt++; } } } else #endif { for(i=0;imaxhandle;i++) { vm4ip = &(xc->valpos_mem[4*i]); if(vm4ip[2]) { if(zerocnt) { fpos += fstWriterVarint(f, (zerocnt << 1)); zerocnt = 0; } if(vm4ip[2] & 0x80000000) { fpos += fstWriterVarint(f, 0); /* signal, note that using a *signed* varint would be more efficient than this byte escape! */ fpos += fstWriterVarint(f, (-(int32_t)vm4ip[2])); } else { fpos += fstWriterVarint(f, ((vm4ip[2] - prevpos) << 1) | 1); prevpos = vm4ip[2]; } vm4ip[2] = 0; vm4ip[3] = 0; /* clear out tchn idx */ } else { zerocnt++; } } } if(zerocnt) { /* fpos += */ fstWriterVarint(f, (zerocnt << 1)); /* scan-build */ } #ifdef FST_DEBUG fprintf(stderr, "value chains: %d\n", cnt); #endif xc->vchg_mem[0] = '!'; xc->vchg_siz = 1; endpos = ftello(xc->handle); fstWriterUint64(xc->handle, endpos-indxpos); /* write delta index position at very end of block */ /*emit time changes for block */ fflush(xc->tchn_handle); tlen = ftello(xc->tchn_handle); fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); tmem = fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0); if(tmem) { unsigned long destlen = tlen; unsigned char *dmem = malloc(compressBound(destlen)); int rc = compress2(dmem, &destlen, tmem, tlen, 9); if((rc == Z_OK) && (((off_t)destlen) < tlen)) { fstFwrite(dmem, destlen, 1, xc->handle); } else /* comparison between compressed / decompressed len tells if compressed */ { fstFwrite(tmem, tlen, 1, xc->handle); destlen = tlen; } free(dmem); fstMunmap(tmem, tlen); fstWriterUint64(xc->handle, tlen); /* uncompressed */ fstWriterUint64(xc->handle, destlen); /* compressed */ fstWriterUint64(xc->handle, xc->tchn_cnt); /* number of time items */ } xc->tchn_cnt = xc->tchn_idx = 0; fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); fstFtruncate(fileno(xc->tchn_handle), 0); /* write block trailer */ endpos = ftello(xc->handle); fstWriterFseeko(xc, xc->handle, xc->section_start, SEEK_SET); fstWriterUint64(xc->handle, endpos - xc->section_start); /* write block length */ fstWriterFseeko(xc, xc->handle, 8, SEEK_CUR); /* skip begin time */ fstWriterUint64(xc->handle, xc->curtime); /* write end time for section */ fstWriterUint64(xc->handle, unc_memreq); /* amount of buffer memory required in reader for full traversal */ fflush(xc->handle); fstWriterFseeko(xc, xc->handle, xc->section_start-1, SEEK_SET); /* write out FST_BL_VCDATA over FST_BL_SKIP */ #ifndef FST_DYNAMIC_ALIAS_DISABLE #ifndef FST_DYNAMIC_ALIAS2_DISABLE fputc(FST_BL_VCDATA_DYN_ALIAS2, xc->handle); #else fputc(FST_BL_VCDATA_DYN_ALIAS, xc->handle); #endif #else fputc(FST_BL_VCDATA, xc->handle); #endif fflush(xc->handle); fstWriterFseeko(xc, xc->handle, endpos, SEEK_SET); /* seek to end of file */ xc2->section_header_truncpos = endpos; /* cache in case of need to truncate */ if(xc->dump_size_limit) { if(endpos >= ((off_t)xc->dump_size_limit)) { xc2->skip_writing_section_hdr = 1; xc2->size_limit_locked = 1; xc2->is_initial_time = 1; /* to trick emit value and emit time change */ #ifdef FST_DEBUG fprintf(stderr, "<< dump file size limit reached, stopping dumping >>\n"); #endif } } if(!xc2->skip_writing_section_hdr) { fstWriterEmitSectionHeader(xc); /* emit next section header */ } fflush(xc->handle); xc->already_in_flush = 0; } #ifdef FST_WRITER_PARALLEL static void *fstWriterFlushContextPrivate1(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; fstWriterFlushContextPrivate2(xc); pthread_mutex_unlock(&(xc->xc_parent->mutex)); #ifdef FST_REMOVE_DUPLICATE_VC free(xc->curval_mem); #endif free(xc->valpos_mem); free(xc->vchg_mem); tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); free(xc); return(NULL); } static void fstWriterFlushContextPrivate(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc->parallel_enabled) { struct fstWriterContext *xc2 = malloc(sizeof(struct fstWriterContext)); unsigned int i; pthread_mutex_lock(&xc->mutex); pthread_mutex_unlock(&xc->mutex); xc->xc_parent = xc; memcpy(xc2, xc, sizeof(struct fstWriterContext)); xc2->valpos_mem = malloc(xc->maxhandle * 4 * sizeof(uint32_t)); memcpy(xc2->valpos_mem, xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); /* curval mem is updated in the thread */ #ifdef FST_REMOVE_DUPLICATE_VC xc2->curval_mem = malloc(xc->maxvalpos); memcpy(xc2->curval_mem, xc->curval_mem, xc->maxvalpos); #endif xc->vchg_mem = malloc(xc->vchg_alloc_siz); xc->vchg_mem[0] = '!'; xc->vchg_siz = 1; for(i=0;imaxhandle;i++) { uint32_t *vm4ip = &(xc->valpos_mem[4*i]); vm4ip[2] = 0; /* zero out offset val */ vm4ip[3] = 0; /* zero out last time change val */ } xc->tchn_cnt = xc->tchn_idx = 0; xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam); /* child thread will deallocate file/name */ fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); fstFtruncate(fileno(xc->tchn_handle), 0); xc->section_header_only = 0; xc->secnum++; pthread_mutex_lock(&xc->mutex); pthread_create(&xc->thread, &xc->thread_attr, fstWriterFlushContextPrivate1, xc2); } else { if(xc->parallel_was_enabled) /* conservatively block */ { pthread_mutex_lock(&xc->mutex); pthread_mutex_unlock(&xc->mutex); } xc->xc_parent = xc; fstWriterFlushContextPrivate2(xc); } } #endif /* * queues up a flush context operation */ void fstWriterFlushContext(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { if(xc->tchn_idx > 1) { xc->flush_context_pending = 1; } } } /* * close out FST file */ void fstWriterClose(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; #ifdef FST_WRITER_PARALLEL if(xc) { pthread_mutex_lock(&xc->mutex); pthread_mutex_unlock(&xc->mutex); } #endif if(xc && !xc->already_in_close && !xc->already_in_flush) { unsigned char *tmem; off_t fixup_offs, tlen, hlen; xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */ if(xc->section_header_only && xc->section_header_truncpos && (xc->vchg_siz <= 1) && (!xc->is_initial_time)) { fstFtruncate(fileno(xc->handle), xc->section_header_truncpos); fstWriterFseeko(xc, xc->handle, xc->section_header_truncpos, SEEK_SET); xc->section_header_only = 0; } else { xc->skip_writing_section_hdr = 1; if(!xc->size_limit_locked) { if(xc->is_initial_time) /* simulation time never advanced so mock up the changes as time zero ones */ { fstHandle dupe_idx; fstWriterEmitTimeChange(xc, 0); /* emit some time change just to have one */ for(dupe_idx = 0; dupe_idx < xc->maxhandle; dupe_idx++) /* now clone the values */ { fstWriterEmitValueChange(xc, dupe_idx+1, xc->curval_mem + xc->valpos_mem[4*dupe_idx]); } } fstWriterFlushContextPrivate(xc); #ifdef FST_WRITER_PARALLEL pthread_mutex_lock(&xc->mutex); pthread_mutex_unlock(&xc->mutex); #endif } } fstDestroyMmaps(xc, 1); /* write out geom section */ fflush(xc->geom_handle); tlen = ftello(xc->geom_handle); tmem = fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->geom_handle), 0); if(tmem) { unsigned long destlen = tlen; unsigned char *dmem = malloc(compressBound(destlen)); int rc = compress2(dmem, &destlen, tmem, tlen, 9); if((rc != Z_OK) || (((off_t)destlen) > tlen)) { destlen = tlen; } fixup_offs = ftello(xc->handle); fputc(FST_BL_SKIP, xc->handle); /* temporary tag */ fstWriterUint64(xc->handle, destlen + 24); /* section length */ fstWriterUint64(xc->handle, tlen); /* uncompressed */ /* compressed len is section length - 24 */ fstWriterUint64(xc->handle, xc->maxhandle); /* maxhandle */ fstFwrite((((off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET); fputc(FST_BL_GEOM, xc->handle); /* actual tag */ fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */ fflush(xc->handle); free(dmem); fstMunmap(tmem, tlen); } if(xc->num_blackouts) { uint64_t cur_bl = 0; off_t bpos, eos; uint32_t i; fixup_offs = ftello(xc->handle); fputc(FST_BL_SKIP, xc->handle); /* temporary tag */ bpos = fixup_offs + 1; fstWriterUint64(xc->handle, 0); /* section length */ fstWriterVarint(xc->handle, xc->num_blackouts); for(i=0;inum_blackouts;i++) { fputc(xc->blackout_head->active, xc->handle); fstWriterVarint(xc->handle, xc->blackout_head->tim - cur_bl); cur_bl = xc->blackout_head->tim; xc->blackout_curr = xc->blackout_head->next; free(xc->blackout_head); xc->blackout_head = xc->blackout_curr; } eos = ftello(xc->handle); fstWriterFseeko(xc, xc->handle, bpos, SEEK_SET); fstWriterUint64(xc->handle, eos - bpos); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET); fputc(FST_BL_BLACKOUT, xc->handle); /* actual tag */ fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */ fflush(xc->handle); } if(xc->compress_hier) { off_t hl, eos; gzFile zhandle; int zfd; int fourpack_duo = 0; #ifndef __MINGW32__ char *fnam = malloc(strlen(xc->filename) + 5 + 1); #endif fixup_offs = ftello(xc->handle); fputc(FST_BL_SKIP, xc->handle); /* temporary tag */ hlen = ftello(xc->handle); fstWriterUint64(xc->handle, 0); /* section length */ fstWriterUint64(xc->handle, xc->hier_file_len); /* uncompressed length */ if(!xc->fourpack) { unsigned char *mem = malloc(FST_GZIO_LEN); zfd = dup(fileno(xc->handle)); fflush(xc->handle); zhandle = gzdopen(zfd, "wb4"); if(zhandle) { fstWriterFseeko(xc, xc->hier_handle, 0, SEEK_SET); for(hl = 0; hl < xc->hier_file_len; hl += FST_GZIO_LEN) { unsigned len = ((xc->hier_file_len - hl) > FST_GZIO_LEN) ? FST_GZIO_LEN : (xc->hier_file_len - hl); fstFread(mem, len, 1, xc->hier_handle); gzwrite(zhandle, mem, len); } gzclose(zhandle); } else { close(zfd); } free(mem); } else { int lz4_maxlen; unsigned char *mem; unsigned char *hmem; int packed_len; fflush(xc->handle); lz4_maxlen = LZ4_compressBound(xc->hier_file_len); mem = malloc(lz4_maxlen); hmem = fstMmap(NULL, xc->hier_file_len, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->hier_handle), 0); packed_len = LZ4_compress((char *)hmem, (char *)mem, xc->hier_file_len); fstMunmap(hmem, xc->hier_file_len); fourpack_duo = (!xc->repack_on_close) && (xc->hier_file_len > FST_HDR_FOURPACK_DUO_SIZE); /* double pack when hierarchy is large */ if(fourpack_duo) /* double packing with LZ4 is faster than gzip */ { unsigned char *mem_duo; int lz4_maxlen_duo; int packed_len_duo; lz4_maxlen_duo = LZ4_compressBound(packed_len); mem_duo = malloc(lz4_maxlen_duo); packed_len_duo = LZ4_compress((char *)mem, (char *)mem_duo, packed_len); fstWriterVarint(xc->handle, packed_len); /* 1st round compressed length */ fstFwrite(mem_duo, packed_len_duo, 1, xc->handle); free(mem_duo); } else { fstFwrite(mem, packed_len, 1, xc->handle); } free(mem); } fstWriterFseeko(xc, xc->handle, 0, SEEK_END); eos = ftello(xc->handle); fstWriterFseeko(xc, xc->handle, hlen, SEEK_SET); fstWriterUint64(xc->handle, eos - hlen); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET); fputc(xc->fourpack ? ( fourpack_duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4) : FST_BL_HIER, xc->handle); /* actual tag now also == compression type */ fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */ fflush(xc->handle); #ifndef __MINGW32__ sprintf(fnam, "%s.hier", xc->filename); unlink(fnam); free(fnam); #endif } /* finalize out header */ fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_START_TIME, SEEK_SET); fstWriterUint64(xc->handle, xc->firsttime); fstWriterUint64(xc->handle, xc->curtime); fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_NUM_SCOPES, SEEK_SET); fstWriterUint64(xc->handle, xc->numscopes); fstWriterUint64(xc->handle, xc->numsigs); fstWriterUint64(xc->handle, xc->maxhandle); fstWriterUint64(xc->handle, xc->secnum); fflush(xc->handle); tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); free(xc->vchg_mem); xc->vchg_mem = NULL; tmpfile_close(&xc->curval_handle, &xc->curval_handle_nam); tmpfile_close(&xc->valpos_handle, &xc->valpos_handle_nam); tmpfile_close(&xc->geom_handle, &xc->geom_handle_nam); if(xc->hier_handle) { fclose(xc->hier_handle); xc->hier_handle = NULL; } if(xc->handle) { if(xc->repack_on_close) { FILE *fp; off_t offpnt, uclen; int flen = strlen(xc->filename); char *hf = calloc(1, flen + 5); strcpy(hf, xc->filename); strcpy(hf+flen, ".pak"); fp = fopen(hf, "wb"); if(fp) { void *dsth; int zfd; char gz_membuf[FST_GZIO_LEN]; fstWriterFseeko(xc, xc->handle, 0, SEEK_END); uclen = ftello(xc->handle); fputc(FST_BL_ZWRAPPER, fp); fstWriterUint64(fp, 0); fstWriterUint64(fp, uclen); fflush(fp); fstWriterFseeko(xc, xc->handle, 0, SEEK_SET); zfd = dup(fileno(fp)); dsth = gzdopen(zfd, "wb4"); if(dsth) { for(offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) { size_t this_len = ((uclen - offpnt) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - offpnt); fstFread(gz_membuf, this_len, 1, xc->handle); gzwrite(dsth, gz_membuf, this_len); } gzclose(dsth); } else { close(zfd); } fstWriterFseeko(xc, fp, 0, SEEK_END); offpnt = ftello(fp); fstWriterFseeko(xc, fp, 1, SEEK_SET); fstWriterUint64(fp, offpnt - 1); fclose(fp); fclose(xc->handle); xc->handle = NULL; unlink(xc->filename); rename(hf, xc->filename); } else { xc->repack_on_close = 0; fclose(xc->handle); xc->handle = NULL; } free(hf); } else { fclose(xc->handle); xc->handle = NULL; } } #ifdef __MINGW32__ { int flen = strlen(xc->filename); char *hf = calloc(1, flen + 6); strcpy(hf, xc->filename); if(xc->compress_hier) { strcpy(hf + flen, ".hier"); unlink(hf); /* no longer needed as a section now exists for this */ } free(hf); } #endif #ifdef FST_WRITER_PARALLEL pthread_mutex_destroy(&xc->mutex); pthread_attr_destroy(&xc->thread_attr); #endif if(xc->path_array) { #ifndef _WAVE_HAVE_JUDY const uint32_t hashmask = FST_PATH_HASHMASK; #endif JudyHSFreeArray(&(xc->path_array), NULL); } free(xc->filename); xc->filename = NULL; free(xc); } } /* * functions to set miscellaneous header/block information */ void fstWriterSetDate(void *ctx, const char *dat) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { char s[FST_HDR_DATE_SIZE]; off_t fpos = ftello(xc->handle); int len = strlen(dat); fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_DATE, SEEK_SET); memset(s, 0, FST_HDR_DATE_SIZE); memcpy(s, dat, (len < FST_HDR_DATE_SIZE) ? len : FST_HDR_DATE_SIZE); fstFwrite(s, FST_HDR_DATE_SIZE, 1, xc->handle); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); } } void fstWriterSetVersion(void *ctx, const char *vers) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc && vers) { char s[FST_HDR_SIM_VERSION_SIZE]; off_t fpos = ftello(xc->handle); int len = strlen(vers); fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_SIM_VERSION, SEEK_SET); memset(s, 0, FST_HDR_SIM_VERSION_SIZE); memcpy(s, vers, (len < FST_HDR_SIM_VERSION_SIZE) ? len : FST_HDR_SIM_VERSION_SIZE); fstFwrite(s, FST_HDR_SIM_VERSION_SIZE, 1, xc->handle); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); } } void fstWriterSetFileType(void *ctx, enum fstFileType filetype) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { if(/*(filetype >= FST_FT_MIN) &&*/ (filetype <= FST_FT_MAX)) { off_t fpos = ftello(xc->handle); xc->filetype = filetype; fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_FILETYPE, SEEK_SET); fputc(xc->filetype, xc->handle); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); } } } static void fstWriterSetAttrDoubleArgGeneric(void *ctx, int typ, uint64_t arg1, uint64_t arg2) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { unsigned char buf[11]; /* ceil(64/7) = 10 + null term */ unsigned char *pnt = fstCopyVarint64ToRight(buf, arg1); if(arg1) { *pnt = 0; /* this converts any *nonzero* arg1 when made a varint into a null-term string */ } fstWriterSetAttrBegin(xc, FST_AT_MISC, typ, (char *)buf, arg2); } } static void fstWriterSetAttrGeneric(void *ctx, const char *comm, int typ, uint64_t arg) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc && comm) { char *s = strdup(comm); char *sf = s; while(*s) { if((*s == '\n') || (*s == '\r')) *s = ' '; s++; } fstWriterSetAttrBegin(xc, FST_AT_MISC, typ, sf, arg); free(sf); } } static void fstWriterSetSourceStem_2(void *ctx, const char *path, unsigned int line, unsigned int use_realpath, int typ) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc && path && path[0]) { uint64_t sidx = 0; int slen = strlen(path); #ifndef _WAVE_HAVE_JUDY const uint32_t hashmask = FST_PATH_HASHMASK; const unsigned char *path2 = (const unsigned char *)path; PPvoid_t pv; #else char *path2 = alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */ PPvoid_t pv; strcpy(path2, path); #endif pv = JudyHSIns(&(xc->path_array), path2, slen, NULL); if(*pv) { sidx = (intptr_t)(*pv); } else { char *rp = NULL; sidx = ++xc->path_array_count; *pv = (void *)(intptr_t)(xc->path_array_count); if(use_realpath) { rp = fstRealpath( #ifndef _WAVE_HAVE_JUDY (const char *) #endif path2, NULL); } fstWriterSetAttrGeneric(xc, rp ? rp : #ifndef _WAVE_HAVE_JUDY (const char *) #endif path2, FST_MT_PATHNAME, sidx); if(rp) { free(rp); } } fstWriterSetAttrDoubleArgGeneric(xc, typ, sidx, line); } } void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath) { fstWriterSetSourceStem_2(ctx, path, line, use_realpath, FST_MT_SOURCESTEM); } void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath) { fstWriterSetSourceStem_2(ctx, path, line, use_realpath, FST_MT_SOURCEISTEM); } void fstWriterSetComment(void *ctx, const char *comm) { fstWriterSetAttrGeneric(ctx, comm, FST_MT_COMMENT, 0); } void fstWriterSetValueList(void *ctx, const char *vl) { fstWriterSetAttrGeneric(ctx, vl, FST_MT_VALUELIST, 0); } void fstWriterSetEnvVar(void *ctx, const char *envvar) { fstWriterSetAttrGeneric(ctx, envvar, FST_MT_ENVVAR, 0); } void fstWriterSetTimescale(void *ctx, int ts) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { off_t fpos = ftello(xc->handle); fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMESCALE, SEEK_SET); fputc(ts & 255, xc->handle); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); } } void fstWriterSetTimescaleFromString(void *ctx, const char *s) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc && s) { int mat = 0; int seconds_exp = -9; int tv = atoi(s); const char *pnt = s; while(*pnt) { switch(*pnt) { case 'm': seconds_exp = -3; mat = 1; break; case 'u': seconds_exp = -6; mat = 1; break; case 'n': seconds_exp = -9; mat = 1; break; case 'p': seconds_exp = -12; mat = 1; break; case 'f': seconds_exp = -15; mat = 1; break; case 'a': seconds_exp = -18; mat = 1; break; case 'z': seconds_exp = -21; mat = 1; break; case 's': seconds_exp = 0; mat = 1; break; default: break; } if(mat) break; pnt++; } if(tv == 10) { seconds_exp++; } else if(tv == 100) { seconds_exp+=2; } fstWriterSetTimescale(ctx, seconds_exp); } } void fstWriterSetTimezero(void *ctx, int64_t tim) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { off_t fpos = ftello(xc->handle); fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMEZERO, SEEK_SET); fstWriterUint64(xc->handle, (xc->timezero = tim)); fflush(xc->handle); fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); } } void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { xc->fastpack = (typ != FST_WR_PT_ZLIB); xc->fourpack = (typ == FST_WR_PT_LZ4); } } void fstWriterSetRepackOnClose(void *ctx, int enable) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { xc->repack_on_close = (enable != 0); } } void fstWriterSetParallelMode(void *ctx, int enable) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { xc->parallel_was_enabled |= xc->parallel_enabled; /* make sticky */ xc->parallel_enabled = (enable != 0); #ifndef FST_WRITER_PARALLEL if(xc->parallel_enabled) { fprintf(stderr, "ERROR: fstWriterSetParallelMode(), FST_WRITER_PARALLEL not enabled during compile, exiting.\n"); exit(255); } #endif } } void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { xc->dump_size_limit = numbytes; } } int fstWriterGetDumpSizeLimitReached(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { return(xc->size_limit_locked != 0); } return(0); } int fstWriterGetFseekFailed(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { return(xc->fseek_failed != 0); } return(0); } /* * writer attr/scope/var creation: * fstWriterCreateVar2() is used to dump VHDL or other languages, but the * underlying variable needs to map to Verilog/SV via the proper fstVarType vt */ fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt, enum fstSupplementalDataType sdt) { fstWriterSetAttrGeneric(ctx, type ? type : "", FST_MT_SUPVAR, (svt<valpos_mem) { fstDestroyMmaps(xc, 0); } fputc(vt, xc->hier_handle); fputc(vd, xc->hier_handle); nlen = strlen(nam); fstFwrite(nam, nlen, 1, xc->hier_handle); fputc(0, xc->hier_handle); xc->hier_file_len += (nlen+3); if((vt == FST_VT_VCD_REAL) || (vt == FST_VT_VCD_REAL_PARAMETER) || (vt == FST_VT_VCD_REALTIME) || (vt == FST_VT_SV_SHORTREAL)) { is_real = 1; len = 8; /* recast number of bytes to that of what a double is */ } else { is_real = 0; if(vt == FST_VT_GEN_STRING) { len = 0; } } xc->hier_file_len += fstWriterVarint(xc->hier_handle, len); if(aliasHandle > xc->maxhandle) aliasHandle = 0; xc->hier_file_len += fstWriterVarint(xc->hier_handle, aliasHandle); xc->numsigs++; if(xc->numsigs == xc->next_huge_break) { if(xc->fst_break_size < xc->fst_huge_break_size) { xc->next_huge_break += FST_ACTIVATE_HUGE_INC; xc->fst_break_size += xc->fst_orig_break_size; xc->fst_break_add_size += xc->fst_orig_break_add_size; xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size; if(xc->vchg_mem) { xc->vchg_mem = realloc(xc->vchg_mem, xc->vchg_alloc_siz); } } } if(!aliasHandle) { uint32_t zero = 0; if(len) { fstWriterVarint(xc->geom_handle, !is_real ? len : 0); /* geom section encodes reals as zero byte */ } else { fstWriterVarint(xc->geom_handle, 0xFFFFFFFF); /* geom section encodes zero len as 32b -1 */ } fstFwrite(&xc->maxvalpos, sizeof(uint32_t), 1, xc->valpos_handle); fstFwrite(&len, sizeof(uint32_t), 1, xc->valpos_handle); fstFwrite(&zero, sizeof(uint32_t), 1, xc->valpos_handle); fstFwrite(&zero, sizeof(uint32_t), 1, xc->valpos_handle); if(!is_real) { for(i=0;icurval_handle); } } else { fstFwrite(&xc->nan, 8, 1, xc->curval_handle); /* initialize doubles to NaN rather than x */ } xc->maxvalpos+=len; xc->maxhandle++; return(xc->maxhandle); } else { return(aliasHandle); } } return(0); } void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { fputc(FST_ST_VCD_SCOPE, xc->hier_handle); if(/*(scopetype < FST_ST_VCD_MODULE) ||*/ (scopetype > FST_ST_MAX)) { scopetype = FST_ST_VCD_MODULE; } fputc(scopetype, xc->hier_handle); fprintf(xc->hier_handle, "%s%c%s%c", scopename ? scopename : "", 0, scopecomp ? scopecomp : "", 0); if(scopename) { xc->hier_file_len += strlen(scopename); } if(scopecomp) { xc->hier_file_len += strlen(scopecomp); } xc->hier_file_len += 4; /* FST_ST_VCD_SCOPE + scopetype + two string terminating zeros */ xc->numscopes++; } } void fstWriterSetUpscope(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { fputc(FST_ST_VCD_UPSCOPE, xc->hier_handle); xc->hier_file_len++; } } void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { fputc(FST_ST_GEN_ATTRBEGIN, xc->hier_handle); if(/*(attrtype < FST_AT_MISC) ||*/ (attrtype > FST_AT_MAX)) { attrtype = FST_AT_MISC; subtype = FST_MT_UNKNOWN; } fputc(attrtype, xc->hier_handle); switch(attrtype) { case FST_AT_ARRAY: if((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX)) subtype = FST_AR_NONE; break; case FST_AT_ENUM: if((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX)) subtype = FST_EV_SV_INTEGER; break; case FST_AT_PACK: if((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX)) subtype = FST_PT_NONE; break; case FST_AT_MISC: default: break; } fputc(subtype, xc->hier_handle); fprintf(xc->hier_handle, "%s%c", attrname ? attrname : "", 0); if(attrname) { xc->hier_file_len += strlen(attrname); } xc->hier_file_len += 4; /* FST_ST_GEN_ATTRBEGIN + type + subtype + string terminating zero */ xc->hier_file_len += fstWriterVarint(xc->hier_handle, arg); } } void fstWriterSetAttrEnd(void *ctx) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { fputc(FST_ST_GEN_ATTREND, xc->hier_handle); xc->hier_file_len++; } } /* * value and time change emission */ void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; const unsigned char *buf = (const unsigned char *)val; uint32_t offs; int len; if((xc) && (handle <= xc->maxhandle)) { uint32_t fpos; uint32_t *vm4ip; if(!xc->valpos_mem) { xc->vc_emitted = 1; fstWriterCreateMmaps(xc); } handle--; /* move starting at 1 index to starting at 0 */ vm4ip = &(xc->valpos_mem[4*handle]); len = vm4ip[1]; if(len) /* len of zero = variable length, use fstWriterEmitVariableLengthValueChange */ { if(!xc->is_initial_time) { fpos = xc->vchg_siz; if((fpos + len + 10) > xc->vchg_alloc_siz) { xc->vchg_alloc_siz += (xc->fst_break_add_size + len); /* +len added in the case of extremely long vectors and small break add sizes */ xc->vchg_mem = realloc(xc->vchg_mem, xc->vchg_alloc_siz); if(!xc->vchg_mem) { fprintf(stderr, "FATAL ERROR, could not realloc() in fstWriterEmitValueChange, exiting.\n"); exit(255); } } #ifdef FST_REMOVE_DUPLICATE_VC offs = vm4ip[0]; if(len != 1) { if((vm4ip[3]==xc->tchn_idx)&&(vm4ip[2])) { unsigned char *old_value = xc->vchg_mem + vm4ip[2] + 4; /* the +4 skips old vm4ip[2] value */ while(*(old_value++) & 0x80) { /* skips over varint encoded "xc->tchn_idx - vm4ip[3]" */ } memcpy(old_value, buf, len); /* overlay new value */ memcpy(xc->curval_mem + offs, buf, len); return; } else { if(!memcmp(xc->curval_mem + offs, buf, len)) { if(!xc->curtime) { int i; for(i=0;icurval_mem + offs, buf, len); } else { if((vm4ip[3]==xc->tchn_idx)&&(vm4ip[2])) { unsigned char *old_value = xc->vchg_mem + vm4ip[2] + 4; /* the +4 skips old vm4ip[2] value */ while(*(old_value++) & 0x80) { /* skips over varint encoded "xc->tchn_idx - vm4ip[3]" */ } *old_value = *buf; /* overlay new value */ *(xc->curval_mem + offs) = *buf; return; } else { if((*(xc->curval_mem + offs)) == (*buf)) { if(!xc->curtime) { if(*buf != 'x') return; } else { return; } } } *(xc->curval_mem + offs) = *buf; } #endif xc->vchg_siz += fstWriterUint32WithVarint32(xc, &vm4ip[2], xc->tchn_idx - vm4ip[3], buf, len); /* do one fwrite op only */ vm4ip[3] = xc->tchn_idx; vm4ip[2] = fpos; } else { offs = vm4ip[0]; memcpy(xc->curval_mem + offs, buf, len); } } } } void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; const unsigned char *buf = (const unsigned char *)val; if((xc) && (handle <= xc->maxhandle)) { uint32_t fpos; uint32_t *vm4ip; if(!xc->valpos_mem) { xc->vc_emitted = 1; fstWriterCreateMmaps(xc); } handle--; /* move starting at 1 index to starting at 0 */ vm4ip = &(xc->valpos_mem[4*handle]); /* there is no initial time dump for variable length value changes */ if(!vm4ip[1]) /* len of zero = variable length */ { fpos = xc->vchg_siz; if((fpos + len + 10 + 5) > xc->vchg_alloc_siz) { xc->vchg_alloc_siz += (xc->fst_break_add_size + len + 5); /* +len added in the case of extremely long vectors and small break add sizes */ xc->vchg_mem = realloc(xc->vchg_mem, xc->vchg_alloc_siz); if(!xc->vchg_mem) { fprintf(stderr, "FATAL ERROR, could not realloc() in fstWriterEmitVariableLengthValueChange, exiting.\n"); exit(255); } } xc->vchg_siz += fstWriterUint32WithVarint32AndLength(xc, &vm4ip[2], xc->tchn_idx - vm4ip[3], buf, len); /* do one fwrite op only */ vm4ip[3] = xc->tchn_idx; vm4ip[2] = fpos; } } } void fstWriterEmitTimeChange(void *ctx, uint64_t tim) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; unsigned int i; int skip = 0; if(xc) { if(xc->is_initial_time) { if(xc->size_limit_locked) /* this resets xc->is_initial_time to one */ { return; } if(!xc->valpos_mem) { fstWriterCreateMmaps(xc); } skip = 1; xc->firsttime = (xc->vc_emitted) ? 0: tim; xc->curtime = 0; xc->vchg_mem[0] = '!'; xc->vchg_siz = 1; fstWriterEmitSectionHeader(xc); for(i=0;imaxhandle;i++) { xc->valpos_mem[4*i+2] = 0; /* zero out offset val */ xc->valpos_mem[4*i+3] = 0; /* zero out last time change val */ } xc->is_initial_time = 0; } else { if((xc->vchg_siz >= xc->fst_break_size) || (xc->flush_context_pending)) { xc->flush_context_pending = 0; fstWriterFlushContextPrivate(xc); xc->tchn_cnt++; fstWriterVarint(xc->tchn_handle, xc->curtime); } } if(!skip) { xc->tchn_idx++; } fstWriterVarint(xc->tchn_handle, tim - xc->curtime); xc->tchn_cnt++; xc->curtime = tim; } } void fstWriterEmitDumpActive(void *ctx, int enable) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { struct fstBlackoutChain *b = calloc(1, sizeof(struct fstBlackoutChain)); b->tim = xc->curtime; b->active = (enable != 0); xc->num_blackouts++; if(xc->blackout_curr) { xc->blackout_curr->next = b; xc->blackout_curr = b; } else { xc->blackout_head = b; xc->blackout_curr = b; } } } /***********************/ /*** ***/ /*** reader function ***/ /*** ***/ /***********************/ /* * private structs */ static const char *vartypes[] = { "event", "integer", "parameter", "real", "real_parameter", "reg", "supply0", "supply1", "time", "tri", "triand", "trior", "trireg", "tri0", "tri1", "wand", "wire", "wor", "port", "sparray", "realtime", "string", "bit", "logic", "int", "shortint", "longint", "byte", "enum", "shortreal" }; static const char *modtypes[] = { "module", "task", "function", "begin", "fork", "generate", "struct", "union", "class", "interface", "package", "program", "vhdl_architecture", "vhdl_procedure", "vhdl_function", "vhdl_record", "vhdl_process", "vhdl_block", "vhdl_for_generate", "vhdl_if_generate", "vhdl_generate", "vhdl_package" }; static const char *attrtypes[] = { "misc", "array", "enum", "class" }; static const char *arraytypes[] = { "none", "unpacked", "packed", "sparse" }; static const char *enumvaluetypes[] = { "integer", "bit", "logic", "int", "shortint", "longint", "byte", "unsigned_integer", "unsigned_bit", "unsigned_logic", "unsigned_int", "unsigned_shortint", "unsigned_longint", "unsigned_byte" }; static const char *packtypes[] = { "none", "unpacked", "packed", "tagged_packed" }; struct fstCurrHier { struct fstCurrHier *prev; void *user_info; int len; }; struct fstReaderContext { /* common entries */ FILE *f, *fh; uint64_t start_time, end_time; uint64_t mem_used_by_writer; uint64_t scope_count; uint64_t var_count; fstHandle maxhandle; uint64_t num_alias; uint64_t vc_section_count; uint32_t *signal_lens; /* maxhandle sized */ unsigned char *signal_typs; /* maxhandle sized */ unsigned char *process_mask; /* maxhandle-based, bitwise sized */ uint32_t longest_signal_value_len; /* longest len value encountered */ unsigned char *temp_signal_value_buf; /* malloced for len in longest_signal_value_len */ signed char timescale; unsigned char filetype; unsigned use_vcd_extensions : 1; unsigned double_endian_match : 1; unsigned native_doubles_for_cb : 1; unsigned contains_geom_section : 1; unsigned contains_hier_section : 1; /* valid for hier_pos */ unsigned contains_hier_section_lz4duo : 1; /* valid for hier_pos (contains_hier_section_lz4 always also set) */ unsigned contains_hier_section_lz4 : 1; /* valid for hier_pos */ unsigned limit_range_valid : 1; /* valid for limit_range_start, limit_range_end */ char version[FST_HDR_SIM_VERSION_SIZE + 1]; char date[FST_HDR_DATE_SIZE + 1]; int64_t timezero; char *filename, *filename_unpacked; off_t hier_pos; uint32_t num_blackouts; uint64_t *blackout_times; unsigned char *blackout_activity; uint64_t limit_range_start, limit_range_end; /* entries specific to read value at time functions */ unsigned rvat_data_valid : 1; uint64_t *rvat_time_table; uint64_t rvat_beg_tim, rvat_end_tim; unsigned char *rvat_frame_data; uint64_t rvat_frame_maxhandle; off_t *rvat_chain_table; uint32_t *rvat_chain_table_lengths; uint64_t rvat_vc_maxhandle; off_t rvat_vc_start; uint32_t *rvat_sig_offs; uint32_t rvat_chain_len; unsigned char *rvat_chain_mem; fstHandle rvat_chain_facidx; uint32_t rvat_chain_pos_tidx; uint32_t rvat_chain_pos_idx; uint64_t rvat_chain_pos_time; unsigned rvat_chain_pos_valid : 1; /* entries specific to hierarchy traversal */ struct fstHier hier; struct fstCurrHier *curr_hier; fstHandle current_handle; char *curr_flat_hier_nam; int flat_hier_alloc_len; unsigned do_rewind : 1; char str_scope_nam[FST_ID_NAM_SIZ+1]; char str_scope_comp[FST_ID_NAM_SIZ+1]; unsigned fseek_failed : 1; /* self-buffered I/O for writes */ #ifndef FST_WRITEX_DISABLE int writex_pos; int writex_fd; unsigned char writex_buf[FST_WRITEX_MAX]; #endif char *f_nam; char *fh_nam; }; int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, off_t offset, int whence) { int rc = fseeko(stream, offset, whence); if(rc<0) { xc->fseek_failed = 1; #ifdef FST_DEBUG fprintf(stderr, "Seek to #%"PRId64" (whence = %d) failed!\n", offset, whence); perror("Why"); #endif } return(rc); } #ifndef FST_WRITEX_DISABLE static void fstWritex(struct fstReaderContext *xc, void *v, int len) { unsigned char *s = (unsigned char *)v; if(len) { if(len < FST_WRITEX_MAX) { if(xc->writex_pos + len >= FST_WRITEX_MAX) { fstWritex(xc, NULL, 0); } memcpy(xc->writex_buf + xc->writex_pos, s, len); xc->writex_pos += len; } else { fstWritex(xc, NULL, 0); if (write(xc->writex_fd, s, len)) { }; } } else { if(xc->writex_pos) { if(write(xc->writex_fd, xc->writex_buf, xc->writex_pos)) { }; xc->writex_pos = 0; } } } #endif /* * scope -> flat name handling */ static void fstReaderDeallocateScopeData(struct fstReaderContext *xc) { struct fstCurrHier *chp; free(xc->curr_flat_hier_nam); xc->curr_flat_hier_nam = NULL; while(xc->curr_hier) { chp = xc->curr_hier->prev; free(xc->curr_hier); xc->curr_hier = chp; } } const char *fstReaderGetCurrentFlatScope(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { return(xc->curr_flat_hier_nam ? xc->curr_flat_hier_nam : ""); } else { return(NULL); } } void *fstReaderGetCurrentScopeUserInfo(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { return(xc->curr_hier ? xc->curr_hier->user_info : NULL); } else { return(NULL); } } const char *fstReaderPopScope(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc && xc->curr_hier) { struct fstCurrHier *ch = xc->curr_hier; if(xc->curr_hier->prev) { xc->curr_flat_hier_nam[xc->curr_hier->prev->len] = 0; } else { *xc->curr_flat_hier_nam = 0; } xc->curr_hier = xc->curr_hier->prev; free(ch); return(xc->curr_flat_hier_nam ? xc->curr_flat_hier_nam : ""); } return(NULL); } void fstReaderResetScope(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { while(fstReaderPopScope(xc)); /* remove any already-built scoping info */ } } const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { struct fstCurrHier *ch = malloc(sizeof(struct fstCurrHier)); int chl = xc->curr_hier ? xc->curr_hier->len : 0; int len = chl + 1 + strlen(nam); if(len >= xc->flat_hier_alloc_len) { xc->curr_flat_hier_nam = xc->curr_flat_hier_nam ? realloc(xc->curr_flat_hier_nam, len+1) : malloc(len+1); } if(chl) { xc->curr_flat_hier_nam[chl] = '.'; strcpy(xc->curr_flat_hier_nam + chl + 1, nam); } else { strcpy(xc->curr_flat_hier_nam, nam); len--; } ch->len = len; ch->prev = xc->curr_hier; ch->user_info = user_info; xc->curr_hier = ch; return(xc->curr_flat_hier_nam); } return(NULL); } int fstReaderGetCurrentScopeLen(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc && xc->curr_hier) { return(xc->curr_hier->len); } return(0); } int fstReaderGetFseekFailed(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { return(xc->fseek_failed != 0); } return(0); } /* * iter mask manipulation util functions */ int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { facidx--; if(facidxmaxhandle) { int process_idx = facidx/8; int process_bit = facidx&7; return( (xc->process_mask[process_idx]&(1<maxhandle) { int idx = facidx/8; int bitpos = facidx&7; xc->process_mask[idx] |= (1<maxhandle) { int idx = facidx/8; int bitpos = facidx&7; xc->process_mask[idx] &= (~(1<process_mask, 0xff, (xc->maxhandle+7)/8); } } void fstReaderClrFacProcessMaskAll(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { memset(xc->process_mask, 0x00, (xc->maxhandle+7)/8); } } /* * various utility read/write functions */ signed char fstReaderGetTimescale(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->timescale : 0); } uint64_t fstReaderGetStartTime(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->start_time : 0); } uint64_t fstReaderGetEndTime(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->end_time : 0); } uint64_t fstReaderGetMemoryUsedByWriter(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->mem_used_by_writer : 0); } uint64_t fstReaderGetScopeCount(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->scope_count : 0); } uint64_t fstReaderGetVarCount(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->var_count : 0); } fstHandle fstReaderGetMaxHandle(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->maxhandle : 0); } uint64_t fstReaderGetAliasCount(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->num_alias : 0); } uint64_t fstReaderGetValueChangeSectionCount(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->vc_section_count : 0); } int fstReaderGetDoubleEndianMatchState(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->double_endian_match : 0); } const char *fstReaderGetVersionString(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->version : NULL); } const char *fstReaderGetDateString(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->date : NULL); } int fstReaderGetFileType(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->filetype : FST_FT_VERILOG); } int64_t fstReaderGetTimezero(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->timezero : 0); } uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; return(xc ? xc->num_blackouts : 0); } uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc && (idx < xc->num_blackouts) && (xc->blackout_times)) { return(xc->blackout_times[idx]); } else { return(0); } } unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc && (idx < xc->num_blackouts) && (xc->blackout_activity)) { return(xc->blackout_activity[idx]); } else { return(0); } } void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { xc->limit_range_valid = 1; xc->limit_range_start = start_time; xc->limit_range_end = end_time; } } void fstReaderSetUnlimitedTimeRange(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { xc->limit_range_valid = 0; } } void fstReaderSetVcdExtensions(void *ctx, int enable) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { xc->use_vcd_extensions = (enable != 0); } } void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { xc->native_doubles_for_cb = (enable != 0); } } /* * hierarchy processing */ static void fstVcdID(char *buf, unsigned int value) { char *pnt = buf; /* zero is illegal for a value...it is assumed they start at one */ while (value) { value--; *(pnt++) = (char)('!' + value % 94); value = value / 94; } *pnt = 0; } static int fstVcdIDForFwrite(char *buf, unsigned int value) { char *pnt = buf; /* zero is illegal for a value...it is assumed they start at one */ while (value) { value--; *(pnt++) = (char)('!' + value % 94); value = value / 94; } return(pnt - buf); } static int fstReaderRecreateHierFile(struct fstReaderContext *xc) { int pass_status = 1; if(!xc->fh) { off_t offs_cache = ftello(xc->f); char *fnam = malloc(strlen(xc->filename) + 6 + 16 + 32 + 1); unsigned char *mem = malloc(FST_GZIO_LEN); off_t hl, uclen; off_t clen = 0; gzFile zhandle = NULL; int zfd; int htyp = FST_BL_SKIP; /* can't handle both set at once should never happen in a real file */ if(!xc->contains_hier_section_lz4 && xc->contains_hier_section) { htyp = FST_BL_HIER; } else if(xc->contains_hier_section_lz4 && !xc->contains_hier_section) { htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4; } sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc); fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET); uclen = fstReaderUint64(xc->f); #ifndef __MINGW32__ fflush(xc->f); #endif if(htyp == FST_BL_HIER) { fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET); uclen = fstReaderUint64(xc->f); #ifndef __MINGW32__ fflush(xc->f); #endif zfd = dup(fileno(xc->f)); zhandle = gzdopen(zfd, "rb"); if(!zhandle) { close(zfd); free(mem); free(fnam); return(0); } } else if((htyp == FST_BL_HIER_LZ4) || (htyp == FST_BL_HIER_LZ4DUO)) { fstReaderFseeko(xc, xc->f, xc->hier_pos - 8, SEEK_SET); /* get section len */ clen = fstReaderUint64(xc->f) - 16; uclen = fstReaderUint64(xc->f); #ifndef __MINGW32__ fflush(xc->f); #endif } #ifndef __MINGW32__ xc->fh = fopen(fnam, "w+b"); if(!xc->fh) #endif { xc->fh = tmpfile_open(&xc->fh_nam); free(fnam); fnam = NULL; if(!xc->fh) { tmpfile_close(&xc->fh, &xc->fh_nam); free(mem); return(0); } } #ifndef __MINGW32__ if(fnam) unlink(fnam); #endif if(htyp == FST_BL_HIER) { for(hl = 0; hl < uclen; hl += FST_GZIO_LEN) { size_t len = ((uclen - hl) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - hl); size_t gzreadlen = gzread(zhandle, mem, len); /* rc should equal len... */ size_t fwlen; if(gzreadlen != len) { pass_status = 0; break; } fwlen = fstFwrite(mem, len, 1, xc->fh); if(fwlen != 1) { pass_status = 0; break; } } gzclose(zhandle); } else if(htyp == FST_BL_HIER_LZ4DUO) { unsigned char *lz4_cmem = malloc(clen); unsigned char *lz4_ucmem = malloc(uclen); unsigned char *lz4_ucmem2; uint64_t uclen2; int skiplen2 = 0; fstFread(lz4_cmem, clen, 1, xc->f); uclen2 = fstGetVarint64(lz4_cmem, &skiplen2); lz4_ucmem2 = malloc(uclen2); pass_status = (uclen2 == (uint64_t)LZ4_decompress_safe_partial ((char *)lz4_cmem + skiplen2, (char *)lz4_ucmem2, clen - skiplen2, uclen2, uclen2)); if(pass_status) { pass_status = (uclen == LZ4_decompress_safe_partial ((char *)lz4_ucmem2, (char *)lz4_ucmem, uclen2, uclen, uclen)); if(fstFwrite(lz4_ucmem, uclen, 1, xc->fh) != 1) { pass_status = 0; } } free(lz4_ucmem2); free(lz4_ucmem); free(lz4_cmem); } else if(htyp == FST_BL_HIER_LZ4) { unsigned char *lz4_cmem = malloc(clen); unsigned char *lz4_ucmem = malloc(uclen); fstFread(lz4_cmem, clen, 1, xc->f); pass_status = (uclen == LZ4_decompress_safe_partial ((char *)lz4_cmem, (char *)lz4_ucmem, clen, uclen, uclen)); if(fstFwrite(lz4_ucmem, uclen, 1, xc->fh) != 1) { pass_status = 0; } free(lz4_ucmem); free(lz4_cmem); } else /* FST_BL_SKIP */ { pass_status = 0; } free(mem); free(fnam); fstReaderFseeko(xc, xc->f, offs_cache, SEEK_SET); } return(pass_status); } int fstReaderIterateHierRewind(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; int pass_status = 0; if(xc) { pass_status = 1; if(!xc->fh) { pass_status = fstReaderRecreateHierFile(xc); } xc->do_rewind = 1; } return(pass_status); } struct fstHier *fstReaderIterateHier(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; int isfeof; fstHandle alias; char *pnt; int ch; if(!xc) return(NULL); if(!xc->fh) { if(!fstReaderRecreateHierFile(xc)) { return(NULL); } } if(xc->do_rewind) { xc->do_rewind = 0; xc->current_handle = 0; fstReaderFseeko(xc, xc->fh, 0, SEEK_SET); clearerr(xc->fh); } if(!(isfeof=feof(xc->fh))) { int tag = fgetc(xc->fh); switch(tag) { case FST_ST_VCD_SCOPE: xc->hier.htyp = FST_HT_SCOPE; xc->hier.u.scope.typ = fgetc(xc->fh); xc->hier.u.scope.name = pnt = xc->str_scope_nam; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* scopename */ *pnt = 0; xc->hier.u.scope.name_length = pnt - xc->hier.u.scope.name; xc->hier.u.scope.component = pnt = xc->str_scope_comp; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* scopecomp */ *pnt = 0; xc->hier.u.scope.component_length = pnt - xc->hier.u.scope.component; break; case FST_ST_VCD_UPSCOPE: xc->hier.htyp = FST_HT_UPSCOPE; break; case FST_ST_GEN_ATTRBEGIN: xc->hier.htyp = FST_HT_ATTRBEGIN; xc->hier.u.attr.typ = fgetc(xc->fh); xc->hier.u.attr.subtype = fgetc(xc->fh); xc->hier.u.attr.name = pnt = xc->str_scope_nam; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* scopename */ *pnt = 0; xc->hier.u.attr.name_length = pnt - xc->hier.u.scope.name; xc->hier.u.attr.arg = fstReaderVarint64(xc->fh); if(xc->hier.u.attr.typ == FST_AT_MISC) { if((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM)||(xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM)) { int sidx_skiplen_dummy = 0; xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy); } } break; case FST_ST_GEN_ATTREND: xc->hier.htyp = FST_HT_ATTREND; break; case FST_VT_VCD_EVENT: case FST_VT_VCD_INTEGER: case FST_VT_VCD_PARAMETER: case FST_VT_VCD_REAL: case FST_VT_VCD_REAL_PARAMETER: case FST_VT_VCD_REG: case FST_VT_VCD_SUPPLY0: case FST_VT_VCD_SUPPLY1: case FST_VT_VCD_TIME: case FST_VT_VCD_TRI: case FST_VT_VCD_TRIAND: case FST_VT_VCD_TRIOR: case FST_VT_VCD_TRIREG: case FST_VT_VCD_TRI0: case FST_VT_VCD_TRI1: case FST_VT_VCD_WAND: case FST_VT_VCD_WIRE: case FST_VT_VCD_WOR: case FST_VT_VCD_PORT: case FST_VT_VCD_SPARRAY: case FST_VT_VCD_REALTIME: case FST_VT_GEN_STRING: case FST_VT_SV_BIT: case FST_VT_SV_LOGIC: case FST_VT_SV_INT: case FST_VT_SV_SHORTINT: case FST_VT_SV_LONGINT: case FST_VT_SV_BYTE: case FST_VT_SV_ENUM: case FST_VT_SV_SHORTREAL: xc->hier.htyp = FST_HT_VAR; xc->hier.u.var.svt_workspace = FST_SVT_NONE; xc->hier.u.var.sdt_workspace = FST_SDT_NONE; xc->hier.u.var.sxt_workspace = 0; xc->hier.u.var.typ = tag; xc->hier.u.var.direction = fgetc(xc->fh); xc->hier.u.var.name = pnt = xc->str_scope_nam; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* varname */ *pnt = 0; xc->hier.u.var.name_length = pnt - xc->hier.u.var.name; xc->hier.u.var.length = fstReaderVarint32(xc->fh); if(tag == FST_VT_VCD_PORT) { xc->hier.u.var.length -= 2; /* removal of delimiting spaces */ xc->hier.u.var.length /= 3; /* port -> signal size adjust */ } alias = fstReaderVarint32(xc->fh); if(!alias) { xc->current_handle++; xc->hier.u.var.handle = xc->current_handle; xc->hier.u.var.is_alias = 0; } else { xc->hier.u.var.handle = alias; xc->hier.u.var.is_alias = 1; } break; default: isfeof = 1; break; } } return(!isfeof ? &xc->hier : NULL); } int fstReaderProcessHier(void *ctx, FILE *fv) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; char *str; char *pnt; int ch, scopetype; int vartype; uint32_t len, alias; /* uint32_t maxvalpos=0; */ unsigned int num_signal_dyn = 65536; int attrtype, subtype; uint64_t attrarg; fstHandle maxhandle_scanbuild; if(!xc) return(0); xc->longest_signal_value_len = 32; /* arbitrarily set at 32...this is much longer than an expanded double */ if(!xc->fh) { if(!fstReaderRecreateHierFile(xc)) { return(0); } } str = malloc(FST_ID_NAM_ATTR_SIZ+1); if(fv) { char time_dimension[2] = {0, 0}; int time_scale = 1; fprintf(fv, "$date\n\t%s\n$end\n", xc->date); fprintf(fv, "$version\n\t%s\n$end\n", xc->version); if(xc->timezero) fprintf(fv, "$timezero\n\t%"PRId64"\n$end\n", xc->timezero); switch(xc->timescale) { case 2: time_scale = 100; time_dimension[0] = 0; break; case 1: time_scale = 10; case 0: time_dimension[0] = 0; break; case -1: time_scale = 100; time_dimension[0] = 'm'; break; case -2: time_scale = 10; case -3: time_dimension[0] = 'm'; break; case -4: time_scale = 100; time_dimension[0] = 'u'; break; case -5: time_scale = 10; case -6: time_dimension[0] = 'u'; break; case -10: time_scale = 100; time_dimension[0] = 'p'; break; case -11: time_scale = 10; case -12: time_dimension[0] = 'p'; break; case -13: time_scale = 100; time_dimension[0] = 'f'; break; case -14: time_scale = 10; case -15: time_dimension[0] = 'f'; break; case -16: time_scale = 100; time_dimension[0] = 'a'; break; case -17: time_scale = 10; case -18: time_dimension[0] = 'a'; break; case -19: time_scale = 100; time_dimension[0] = 'z'; break; case -20: time_scale = 10; case -21: time_dimension[0] = 'z'; break; case -7: time_scale = 100; time_dimension[0] = 'n'; break; case -8: time_scale = 10; case -9: default: time_dimension[0] = 'n'; break; } if(fv) fprintf(fv, "$timescale\n\t%d%ss\n$end\n", time_scale, time_dimension); } xc->maxhandle = 0; xc->num_alias = 0; free(xc->signal_lens); xc->signal_lens = malloc(num_signal_dyn*sizeof(uint32_t)); free(xc->signal_typs); xc->signal_typs = malloc(num_signal_dyn*sizeof(unsigned char)); fstReaderFseeko(xc, xc->fh, 0, SEEK_SET); while(!feof(xc->fh)) { int tag = fgetc(xc->fh); switch(tag) { case FST_ST_VCD_SCOPE: scopetype = fgetc(xc->fh); if((scopetype < FST_ST_MIN) || (scopetype > FST_ST_MAX)) scopetype = FST_ST_VCD_MODULE; pnt = str; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* scopename */ *pnt = 0; while(fgetc(xc->fh)) { }; /* scopecomp */ if(fv) fprintf(fv, "$scope %s %s $end\n", modtypes[scopetype], str); break; case FST_ST_VCD_UPSCOPE: if(fv) fprintf(fv, "$upscope $end\n"); break; case FST_ST_GEN_ATTRBEGIN: attrtype = fgetc(xc->fh); subtype = fgetc(xc->fh); pnt = str; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* attrname */ *pnt = 0; if(!str[0]) { strcpy(str, "\"\""); } attrarg = fstReaderVarint64(xc->fh); if(fv && xc->use_vcd_extensions) { switch(attrtype) { case FST_AT_ARRAY: if((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX)) subtype = FST_AR_NONE; fprintf(fv, "$attrbegin %s %s %s %"PRId64" $end\n", attrtypes[attrtype], arraytypes[subtype], str, attrarg); break; case FST_AT_ENUM: if((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX)) subtype = FST_EV_SV_INTEGER; fprintf(fv, "$attrbegin %s %s %s %"PRId64" $end\n", attrtypes[attrtype], enumvaluetypes[subtype], str, attrarg); break; case FST_AT_PACK: if((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX)) subtype = FST_PT_NONE; fprintf(fv, "$attrbegin %s %s %s %"PRId64" $end\n", attrtypes[attrtype], packtypes[subtype], str, attrarg); break; case FST_AT_MISC: default: attrtype = FST_AT_MISC; if(subtype == FST_MT_COMMENT) { fprintf(fv, "$comment\n\t%s\n$end\n", str); } else { if((subtype == FST_MT_SOURCESTEM)||(subtype == FST_MT_SOURCEISTEM)) { int sidx_skiplen_dummy = 0; uint64_t sidx = fstGetVarint64((unsigned char *)str, &sidx_skiplen_dummy); fprintf(fv, "$attrbegin %s %02x %"PRId64" %"PRId64" $end\n", attrtypes[attrtype], subtype, sidx, attrarg); } else { fprintf(fv, "$attrbegin %s %02x %s %"PRId64" $end\n", attrtypes[attrtype], subtype, str, attrarg); } } break; } } break; case FST_ST_GEN_ATTREND: if(fv && xc->use_vcd_extensions) fprintf(fv, "$attrend $end\n"); break; case FST_VT_VCD_EVENT: case FST_VT_VCD_INTEGER: case FST_VT_VCD_PARAMETER: case FST_VT_VCD_REAL: case FST_VT_VCD_REAL_PARAMETER: case FST_VT_VCD_REG: case FST_VT_VCD_SUPPLY0: case FST_VT_VCD_SUPPLY1: case FST_VT_VCD_TIME: case FST_VT_VCD_TRI: case FST_VT_VCD_TRIAND: case FST_VT_VCD_TRIOR: case FST_VT_VCD_TRIREG: case FST_VT_VCD_TRI0: case FST_VT_VCD_TRI1: case FST_VT_VCD_WAND: case FST_VT_VCD_WIRE: case FST_VT_VCD_WOR: case FST_VT_VCD_PORT: case FST_VT_VCD_SPARRAY: case FST_VT_VCD_REALTIME: case FST_VT_GEN_STRING: case FST_VT_SV_BIT: case FST_VT_SV_LOGIC: case FST_VT_SV_INT: case FST_VT_SV_SHORTINT: case FST_VT_SV_LONGINT: case FST_VT_SV_BYTE: case FST_VT_SV_ENUM: case FST_VT_SV_SHORTREAL: vartype = tag; /* vardir = */ fgetc(xc->fh); /* unused in VCD reader, but need to advance read pointer */ pnt = str; while((ch = fgetc(xc->fh))) { *(pnt++) = ch; }; /* varname */ *pnt = 0; len = fstReaderVarint32(xc->fh); alias = fstReaderVarint32(xc->fh); if(!alias) { if(xc->maxhandle == num_signal_dyn) { num_signal_dyn *= 2; xc->signal_lens = realloc(xc->signal_lens, num_signal_dyn*sizeof(uint32_t)); xc->signal_typs = realloc(xc->signal_typs, num_signal_dyn*sizeof(unsigned char)); } xc->signal_lens[xc->maxhandle] = len; xc->signal_typs[xc->maxhandle] = vartype; /* maxvalpos+=len; */ if(len > xc->longest_signal_value_len) { xc->longest_signal_value_len = len; } if((vartype == FST_VT_VCD_REAL) || (vartype == FST_VT_VCD_REAL_PARAMETER) || (vartype == FST_VT_VCD_REALTIME) || (vartype == FST_VT_SV_SHORTREAL)) { len = (vartype != FST_VT_SV_SHORTREAL) ? 64 : 32; xc->signal_typs[xc->maxhandle] = FST_VT_VCD_REAL; } if(fv) { char vcdid_buf[16]; uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3); fstVcdID(vcdid_buf, xc->maxhandle+1); fprintf(fv, "$var %s %"PRIu32" %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); } xc->maxhandle++; } else { if((vartype == FST_VT_VCD_REAL) || (vartype == FST_VT_VCD_REAL_PARAMETER) || (vartype == FST_VT_VCD_REALTIME) || (vartype == FST_VT_SV_SHORTREAL)) { len = (vartype != FST_VT_SV_SHORTREAL) ? 64 : 32; xc->signal_typs[xc->maxhandle] = FST_VT_VCD_REAL; } if(fv) { char vcdid_buf[16]; uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3); fstVcdID(vcdid_buf, alias); fprintf(fv, "$var %s %"PRIu32" %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); } xc->num_alias++; } break; default: break; } } if(fv) fprintf(fv, "$enddefinitions $end\n"); maxhandle_scanbuild = xc->maxhandle ? xc->maxhandle : 1; /*scan-build warning suppression, in reality we have at least one signal */ xc->signal_lens = realloc(xc->signal_lens, maxhandle_scanbuild*sizeof(uint32_t)); xc->signal_typs = realloc(xc->signal_typs, maxhandle_scanbuild*sizeof(unsigned char)); free(xc->process_mask); xc->process_mask = calloc(1, (maxhandle_scanbuild+7)/8); free(xc->temp_signal_value_buf); xc->temp_signal_value_buf = malloc(xc->longest_signal_value_len + 1); xc->var_count = xc->maxhandle + xc->num_alias; free(str); return(1); } /* * reader file open/close functions */ int fstReaderInit(struct fstReaderContext *xc) { off_t blkpos = 0; off_t endfile; uint64_t seclen; int sectype; uint64_t vc_section_count_actual = 0; int hdr_incomplete = 0; int hdr_seen = 0; int gzread_pass_status = 1; sectype = fgetc(xc->f); if(sectype == FST_BL_ZWRAPPER) { FILE *fcomp; off_t offpnt, uclen; char gz_membuf[FST_GZIO_LEN]; void *zhandle; int zfd; int flen = strlen(xc->filename); char *hf; seclen = fstReaderUint64(xc->f); uclen = fstReaderUint64(xc->f); if(!seclen) return(0); /* not finished compressing, this is a failed read */ hf = calloc(1, flen + 16 + 32 + 1); sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc); fcomp = fopen(hf, "w+b"); if(!fcomp) { fcomp = tmpfile_open(&xc->f_nam); free(hf); hf = NULL; if(!fcomp) { tmpfile_close(&fcomp, &xc->f_nam); return(0); } } #if defined(FST_MACOSX) setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */ #endif #ifdef __MINGW32__ setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */ xc->filename_unpacked = hf; #else if(hf) { unlink(hf); free(hf); } #endif fstReaderFseeko(xc, xc->f, 1+8+8, SEEK_SET); #ifndef __MINGW32__ fflush(xc->f); #endif zfd = dup(fileno(xc->f)); zhandle = gzdopen(zfd, "rb"); if(zhandle) { for(offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) { size_t this_len = ((uclen - offpnt) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - offpnt); size_t gzreadlen = gzread(zhandle, gz_membuf, this_len); size_t fwlen; if(gzreadlen != this_len) { gzread_pass_status = 0; break; } fwlen = fstFwrite(gz_membuf, this_len, 1, fcomp); if(fwlen != 1) { gzread_pass_status = 0; break; } } gzclose(zhandle); } else { close(zfd); } fflush(fcomp); fclose(xc->f); xc->f = fcomp; } if(gzread_pass_status) { fstReaderFseeko(xc, xc->f, 0, SEEK_END); endfile = ftello(xc->f); while(blkpos < endfile) { fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET); sectype = fgetc(xc->f); seclen = fstReaderUint64(xc->f); if(sectype == EOF) { break; } if((hdr_incomplete) && (!seclen)) { break; } if(!hdr_seen && (sectype != FST_BL_HDR)) { break; } blkpos++; if(sectype == FST_BL_HDR) { if(!hdr_seen) { int ch; double dcheck; xc->start_time = fstReaderUint64(xc->f); xc->end_time = fstReaderUint64(xc->f); hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0); fstFread(&dcheck, 8, 1, xc->f); xc->double_endian_match = (dcheck == FST_DOUBLE_ENDTEST); if(!xc->double_endian_match) { union { unsigned char rvs_buf[8]; double d; } vu; unsigned char *dcheck_alias = (unsigned char *)&dcheck; int rvs_idx; for(rvs_idx=0;rvs_idx<8;rvs_idx++) { vu.rvs_buf[rvs_idx] = dcheck_alias[7-rvs_idx]; } if(vu.d != FST_DOUBLE_ENDTEST) { break; /* either corrupt file or wrong architecture (offset +33 also functions as matchword) */ } } hdr_seen = 1; xc->mem_used_by_writer = fstReaderUint64(xc->f); xc->scope_count = fstReaderUint64(xc->f); xc->var_count = fstReaderUint64(xc->f); xc->maxhandle = fstReaderUint64(xc->f); xc->num_alias = xc->var_count - xc->maxhandle; xc->vc_section_count = fstReaderUint64(xc->f); ch = fgetc(xc->f); xc->timescale = (signed char)ch; fstFread(xc->version, FST_HDR_SIM_VERSION_SIZE, 1, xc->f); xc->version[FST_HDR_SIM_VERSION_SIZE] = 0; fstFread(xc->date, FST_HDR_DATE_SIZE, 1, xc->f); xc->date[FST_HDR_DATE_SIZE] = 0; ch = fgetc(xc->f); xc->filetype = (unsigned char)ch; xc->timezero = fstReaderUint64(xc->f); } } else if((sectype == FST_BL_VCDATA) || (sectype == FST_BL_VCDATA_DYN_ALIAS) || (sectype == FST_BL_VCDATA_DYN_ALIAS2)) { if(hdr_incomplete) { uint64_t bt = fstReaderUint64(xc->f); xc->end_time = fstReaderUint64(xc->f); if(!vc_section_count_actual) { xc->start_time = bt; } } vc_section_count_actual++; } else if(sectype == FST_BL_GEOM) { if(!hdr_incomplete) { uint64_t clen = seclen - 24; uint64_t uclen = fstReaderUint64(xc->f); unsigned char *ucdata = malloc(uclen); unsigned char *pnt = ucdata; unsigned int i; xc->contains_geom_section = 1; xc->maxhandle = fstReaderUint64(xc->f); xc->longest_signal_value_len = 32; /* arbitrarily set at 32...this is much longer than an expanded double */ free(xc->process_mask); xc->process_mask = calloc(1, (xc->maxhandle+7)/8); if(clen != uclen) { unsigned char *cdata = malloc(clen); unsigned long destlen = uclen; unsigned long sourcelen = clen; int rc; fstFread(cdata, clen, 1, xc->f); rc = uncompress(ucdata, &destlen, cdata, sourcelen); if(rc != Z_OK) { printf("geom uncompress rc = %d\n", rc); exit(255); } free(cdata); } else { fstFread(ucdata, uclen, 1, xc->f); } free(xc->signal_lens); xc->signal_lens = malloc(sizeof(uint32_t) * xc->maxhandle); free(xc->signal_typs); xc->signal_typs = malloc(sizeof(unsigned char) * xc->maxhandle); for(i=0;imaxhandle;i++) { int skiplen; uint64_t val = fstGetVarint32(pnt, &skiplen); pnt += skiplen; if(val) { xc->signal_lens[i] = (val != 0xFFFFFFFF) ? val : 0; xc->signal_typs[i] = FST_VT_VCD_WIRE; if(xc->signal_lens[i] > xc->longest_signal_value_len) { xc->longest_signal_value_len = xc->signal_lens[i]; } } else { xc->signal_lens[i] = 8; /* backpatch in real */ xc->signal_typs[i] = FST_VT_VCD_REAL; /* xc->longest_signal_value_len handled above by overly large init size */ } } free(xc->temp_signal_value_buf); xc->temp_signal_value_buf = malloc(xc->longest_signal_value_len + 1); free(ucdata); } } else if(sectype == FST_BL_HIER) { xc->contains_hier_section = 1; xc->hier_pos = ftello(xc->f); } else if(sectype == FST_BL_HIER_LZ4DUO) { xc->contains_hier_section_lz4 = 1; xc->contains_hier_section_lz4duo = 1; xc->hier_pos = ftello(xc->f); } else if(sectype == FST_BL_HIER_LZ4) { xc->contains_hier_section_lz4 = 1; xc->hier_pos = ftello(xc->f); } else if(sectype == FST_BL_BLACKOUT) { uint32_t i; uint64_t cur_bl = 0; uint64_t delta; xc->num_blackouts = fstReaderVarint32(xc->f); free(xc->blackout_times); xc->blackout_times = calloc(xc->num_blackouts, sizeof(uint64_t)); free(xc->blackout_activity); xc->blackout_activity = calloc(xc->num_blackouts, sizeof(unsigned char)); for(i=0;inum_blackouts;i++) { xc->blackout_activity[i] = fgetc(xc->f) != 0; delta = fstReaderVarint64(xc->f); cur_bl += delta; xc->blackout_times[i] = cur_bl; } } blkpos += seclen; if(!hdr_seen) break; } if(hdr_seen) { if(xc->vc_section_count != vc_section_count_actual) { xc->vc_section_count = vc_section_count_actual; } if(!xc->contains_geom_section) { fstReaderProcessHier(xc, NULL); /* recreate signal_lens/signal_typs info */ } } } return(hdr_seen); } void *fstReaderOpenForUtilitiesOnly(void) { struct fstReaderContext *xc = calloc(1, sizeof(struct fstReaderContext)); return(xc); } void *fstReaderOpen(const char *nam) { struct fstReaderContext *xc = calloc(1, sizeof(struct fstReaderContext)); if((!nam)||(!(xc->f=fopen(nam, "rb")))) { free(xc); xc=NULL; } else { int flen = strlen(nam); char *hf = calloc(1, flen + 6); int rc; #if defined(__MINGW32__) || defined(FST_MACOSX) setvbuf(xc->f, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */ #endif memcpy(hf, nam, flen); strcpy(hf + flen, ".hier"); xc->fh = fopen(hf, "rb"); free(hf); xc->filename = strdup(nam); rc = fstReaderInit(xc); if((rc) && (xc->vc_section_count) && (xc->maxhandle) && ((xc->fh)||(xc->contains_hier_section||(xc->contains_hier_section_lz4)))) { /* more init */ xc->do_rewind = 1; } else { fstReaderClose(xc); xc = NULL; } } return(xc); } static void fstReaderDeallocateRvatData(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { free(xc->rvat_chain_mem); xc->rvat_chain_mem = NULL; free(xc->rvat_frame_data); xc->rvat_frame_data = NULL; free(xc->rvat_time_table); xc->rvat_time_table = NULL; free(xc->rvat_chain_table); xc->rvat_chain_table = NULL; free(xc->rvat_chain_table_lengths); xc->rvat_chain_table_lengths = NULL; xc->rvat_data_valid = 0; } } void fstReaderClose(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { fstReaderDeallocateScopeData(xc); fstReaderDeallocateRvatData(xc); free(xc->rvat_sig_offs); xc->rvat_sig_offs = NULL; free(xc->process_mask); xc->process_mask = NULL; free(xc->blackout_times); xc->blackout_times = NULL; free(xc->blackout_activity); xc->blackout_activity = NULL; free(xc->temp_signal_value_buf); xc->temp_signal_value_buf = NULL; free(xc->signal_typs); xc->signal_typs = NULL; free(xc->signal_lens); xc->signal_lens = NULL; free(xc->filename); xc->filename = NULL; if(xc->fh) { tmpfile_close(&xc->fh, &xc->fh_nam); } if(xc->f) { tmpfile_close(&xc->f, &xc->f_nam); if(xc->filename_unpacked) { unlink(xc->filename_unpacked); free(xc->filename_unpacked); } } free(xc); } } /* * read processing */ /* normal read which re-interleaves the value change data */ int fstReaderIterBlocks(void *ctx, void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value), void *user_callback_data_pointer, FILE *fv) { return(fstReaderIterBlocks2(ctx, value_change_callback, NULL, user_callback_data_pointer, fv)); } int fstReaderIterBlocks2(void *ctx, void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value), void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value, uint32_t len), void *user_callback_data_pointer, FILE *fv) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; uint64_t previous_time = UINT64_MAX; uint64_t *time_table = NULL; uint64_t tsec_nitems; unsigned int secnum = 0; int blocks_skipped = 0; off_t blkpos = 0; uint64_t seclen, beg_tim; #ifdef FST_DEBUG uint64_t end_tim; #endif uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle; off_t vc_start; off_t indx_pntr, indx_pos; off_t *chain_table = NULL; uint32_t *chain_table_lengths = NULL; unsigned char *chain_cmem; unsigned char *pnt; long chain_clen; fstHandle idx, pidx=0, i; uint64_t pval; uint64_t vc_maxhandle_largest = 0; uint64_t tsec_uclen = 0, tsec_clen = 0; int sectype; uint64_t mem_required_for_traversal; unsigned char *mem_for_traversal = NULL; uint32_t traversal_mem_offs; uint32_t *scatterptr, *headptr, *length_remaining; uint32_t cur_blackout = 0; int packtype; unsigned char *mc_mem = NULL; uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */ if(!xc) return(0); scatterptr = calloc(xc->maxhandle, sizeof(uint32_t)); headptr = calloc(xc->maxhandle, sizeof(uint32_t)); length_remaining = calloc(xc->maxhandle, sizeof(uint32_t)); if(fv) { fprintf(fv, "$dumpvars\n"); #ifndef FST_WRITEX_DISABLE fflush(fv); setvbuf(fv, (char *) NULL, _IONBF, 0); /* even buffered IO is slow so disable it and use our own routines that don't need seeking */ xc->writex_fd = fileno(fv); #endif } for(;;) { uint32_t *tc_head = NULL; traversal_mem_offs = 0; fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET); sectype = fgetc(xc->f); seclen = fstReaderUint64(xc->f); if((sectype == EOF) || (sectype == FST_BL_SKIP)) { #ifdef FST_DEBUG fprintf(stderr, "<< EOF >>\n"); #endif break; } blkpos++; if((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) && (sectype != FST_BL_VCDATA_DYN_ALIAS2)) { blkpos += seclen; continue; } if(!seclen) break; beg_tim = fstReaderUint64(xc->f); #ifdef FST_DEBUG end_tim = #endif fstReaderUint64(xc->f); if(xc->limit_range_valid) { if(beg_tim < xc->limit_range_start) { blocks_skipped++; blkpos += seclen; continue; } if(beg_tim > xc->limit_range_end) /* likely the compare in for(i=0;if); mem_for_traversal = malloc(mem_required_for_traversal + 66); /* add in potential fastlz overhead */ #ifdef FST_DEBUG fprintf(stderr, "sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim, (int)end_tim); fprintf(stderr, "\tmem_required_for_traversal: %d\n", (int)mem_required_for_traversal); #endif /* process time block */ { unsigned char *ucdata; unsigned char *cdata; unsigned long destlen /* = tsec_uclen */; /* scan-build */ unsigned long sourcelen /*= tsec_clen */; /* scan-build */ int rc; unsigned char *tpnt; uint64_t tpval; unsigned int ti; if(fstReaderFseeko(xc, xc->f, blkpos + seclen - 24, SEEK_SET) != 0) break; tsec_uclen = fstReaderUint64(xc->f); tsec_clen = fstReaderUint64(xc->f); tsec_nitems = fstReaderUint64(xc->f); #ifdef FST_DEBUG fprintf(stderr, "\ttime section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen, (int)tsec_nitems); #endif if(tsec_clen > seclen) break; /* corrupted tsec_clen: by definition it can't be larger than size of section */ ucdata = malloc(tsec_uclen); if(!ucdata) break; /* malloc fail as tsec_uclen out of range from corrupted file */ destlen = tsec_uclen; sourcelen = tsec_clen; fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR); if(tsec_uclen != tsec_clen) { cdata = malloc(tsec_clen); fstFread(cdata, tsec_clen, 1, xc->f); rc = uncompress(ucdata, &destlen, cdata, sourcelen); if(rc != Z_OK) { printf("tsec uncompress rc = %d\n", rc); exit(255); } free(cdata); } else { fstFread(ucdata, tsec_uclen, 1, xc->f); } free(time_table); time_table = calloc(tsec_nitems, sizeof(uint64_t)); tpnt = ucdata; tpval = 0; for(ti=0;tif, blkpos+32, SEEK_SET); frame_uclen = fstReaderVarint64(xc->f); frame_clen = fstReaderVarint64(xc->f); frame_maxhandle = fstReaderVarint64(xc->f); if(secnum == 0) { if((beg_tim != time_table[0]) || (blocks_skipped)) { unsigned char *mu = malloc(frame_uclen); uint32_t sig_offs = 0; if(fv) { char wx_buf[32]; int wx_len; if(beg_tim) { wx_len = sprintf(wx_buf, "#%"PRIu64"\n", beg_tim); fstWritex(xc, wx_buf, wx_len); } if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts)) { if(beg_tim == xc->blackout_times[cur_blackout]) { wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); fstWritex(xc, wx_buf, wx_len); } } } if(frame_uclen == frame_clen) { fstFread(mu, frame_uclen, 1, xc->f); } else { unsigned char *mc = malloc(frame_clen); int rc; unsigned long destlen = frame_uclen; unsigned long sourcelen = frame_clen; fstFread(mc, sourcelen, 1, xc->f); rc = uncompress(mu, &destlen, mc, sourcelen); if(rc != Z_OK) { printf("rc: %d\n", rc); exit(255); } free(mc); } for(idx=0;idxprocess_mask[process_idx]&(1<signal_lens[idx] <= 1) { if(xc->signal_lens[idx] == 1) { unsigned char val = mu[sig_offs]; if(value_change_callback) { xc->temp_signal_value_buf[0] = val; xc->temp_signal_value_buf[1] = 0; value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf); } else { if(fv) { char vcd_id[16]; int vcdid_len = fstVcdIDForFwrite(vcd_id+1, idx+1); vcd_id[0] = val; /* collapse 3 writes into one I/O call */ vcd_id[vcdid_len + 1] = '\n'; fstWritex(xc, vcd_id, vcdid_len + 2); } } } else { /* variable-length ("0" length) records have no initial state */ } } else { if(xc->signal_typs[idx] != FST_VT_VCD_REAL) { if(value_change_callback) { memcpy(xc->temp_signal_value_buf, mu+sig_offs, xc->signal_lens[idx]); xc->temp_signal_value_buf[xc->signal_lens[idx]] = 0; value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf); } else { if(fv) { char vcd_id[16]; int vcdid_len = fstVcdIDForFwrite(vcd_id+1, idx+1); vcd_id[0] = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p'; fstWritex(xc, vcd_id, 1); fstWritex(xc,mu+sig_offs, xc->signal_lens[idx]); vcd_id[0] = ' '; /* collapse 3 writes into one I/O call */ vcd_id[vcdid_len + 1] = '\n'; fstWritex(xc, vcd_id, vcdid_len + 2); } } } else { double d; unsigned char *clone_d; unsigned char *srcdata = mu+sig_offs; if(value_change_callback) { if(xc->native_doubles_for_cb) { if(xc->double_endian_match) { clone_d = srcdata; } else { int j; clone_d = (unsigned char *)&d; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } value_change_callback(user_callback_data_pointer, beg_tim, idx+1, clone_d); } else { clone_d = (unsigned char *)&d; if(xc->double_endian_match) { memcpy(clone_d, srcdata, 8); } else { int j; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } sprintf((char *)xc->temp_signal_value_buf, "%.16g", d); value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf); } } else { if(fv) { char vcdid_buf[16]; char wx_buf[64]; int wx_len; clone_d = (unsigned char *)&d; if(xc->double_endian_match) { memcpy(clone_d, srcdata, 8); } else { int j; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } fstVcdID(vcdid_buf, idx+1); wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf); fstWritex(xc, wx_buf, wx_len); } } } } } sig_offs += xc->signal_lens[idx]; } free(mu); fstReaderFseeko(xc, xc->f, -((off_t)frame_clen), SEEK_CUR); } } fstReaderFseeko(xc, xc->f, (off_t)frame_clen, SEEK_CUR); /* skip past compressed data */ vc_maxhandle = fstReaderVarint64(xc->f); vc_start = ftello(xc->f); /* points to '!' character */ packtype = fgetc(xc->f); #ifdef FST_DEBUG fprintf(stderr, "\tframe_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen, (int)frame_clen, (int)frame_maxhandle); fprintf(stderr, "\tvc_maxhandle: %d, packtype: %c\n", (int)vc_maxhandle, packtype); #endif indx_pntr = blkpos + seclen - 24 -tsec_clen -8; fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET); chain_clen = fstReaderUint64(xc->f); indx_pos = indx_pntr - chain_clen; #ifdef FST_DEBUG fprintf(stderr, "\tindx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); #endif chain_cmem = malloc(chain_clen); if(!chain_cmem) goto block_err; fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET); fstFread(chain_cmem, chain_clen, 1, xc->f); if(vc_maxhandle > vc_maxhandle_largest) { free(chain_table); free(chain_table_lengths); vc_maxhandle_largest = vc_maxhandle; chain_table = calloc((vc_maxhandle+1), sizeof(off_t)); chain_table_lengths = calloc((vc_maxhandle+1), sizeof(uint32_t)); } if(!chain_table || !chain_table_lengths) goto block_err; pnt = chain_cmem; idx = 0; pval = 0; if(sectype == FST_BL_VCDATA_DYN_ALIAS2) { uint32_t prev_alias = 0; do { int skiplen; if(*pnt & 0x01) { int64_t shval = fstGetSVarint64(pnt, &skiplen) >> 1; if(shval > 0) { pval = chain_table[idx] = pval + shval; if(idx) { chain_table_lengths[pidx] = pval - chain_table[pidx]; } pidx = idx++; } else if(shval < 0) { chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ chain_table_lengths[idx] = prev_alias = shval; /* because during this loop iter would give stale data! */ idx++; } else { chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ chain_table_lengths[idx] = prev_alias; /* because during this loop iter would give stale data! */ idx++; } } else { uint64_t val = fstGetVarint32(pnt, &skiplen); fstHandle loopcnt = val >> 1; for(i=0;i> 1); if(idx) { chain_table_lengths[pidx] = pval - chain_table[pidx]; } pidx = idx++; } else { fstHandle loopcnt = val >> 1; for(i=0;i xc->maxhandle) idx = xc->maxhandle; for(i=0;iprocess_mask[process_idx]&(1<f, vc_start + chain_table[i], SEEK_SET); val = fstReaderVarint32WithSkip(xc->f, &skiplen); if(val) { unsigned char *mu = mem_for_traversal + traversal_mem_offs; /* uncomp: dst */ unsigned char *mc; /* comp: src */ unsigned long destlen = val; unsigned long sourcelen = chain_table_lengths[i]; if(mc_mem_len < chain_table_lengths[i]) { free(mc_mem); mc_mem = malloc(mc_mem_len = chain_table_lengths[i]); } mc = mc_mem; fstFread(mc, chain_table_lengths[i], 1, xc->f); switch(packtype) { case '4': rc = (destlen == (unsigned long)LZ4_decompress_safe_partial((char *)mc, (char *)mu, sourcelen, destlen, destlen)) ? Z_OK : Z_DATA_ERROR; break; case 'F': fastlz_decompress(mc, sourcelen, mu, destlen); /* rc appears unreliable */ break; default: rc = uncompress(mu, &destlen, mc, sourcelen); break; } /* data to process is for(j=0;jf); /* data to process is for(j=0;jsignal_lens[i] == 1) { uint32_t vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[i]); uint32_t shcnt = 2 << (vli & 1); tdelta = vli >> shcnt; } else { uint32_t vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[i]); tdelta = vli >> 1; } scatterptr[i] = tc_head[tdelta]; tc_head[tdelta] = i+1; } } } free(mc_mem); /* there is no usage below for this, no real need to clear out mc_mem or mc_mem_len */ for(i=0;ilimit_range_valid) { if(time_table[i] > xc->limit_range_end) { break; } } wx_len = sprintf(wx_buf, "#%"PRIu64"\n", time_table[i]); fstWritex(xc, wx_buf, wx_len); if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts)) { if(time_table[i] == xc->blackout_times[cur_blackout]) { wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); fstWritex(xc, wx_buf, wx_len); } } previous_time = time_table[i]; } } while(tc_head[i]) { idx = tc_head[i] - 1; vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen); if(xc->signal_lens[idx] <= 1) { if(xc->signal_lens[idx] == 1) { unsigned char val; if(!(vli & 1)) { /* tdelta = vli >> 2; */ /* scan-build */ val = ((vli >> 1) & 1) | '0'; } else { /* tdelta = vli >> 4; */ /* scan-build */ val = FST_RCV_STR[((vli >> 1) & 7)]; } if(value_change_callback) { xc->temp_signal_value_buf[0] = val; xc->temp_signal_value_buf[1] = 0; value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf); } else { if(fv) { char vcd_id[16]; int vcdid_len = fstVcdIDForFwrite(vcd_id+1, idx+1); vcd_id[0] = val; vcd_id[vcdid_len+1] = '\n'; fstWritex(xc, vcd_id, vcdid_len+2); } } headptr[idx] += skiplen; length_remaining[idx] -= skiplen; tc_head[i] = scatterptr[idx]; scatterptr[idx] = 0; if(length_remaining[idx]) { int shamt; vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]); shamt = 2 << (vli & 1); tdelta = vli >> shamt; scatterptr[idx] = tc_head[i+tdelta]; tc_head[i+tdelta] = idx+1; } } else { unsigned char *vdata; uint32_t len; vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen); len = fstGetVarint32(mem_for_traversal + headptr[idx] + skiplen, &skiplen2); /* tdelta = vli >> 1; */ /* scan-build */ skiplen += skiplen2; vdata = mem_for_traversal + headptr[idx] + skiplen; if(!(vli & 1)) { if(value_change_callback_varlen) { value_change_callback_varlen(user_callback_data_pointer, time_table[i], idx+1, vdata, len); } else { if(fv) { char vcd_id[16]; int vcdid_len; vcd_id[0] = 's'; fstWritex(xc, vcd_id, 1); vcdid_len = fstVcdIDForFwrite(vcd_id+1, idx+1); { unsigned char *vesc = malloc(len*4 + 1); int vlen = fstUtilityBinToEsc(vesc, vdata, len); fstWritex(xc, vesc, vlen); free(vesc); } vcd_id[0] = ' '; vcd_id[vcdid_len + 1] = '\n'; fstWritex(xc, vcd_id, vcdid_len+2); } } } skiplen += len; headptr[idx] += skiplen; length_remaining[idx] -= skiplen; tc_head[i] = scatterptr[idx]; scatterptr[idx] = 0; if(length_remaining[idx]) { vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]); tdelta = vli >> 1; scatterptr[idx] = tc_head[i+tdelta]; tc_head[i+tdelta] = idx+1; } } } else { uint32_t len = xc->signal_lens[idx]; unsigned char *vdata; vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen); /* tdelta = vli >> 1; */ /* scan-build */ vdata = mem_for_traversal + headptr[idx] + skiplen; if(xc->signal_typs[idx] != FST_VT_VCD_REAL) { if(!(vli & 1)) { int byte = 0; int bit; unsigned int j; for(j=0;j> bit) & 1) | '0'; xc->temp_signal_value_buf[j] = ch; } xc->temp_signal_value_buf[j] = 0; if(value_change_callback) { value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf); } else { if(fv) { unsigned char ch_bp = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p'; fstWritex(xc, &ch_bp, 1); fstWritex(xc, xc->temp_signal_value_buf, len); } } len = byte+1; } else { if(value_change_callback) { memcpy(xc->temp_signal_value_buf, vdata, len); xc->temp_signal_value_buf[len] = 0; value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf); } else { if(fv) { unsigned char ch_bp = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p'; fstWritex(xc, &ch_bp, 1); fstWritex(xc, vdata, len); } } } } else { double d; unsigned char *clone_d /*= (unsigned char *)&d */; /* scan-build */ unsigned char buf[8]; unsigned char *srcdata; if(!(vli & 1)) /* very rare case, but possible */ { int bit; int j; for(j=0;j<8;j++) { unsigned char ch; bit = 7 - (j & 7); ch = ((vdata[0] >> bit) & 1) | '0'; buf[j] = ch; } len = 1; srcdata = buf; } else { srcdata = vdata; } if(value_change_callback) { if(xc->native_doubles_for_cb) { if(xc->double_endian_match) { clone_d = srcdata; } else { int j; clone_d = (unsigned char *)&d; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } value_change_callback(user_callback_data_pointer, time_table[i], idx+1, clone_d); } else { clone_d = (unsigned char *)&d; if(xc->double_endian_match) { memcpy(clone_d, srcdata, 8); } else { int j; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } sprintf((char *)xc->temp_signal_value_buf, "%.16g", d); value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf); } } else { if(fv) { char wx_buf[32]; int wx_len; clone_d = (unsigned char *)&d; if(xc->double_endian_match) { memcpy(clone_d, srcdata, 8); } else { int j; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } wx_len = sprintf(wx_buf, "r%.16g", d); fstWritex(xc, wx_buf, wx_len); } } } if(fv) { char vcd_id[16]; int vcdid_len = fstVcdIDForFwrite(vcd_id+1, idx+1); vcd_id[0] = ' '; vcd_id[vcdid_len+1] = '\n'; fstWritex(xc, vcd_id, vcdid_len+2); } skiplen += len; headptr[idx] += skiplen; length_remaining[idx] -= skiplen; tc_head[i] = scatterptr[idx]; scatterptr[idx] = 0; if(length_remaining[idx]) { vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]); tdelta = vli >> 1; scatterptr[idx] = tc_head[i+tdelta]; tc_head[i+tdelta] = idx+1; } } } } block_err: free(tc_head); free(chain_cmem); free(mem_for_traversal); mem_for_traversal = NULL; secnum++; if(secnum == xc->vc_section_count) break; /* in case file is growing, keep with original block count */ blkpos += seclen; } if(mem_for_traversal) free(mem_for_traversal); /* scan-build */ free(length_remaining); free(headptr); free(scatterptr); if(chain_table) free(chain_table); if(chain_table_lengths) free(chain_table_lengths); free(time_table); #ifndef FST_WRITEX_DISABLE if(fv) { fstWritex(xc, NULL, 0); } #endif return(1); } /* rvat functions */ static char *fstExtractRvatDataFromFrame(struct fstReaderContext *xc, fstHandle facidx, char *buf) { if(facidx >= xc->rvat_frame_maxhandle) { return(NULL); } if(xc->signal_lens[facidx] == 1) { buf[0] = (char)xc->rvat_frame_data[xc->rvat_sig_offs[facidx]]; buf[1] = 0; } else { if(xc->signal_typs[facidx] != FST_VT_VCD_REAL) { memcpy(buf, xc->rvat_frame_data + xc->rvat_sig_offs[facidx], xc->signal_lens[facidx]); buf[xc->signal_lens[facidx]] = 0; } else { double d; unsigned char *clone_d = (unsigned char *)&d; unsigned char *srcdata = xc->rvat_frame_data + xc->rvat_sig_offs[facidx]; if(xc->double_endian_match) { memcpy(clone_d, srcdata, 8); } else { int j; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } sprintf((char *)buf, "%.16g", d); } } return(buf); } char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; off_t blkpos = 0, prev_blkpos; uint64_t beg_tim, end_tim, beg_tim2, end_tim2; int sectype; unsigned int secnum = 0; uint64_t seclen; uint64_t tsec_uclen = 0, tsec_clen = 0; uint64_t tsec_nitems; uint64_t frame_uclen, frame_clen; #ifdef FST_DEBUG uint64_t mem_required_for_traversal; #endif off_t indx_pntr, indx_pos; long chain_clen; unsigned char *chain_cmem; unsigned char *pnt; fstHandle idx, pidx=0, i; uint64_t pval; if((!xc) || (!facidx) || (facidx > xc->maxhandle) || (!buf) || (!xc->signal_lens[facidx-1])) { return(NULL); } if(!xc->rvat_sig_offs) { uint32_t cur_offs = 0; xc->rvat_sig_offs = calloc(xc->maxhandle, sizeof(uint32_t)); for(i=0;imaxhandle;i++) { xc->rvat_sig_offs[i] = cur_offs; cur_offs += xc->signal_lens[i]; } } if(xc->rvat_data_valid) { if((xc->rvat_beg_tim <= tim) && (tim <= xc->rvat_end_tim)) { goto process_value; } fstReaderDeallocateRvatData(xc); } xc->rvat_chain_pos_valid = 0; for(;;) { fstReaderFseeko(xc, xc->f, (prev_blkpos = blkpos), SEEK_SET); sectype = fgetc(xc->f); seclen = fstReaderUint64(xc->f); if((sectype == EOF) || (sectype == FST_BL_SKIP) || (!seclen)) { return(NULL); /* if this loop exits on break, it's successful */ } blkpos++; if((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) && (sectype != FST_BL_VCDATA_DYN_ALIAS2)) { blkpos += seclen; continue; } beg_tim = fstReaderUint64(xc->f); end_tim = fstReaderUint64(xc->f); if((beg_tim <= tim) && (tim <= end_tim)) { if((tim == end_tim) && (tim != xc->end_time)) { off_t cached_pos = ftello(xc->f); fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET); sectype = fgetc(xc->f); seclen = fstReaderUint64(xc->f); beg_tim2 = fstReaderUint64(xc->f); end_tim2 = fstReaderUint64(xc->f); if(((sectype != FST_BL_VCDATA)&&(sectype != FST_BL_VCDATA_DYN_ALIAS)&&(sectype != FST_BL_VCDATA_DYN_ALIAS2)) || (!seclen) || (beg_tim2 != tim)) { blkpos = prev_blkpos; break; } beg_tim = beg_tim2; end_tim = end_tim2; fstReaderFseeko(xc, xc->f, cached_pos, SEEK_SET); } break; } blkpos += seclen; secnum++; } xc->rvat_beg_tim = beg_tim; xc->rvat_end_tim = end_tim; #ifdef FST_DEBUG mem_required_for_traversal = #endif fstReaderUint64(xc->f); #ifdef FST_DEBUG fprintf(stderr, "rvat sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim, (int)end_tim); fprintf(stderr, "\tmem_required_for_traversal: %d\n", (int)mem_required_for_traversal); #endif /* process time block */ { unsigned char *ucdata; unsigned char *cdata; unsigned long destlen /* = tsec_uclen */; /* scan-build */ unsigned long sourcelen /* = tsec_clen */; /* scan-build */ int rc; unsigned char *tpnt; uint64_t tpval; unsigned int ti; fstReaderFseeko(xc, xc->f, blkpos + seclen - 24, SEEK_SET); tsec_uclen = fstReaderUint64(xc->f); tsec_clen = fstReaderUint64(xc->f); tsec_nitems = fstReaderUint64(xc->f); #ifdef FST_DEBUG fprintf(stderr, "\ttime section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen, (int)tsec_nitems); #endif ucdata = malloc(tsec_uclen); destlen = tsec_uclen; sourcelen = tsec_clen; fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR); if(tsec_uclen != tsec_clen) { cdata = malloc(tsec_clen); fstFread(cdata, tsec_clen, 1, xc->f); rc = uncompress(ucdata, &destlen, cdata, sourcelen); if(rc != Z_OK) { printf("tsec uncompress rc = %d\n", rc); exit(255); } free(cdata); } else { fstFread(ucdata, tsec_uclen, 1, xc->f); } xc->rvat_time_table = calloc(tsec_nitems, sizeof(uint64_t)); tpnt = ucdata; tpval = 0; for(ti=0;tirvat_time_table[ti] = tpval + val; tpnt += skiplen; } free(ucdata); } fstReaderFseeko(xc, xc->f, blkpos+32, SEEK_SET); frame_uclen = fstReaderVarint64(xc->f); frame_clen = fstReaderVarint64(xc->f); xc->rvat_frame_maxhandle = fstReaderVarint64(xc->f); xc->rvat_frame_data = malloc(frame_uclen); if(frame_uclen == frame_clen) { fstFread(xc->rvat_frame_data, frame_uclen, 1, xc->f); } else { unsigned char *mc = malloc(frame_clen); int rc; unsigned long destlen = frame_uclen; unsigned long sourcelen = frame_clen; fstFread(mc, sourcelen, 1, xc->f); rc = uncompress(xc->rvat_frame_data, &destlen, mc, sourcelen); if(rc != Z_OK) { printf("decompress rc: %d\n", rc); exit(255); } free(mc); } xc->rvat_vc_maxhandle = fstReaderVarint64(xc->f); xc->rvat_vc_start = ftello(xc->f); /* points to '!' character */ #ifdef FST_DEBUG fprintf(stderr, "\tframe_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen, (int)frame_clen, (int)xc->rvat_frame_maxhandle); fprintf(stderr, "\tvc_maxhandle: %d\n", (int)xc->rvat_vc_maxhandle); #endif indx_pntr = blkpos + seclen - 24 -tsec_clen -8; fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET); chain_clen = fstReaderUint64(xc->f); indx_pos = indx_pntr - chain_clen; #ifdef FST_DEBUG fprintf(stderr, "\tindx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); #endif chain_cmem = malloc(chain_clen); fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET); fstFread(chain_cmem, chain_clen, 1, xc->f); xc->rvat_chain_table = calloc((xc->rvat_vc_maxhandle+1), sizeof(off_t)); xc->rvat_chain_table_lengths = calloc((xc->rvat_vc_maxhandle+1), sizeof(uint32_t)); pnt = chain_cmem; idx = 0; pval = 0; do { int skiplen; uint64_t val = fstGetVarint32(pnt, &skiplen); if(!val) { pnt += skiplen; val = fstGetVarint32(pnt, &skiplen); xc->rvat_chain_table[idx] = 0; xc->rvat_chain_table_lengths[idx] = -val; idx++; } else if(val&1) { pval = xc->rvat_chain_table[idx] = pval + (val >> 1); if(idx) { xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx]; } pidx = idx++; } else { fstHandle loopcnt = val >> 1; for(i=0;irvat_chain_table[idx++] = 0; } } pnt += skiplen; } while (pnt != (chain_cmem + chain_clen)); free(chain_cmem); xc->rvat_chain_table[idx] = indx_pos - xc->rvat_vc_start; xc->rvat_chain_table_lengths[pidx] = xc->rvat_chain_table[idx] - xc->rvat_chain_table[pidx]; for(i=0;irvat_chain_table_lengths[i]; if((v32 < 0) && (!xc->rvat_chain_table[i])) { v32 = -v32; v32--; if(((uint32_t)v32) < i) /* sanity check */ { xc->rvat_chain_table[i] = xc->rvat_chain_table[v32]; xc->rvat_chain_table_lengths[i] = xc->rvat_chain_table_lengths[v32]; } } } #ifdef FST_DEBUG fprintf(stderr, "\tdecompressed chain idx len: %"PRIu32"\n", idx); #endif xc->rvat_data_valid = 1; /* all data at this point is loaded or resident in fst cache, process and return appropriate value */ process_value: if(facidx > xc->rvat_vc_maxhandle) { return(NULL); } facidx--; /* scale down for array which starts at zero */ if(((tim == xc->rvat_beg_tim)&&(!xc->rvat_chain_table[facidx])) || (!xc->rvat_chain_table[facidx])) { return(fstExtractRvatDataFromFrame(xc, facidx, buf)); } if(facidx != xc->rvat_chain_facidx) { if(xc->rvat_chain_mem) { free(xc->rvat_chain_mem); xc->rvat_chain_mem = NULL; xc->rvat_chain_pos_valid = 0; } } if(!xc->rvat_chain_mem) { uint32_t skiplen; fstReaderFseeko(xc, xc->f, xc->rvat_vc_start + xc->rvat_chain_table[facidx], SEEK_SET); xc->rvat_chain_len = fstReaderVarint32WithSkip(xc->f, &skiplen); if(xc->rvat_chain_len) { unsigned char *mu = malloc(xc->rvat_chain_len); unsigned char *mc = malloc(xc->rvat_chain_table_lengths[facidx]); unsigned long destlen = xc->rvat_chain_len; unsigned long sourcelen = xc->rvat_chain_table_lengths[facidx]; int rc; fstFread(mc, xc->rvat_chain_table_lengths[facidx], 1, xc->f); rc = uncompress(mu, &destlen, mc, sourcelen); free(mc); if(rc != Z_OK) { printf("\tclen: %d (rc=%d)\n", (int)xc->rvat_chain_len, rc); exit(255); } /* data to process is for(j=0;jrvat_chain_mem = mu; } else { int destlen = xc->rvat_chain_table_lengths[facidx] - skiplen; unsigned char *mu = malloc(xc->rvat_chain_len = destlen); fstFread(mu, destlen, 1, xc->f); /* data to process is for(j=0;jrvat_chain_mem = mu; } xc->rvat_chain_facidx = facidx; } /* process value chain here */ { uint32_t tidx = 0, ptidx = 0; uint32_t tdelta; int skiplen; unsigned int iprev = xc->rvat_chain_len; uint32_t pvli = 0; int pskip = 0; if((xc->rvat_chain_pos_valid)&&(tim >= xc->rvat_chain_pos_time)) { i = xc->rvat_chain_pos_idx; tidx = xc->rvat_chain_pos_tidx; } else { i = 0; tidx = 0; xc->rvat_chain_pos_time = xc->rvat_beg_tim; } if(xc->signal_lens[facidx] == 1) { while(irvat_chain_len) { uint32_t vli = fstGetVarint32(xc->rvat_chain_mem + i, &skiplen); uint32_t shcnt = 2 << (vli & 1); tdelta = vli >> shcnt; if(xc->rvat_time_table[tidx + tdelta] <= tim) { iprev = i; pvli = vli; ptidx = tidx; /* pskip = skiplen; */ /* scan-build */ tidx += tdelta; i+=skiplen; } else { break; } } if(iprev != xc->rvat_chain_len) { xc->rvat_chain_pos_tidx = ptidx; xc->rvat_chain_pos_idx = iprev; xc->rvat_chain_pos_time = tim; xc->rvat_chain_pos_valid = 1; if(!(pvli & 1)) { buf[0] = ((pvli >> 1) & 1) | '0'; } else { buf[0] = FST_RCV_STR[((pvli >> 1) & 7)]; } buf[1] = 0; return(buf); } else { return(fstExtractRvatDataFromFrame(xc, facidx, buf)); } } else { while(irvat_chain_len) { uint32_t vli = fstGetVarint32(xc->rvat_chain_mem + i, &skiplen); tdelta = vli >> 1; if(xc->rvat_time_table[tidx + tdelta] <= tim) { iprev = i; pvli = vli; ptidx = tidx; pskip = skiplen; tidx += tdelta; i+=skiplen; if(!(pvli & 1)) { i+=((xc->signal_lens[facidx]+7)/8); } else { i+=xc->signal_lens[facidx]; } } else { break; } } if(iprev != xc->rvat_chain_len) { unsigned char *vdata = xc->rvat_chain_mem + iprev + pskip; xc->rvat_chain_pos_tidx = ptidx; xc->rvat_chain_pos_idx = iprev; xc->rvat_chain_pos_time = tim; xc->rvat_chain_pos_valid = 1; if(xc->signal_typs[facidx] != FST_VT_VCD_REAL) { if(!(pvli & 1)) { int byte = 0; int bit; unsigned int j; for(j=0;jsignal_lens[facidx];j++) { unsigned char ch; byte = j/8; bit = 7 - (j & 7); ch = ((vdata[byte] >> bit) & 1) | '0'; buf[j] = ch; } buf[j] = 0; return(buf); } else { memcpy(buf, vdata, xc->signal_lens[facidx]); buf[xc->signal_lens[facidx]] = 0; return(buf); } } else { double d; unsigned char *clone_d = (unsigned char *)&d; unsigned char bufd[8]; unsigned char *srcdata; if(!(pvli & 1)) /* very rare case, but possible */ { int bit; int j; for(j=0;j<8;j++) { unsigned char ch; bit = 7 - (j & 7); ch = ((vdata[0] >> bit) & 1) | '0'; bufd[j] = ch; } srcdata = bufd; } else { srcdata = vdata; } if(xc->double_endian_match) { memcpy(clone_d, srcdata, 8); } else { int j; for(j=0;j<8;j++) { clone_d[j] = srcdata[7-j]; } } sprintf(buf, "r%.16g", d); return(buf); } } else { return(fstExtractRvatDataFromFrame(xc, facidx, buf)); } } } /* return(NULL); */ } /**********************************************************************/ #ifndef _WAVE_HAVE_JUDY /***********************/ /*** ***/ /*** jenkins hash ***/ /*** ***/ /***********************/ /* -------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. For every delta with one or two bits set, and the deltas of all three high bits or all three low bits, whether the original value of a,b,c is almost all zero or is uniformly distributed, * If mix() is run forward or backward, at least 32 bits in a,b,c have at least 1/4 probability of changing. * If mix() is run forward, every bit of c will change between 1/3 and 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) mix() was built out of 36 single-cycle latency instructions in a structure that could supported 2x parallelism, like so: a -= b; a -= c; x = (c>>13); b -= c; a ^= x; b -= a; x = (a<<8); c -= a; b ^= x; c -= b; x = (b>>13); ... Unfortunately, superscalar Pentiums and Sparcs can't take advantage of that parallelism. They've also turned some of those single-cycle latency instructions into multi-cycle latency instructions. Still, this is the fastest good hash I could find. There were about 2^^68 to choose from. I only looked at a billion or so. -------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } /* -------------------------------------------------------------------- j_hash() -- hash a variable-length key into a 32-bit value k : the key (the unaligned variable-length array of bytes) len : the length of the key, counting by bytes initval : can be any 4-byte value Returns a 32-bit value. Every bit of the key affects every bit of the return value. Every 1-bit and 2-bit delta achieves avalanche. About 6*len+35 instructions. The best hash table sizes are powers of 2. There is no need to do mod a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. For example, if you need only 10 bits, do h = (h & hashmask(10)); In which case, the hash table should have hashsize(10) elements. If you are hashing n strings (uint8_t **)k, do it like this: for (i=0, h=0; i= 12) { a += (k[0] +((uint32_t)k[1]<<8) +((uint32_t)k[2]<<16) +((uint32_t)k[3]<<24)); b += (k[4] +((uint32_t)k[5]<<8) +((uint32_t)k[6]<<16) +((uint32_t)k[7]<<24)); c += (k[8] +((uint32_t)k[9]<<8) +((uint32_t)k[10]<<16)+((uint32_t)k[11]<<24)); mix(a,b,c); k += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch(len) /* all the case statements fall through */ { case 11: c+=((uint32_t)k[10]<<24); case 10: c+=((uint32_t)k[9]<<16); case 9 : c+=((uint32_t)k[8]<<8); /* the first byte of c is reserved for the length */ case 8 : b+=((uint32_t)k[7]<<24); case 7 : b+=((uint32_t)k[6]<<16); case 6 : b+=((uint32_t)k[5]<<8); case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3]<<24); case 3 : a+=((uint32_t)k[2]<<16); case 2 : a+=((uint32_t)k[1]<<8); case 1 : a+=k[0]; /* case 0: nothing left to add */ } mix(a,b,c); /*-------------------------------------------- report the result */ return(c); } /********************************************************************/ /***************************/ /*** ***/ /*** judy HS emulation ***/ /*** ***/ /***************************/ struct collchain_t { struct collchain_t *next; void *payload; uint32_t fullhash, length; unsigned char mem[1]; }; void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint32_t hashmask) { struct collchain_t ***base = (struct collchain_t ***)base_i; uint32_t hf, h; struct collchain_t **ar; struct collchain_t *chain, *pchain; if(!*base) { *base = calloc(1, (hashmask + 1) * sizeof(void *)); } ar = *base; h = (hf = j_hash(mem, length, length)) & hashmask; pchain = chain = ar[h]; while(chain) { if((chain->fullhash == hf) && (chain->length == length) && !memcmp(chain->mem, mem, length)) { if(pchain != chain) /* move hit to front */ { pchain->next = chain->next; chain->next = ar[h]; ar[h] = chain; } return(&(chain->payload)); } pchain = chain; chain = chain->next; } chain = calloc(1, sizeof(struct collchain_t) + length - 1); memcpy(chain->mem, mem, length); chain->fullhash = hf; chain->length = length; chain->next = ar[h]; ar[h] = chain; return(&(chain->payload)); } void JenkinsFree(void *base_i, uint32_t hashmask) { struct collchain_t ***base = (struct collchain_t ***)base_i; uint32_t h; struct collchain_t **ar; struct collchain_t *chain, *chain_next; if(base && *base) { ar = *base; for(h=0;h<=hashmask;h++) { chain = ar[h]; while(chain) { chain_next = chain->next; free(chain); chain = chain_next; } } free(*base); *base = NULL; } } #endif /**********************************************************************/ /************************/ /*** ***/ /*** utility function ***/ /*** ***/ /************************/ int fstUtilityBinToEsc(unsigned char *d, unsigned char *s, int len) { unsigned char *src = s; unsigned char *dst = d; unsigned char val; int i; for(i=0;i ' ') && (src[i] <= '~')) /* no white spaces in output */ { *(dst++) = src[i]; } else { val = src[i]; *(dst++) = '\\'; *(dst++) = (val/64) + '0'; val = val & 63; *(dst++) = (val/8) + '0'; val = val & 7; *(dst++) = (val) + '0'; } break; } } return(dst - d); } /* * this overwrites the original string if the destination pointer is NULL */ int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len) { unsigned char *src = s; unsigned char *dst = (!d) ? s : (s = d); unsigned char val[3]; int i; for(i=0;i='A')&&(val[0]<='F')) ? (val[0] - 'A' + 10) : (val[0] - '0'); val[1] = ((val[1]>='A')&&(val[1]<='F')) ? (val[1] - 'A' + 10) : (val[1] - '0'); *(dst++) = val[0] * 16 + val[1]; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': val[0] = src[ i] - '0'; val[1] = src[++i] - '0'; val[2] = src[++i] - '0'; *(dst++) = val[0] * 64 + val[1] * 8 + val[2]; break; default: *(dst++) = src[i]; break; } } } return(dst - s); } iverilog-10_1/vpi/fstapi.h000066400000000000000000000407301265551621300156030ustar00rootroot00000000000000/* * Copyright (c) 2009-2015 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef FST_API_H #define FST_API_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #define FST_RDLOAD "FSTLOAD | " typedef uint32_t fstHandle; enum fstWriterPackType { FST_WR_PT_ZLIB = 0, FST_WR_PT_FASTLZ = 1, FST_WR_PT_LZ4 = 2 }; enum fstFileType { FST_FT_MIN = 0, FST_FT_VERILOG = 0, FST_FT_VHDL = 1, FST_FT_VERILOG_VHDL = 2, FST_FT_MAX = 2 }; enum fstBlockType { FST_BL_HDR = 0, FST_BL_VCDATA = 1, FST_BL_BLACKOUT = 2, FST_BL_GEOM = 3, FST_BL_HIER = 4, FST_BL_VCDATA_DYN_ALIAS = 5, FST_BL_HIER_LZ4 = 6, FST_BL_HIER_LZ4DUO = 7, FST_BL_VCDATA_DYN_ALIAS2 = 8, FST_BL_ZWRAPPER = 254, /* indicates that whole trace is gz wrapped */ FST_BL_SKIP = 255 /* used while block is being written */ }; enum fstScopeType { FST_ST_MIN = 0, FST_ST_VCD_MODULE = 0, FST_ST_VCD_TASK = 1, FST_ST_VCD_FUNCTION = 2, FST_ST_VCD_BEGIN = 3, FST_ST_VCD_FORK = 4, FST_ST_VCD_GENERATE = 5, FST_ST_VCD_STRUCT = 6, FST_ST_VCD_UNION = 7, FST_ST_VCD_CLASS = 8, FST_ST_VCD_INTERFACE = 9, FST_ST_VCD_PACKAGE = 10, FST_ST_VCD_PROGRAM = 11, FST_ST_VHDL_ARCHITECTURE = 12, FST_ST_VHDL_PROCEDURE = 13, FST_ST_VHDL_FUNCTION = 14, FST_ST_VHDL_RECORD = 15, FST_ST_VHDL_PROCESS = 16, FST_ST_VHDL_BLOCK = 17, FST_ST_VHDL_FOR_GENERATE = 18, FST_ST_VHDL_IF_GENERATE = 19, FST_ST_VHDL_GENERATE = 20, FST_ST_VHDL_PACKAGE = 21, FST_ST_MAX = 21, FST_ST_GEN_ATTRBEGIN = 252, FST_ST_GEN_ATTREND = 253, FST_ST_VCD_SCOPE = 254, FST_ST_VCD_UPSCOPE = 255 }; enum fstVarType { FST_VT_MIN = 0, /* start of vartypes */ FST_VT_VCD_EVENT = 0, FST_VT_VCD_INTEGER = 1, FST_VT_VCD_PARAMETER = 2, FST_VT_VCD_REAL = 3, FST_VT_VCD_REAL_PARAMETER = 4, FST_VT_VCD_REG = 5, FST_VT_VCD_SUPPLY0 = 6, FST_VT_VCD_SUPPLY1 = 7, FST_VT_VCD_TIME = 8, FST_VT_VCD_TRI = 9, FST_VT_VCD_TRIAND = 10, FST_VT_VCD_TRIOR = 11, FST_VT_VCD_TRIREG = 12, FST_VT_VCD_TRI0 = 13, FST_VT_VCD_TRI1 = 14, FST_VT_VCD_WAND = 15, FST_VT_VCD_WIRE = 16, FST_VT_VCD_WOR = 17, FST_VT_VCD_PORT = 18, FST_VT_VCD_SPARRAY = 19, /* used to define the rownum (index) port for a sparse array */ FST_VT_VCD_REALTIME = 20, FST_VT_GEN_STRING = 21, /* generic string type (max len is defined dynamically via fstWriterEmitVariableLengthValueChange) */ FST_VT_SV_BIT = 22, FST_VT_SV_LOGIC = 23, FST_VT_SV_INT = 24, /* declare as size = 32 */ FST_VT_SV_SHORTINT = 25, /* declare as size = 16 */ FST_VT_SV_LONGINT = 26, /* declare as size = 64 */ FST_VT_SV_BYTE = 27, /* declare as size = 8 */ FST_VT_SV_ENUM = 28, /* declare as appropriate type range */ FST_VT_SV_SHORTREAL = 29, /* declare and emit same as FST_VT_VCD_REAL (needs to be emitted as double, not a float) */ FST_VT_MAX = 29 /* end of vartypes */ }; enum fstVarDir { FST_VD_MIN = 0, FST_VD_IMPLICIT = 0, FST_VD_INPUT = 1, FST_VD_OUTPUT = 2, FST_VD_INOUT = 3, FST_VD_BUFFER = 4, FST_VD_LINKAGE = 5, FST_VD_MAX = 5 }; enum fstHierType { FST_HT_MIN = 0, FST_HT_SCOPE = 0, FST_HT_UPSCOPE = 1, FST_HT_VAR = 2, FST_HT_ATTRBEGIN = 3, FST_HT_ATTREND = 4, FST_HT_MAX = 4 }; enum fstAttrType { FST_AT_MIN = 0, FST_AT_MISC = 0, /* self-contained: does not need matching FST_HT_ATTREND */ FST_AT_ARRAY = 1, FST_AT_ENUM = 2, FST_AT_PACK = 3, FST_AT_MAX = 3 }; enum fstMiscType { FST_MT_MIN = 0, FST_MT_COMMENT = 0, /* use fstWriterSetComment() to emit */ FST_MT_ENVVAR = 1, /* use fstWriterSetEnvVar() to emit */ FST_MT_SUPVAR = 2, /* use fstWriterCreateVar2() to emit */ FST_MT_PATHNAME = 3, /* reserved for fstWriterSetSourceStem() string -> number management */ FST_MT_SOURCESTEM = 4, /* use fstWriterSetSourceStem() to emit */ FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */ FST_MT_VALUELIST = 6, /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */ FST_MT_UNKNOWN = 7, FST_MT_MAX = 7 }; enum fstArrayType { FST_AR_MIN = 0, FST_AR_NONE = 0, FST_AR_UNPACKED = 1, FST_AR_PACKED = 2, FST_AR_SPARSE = 3, FST_AR_MAX = 3 }; enum fstEnumValueType { FST_EV_SV_INTEGER = 0, FST_EV_SV_BIT = 1, FST_EV_SV_LOGIC = 2, FST_EV_SV_INT = 3, FST_EV_SV_SHORTINT = 4, FST_EV_SV_LONGINT = 5, FST_EV_SV_BYTE = 6, FST_EV_SV_UNSIGNED_INTEGER = 7, FST_EV_SV_UNSIGNED_BIT = 8, FST_EV_SV_UNSIGNED_LOGIC = 9, FST_EV_SV_UNSIGNED_INT = 10, FST_EV_SV_UNSIGNED_SHORTINT = 11, FST_EV_SV_UNSIGNED_LONGINT = 12, FST_EV_SV_UNSIGNED_BYTE = 13, FST_EV_MAX = 13 }; enum fstPackType { FST_PT_NONE = 0, FST_PT_UNPACKED = 1, FST_PT_PACKED = 2, FST_PT_TAGGED_PACKED = 3, FST_PT_MAX = 3 }; enum fstSupplementalVarType { FST_SVT_MIN = 0, FST_SVT_NONE = 0, FST_SVT_VHDL_SIGNAL = 1, FST_SVT_VHDL_VARIABLE = 2, FST_SVT_VHDL_CONSTANT = 3, FST_SVT_VHDL_FILE = 4, FST_SVT_VHDL_MEMORY = 5, FST_SVT_MAX = 5 }; enum fstSupplementalDataType { FST_SDT_MIN = 0, FST_SDT_NONE = 0, FST_SDT_VHDL_BOOLEAN = 1, FST_SDT_VHDL_BIT = 2, FST_SDT_VHDL_BIT_VECTOR = 3, FST_SDT_VHDL_STD_ULOGIC = 4, FST_SDT_VHDL_STD_ULOGIC_VECTOR = 5, FST_SDT_VHDL_STD_LOGIC = 6, FST_SDT_VHDL_STD_LOGIC_VECTOR = 7, FST_SDT_VHDL_UNSIGNED = 8, FST_SDT_VHDL_SIGNED = 9, FST_SDT_VHDL_INTEGER = 10, FST_SDT_VHDL_REAL = 11, FST_SDT_VHDL_NATURAL = 12, FST_SDT_VHDL_POSITIVE = 13, FST_SDT_VHDL_TIME = 14, FST_SDT_VHDL_CHARACTER = 15, FST_SDT_VHDL_STRING = 16, FST_SDT_MAX = 16, FST_SDT_SVT_SHIFT_COUNT = 10, /* FST_SVT_* is ORed in by fstWriterCreateVar2() to the left after shifting FST_SDT_SVT_SHIFT_COUNT */ FST_SDT_ABS_MAX = ((1<<(FST_SDT_SVT_SHIFT_COUNT))-1) }; struct fstHier { unsigned char htyp; union { /* if htyp == FST_HT_SCOPE */ struct fstHierScope { unsigned char typ; /* FST_ST_MIN ... FST_ST_MAX */ const char *name; const char *component; uint32_t name_length; /* strlen(u.scope.name) */ uint32_t component_length; /* strlen(u.scope.component) */ } scope; /* if htyp == FST_HT_VAR */ struct fstHierVar { unsigned char typ; /* FST_VT_MIN ... FST_VT_MAX */ unsigned char direction; /* FST_VD_MIN ... FST_VD_MAX */ unsigned char svt_workspace; /* zeroed out by FST reader, for client code use */ unsigned char sdt_workspace; /* zeroed out by FST reader, for client code use */ unsigned int sxt_workspace; /* zeroed out by FST reader, for client code use */ const char *name; uint32_t length; fstHandle handle; uint32_t name_length; /* strlen(u.var.name) */ unsigned is_alias : 1; } var; /* if htyp == FST_HT_ATTRBEGIN */ struct fstHierAttr { unsigned char typ; /* FST_AT_MIN ... FST_AT_MAX */ unsigned char subtype; /* from fstMiscType, fstArrayType, fstEnumValueType, fstPackType */ const char *name; uint64_t arg; /* number of array elements, struct members, or some other payload (possibly ignored) */ uint64_t arg_from_name; /* for when name is overloaded as a variable-length integer (FST_AT_MISC + FST_MT_SOURCESTEM) */ uint32_t name_length; /* strlen(u.attr.name) */ } attr; } u; }; /* * writer functions */ void fstWriterClose(void *ctx); void * fstWriterCreate(const char *nam, int use_compressed_hier); /* used for Verilog/SV */ fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle); /* future expansion for VHDL and other languages. The variable type, data type, etc map onto the current Verilog/SV one. The "type" string is optional for a more verbose or custom description */ fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt, enum fstSupplementalDataType sdt); void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val); void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len); void fstWriterEmitDumpActive(void *ctx, int enable); void fstWriterEmitTimeChange(void *ctx, uint64_t tim); void fstWriterFlushContext(void *ctx); int fstWriterGetDumpSizeLimitReached(void *ctx); int fstWriterGetFseekFailed(void *ctx); void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg); void fstWriterSetAttrEnd(void *ctx); void fstWriterSetComment(void *ctx, const char *comm); void fstWriterSetDate(void *ctx, const char *dat); void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes); void fstWriterSetEnvVar(void *ctx, const char *envvar); void fstWriterSetFileType(void *ctx, enum fstFileType filetype); void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ); void fstWriterSetParallelMode(void *ctx, int enable); void fstWriterSetRepackOnClose(void *ctx, int enable); /* type = 0 (none), 1 (libz) */ void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp); void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath); void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath); void fstWriterSetTimescale(void *ctx, int ts); void fstWriterSetTimescaleFromString(void *ctx, const char *s); void fstWriterSetTimezero(void *ctx, int64_t tim); void fstWriterSetUpscope(void *ctx); void fstWriterSetValueList(void *ctx, const char *vl); void fstWriterSetVersion(void *ctx, const char *vers); /* * reader functions */ void fstReaderClose(void *ctx); void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx); void fstReaderClrFacProcessMaskAll(void *ctx); uint64_t fstReaderGetAliasCount(void *ctx); const char * fstReaderGetCurrentFlatScope(void *ctx); void * fstReaderGetCurrentScopeUserInfo(void *ctx); int fstReaderGetCurrentScopeLen(void *ctx); const char * fstReaderGetDateString(void *ctx); int fstReaderGetDoubleEndianMatchState(void *ctx); uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx); unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx); uint64_t fstReaderGetEndTime(void *ctx); int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx); int fstReaderGetFileType(void *ctx); int fstReaderGetFseekFailed(void *ctx); fstHandle fstReaderGetMaxHandle(void *ctx); uint64_t fstReaderGetMemoryUsedByWriter(void *ctx); uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx); uint64_t fstReaderGetScopeCount(void *ctx); uint64_t fstReaderGetStartTime(void *ctx); signed char fstReaderGetTimescale(void *ctx); int64_t fstReaderGetTimezero(void *ctx); uint64_t fstReaderGetValueChangeSectionCount(void *ctx); char * fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf); uint64_t fstReaderGetVarCount(void *ctx); const char * fstReaderGetVersionString(void *ctx); struct fstHier *fstReaderIterateHier(void *ctx); int fstReaderIterateHierRewind(void *ctx); int fstReaderIterBlocks(void *ctx, void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value), void *user_callback_data_pointer, FILE *vcdhandle); int fstReaderIterBlocks2(void *ctx, void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value), void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value, uint32_t len), void *user_callback_data_pointer, FILE *vcdhandle); void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable); void * fstReaderOpen(const char *nam); void * fstReaderOpenForUtilitiesOnly(void); const char * fstReaderPopScope(void *ctx); int fstReaderProcessHier(void *ctx, FILE *vcdhandle); const char * fstReaderPushScope(void *ctx, const char *nam, void *user_info); void fstReaderResetScope(void *ctx); void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx); void fstReaderSetFacProcessMaskAll(void *ctx); void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time); void fstReaderSetUnlimitedTimeRange(void *ctx); void fstReaderSetVcdExtensions(void *ctx, int enable); /* * utility functions */ int fstUtilityBinToEsc(unsigned char *d, unsigned char *s, int len); int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len); #ifdef __cplusplus } #endif #endif iverilog-10_1/vpi/lxt2_write.c000066400000000000000000001342431265551621300164160ustar00rootroot00000000000000/* * Copyright (c) 2003-2012 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef _AIX #pragma alloca #endif #include #include "lxt2_write.h" static char *lxt2_wr_vcd_truncate_bitvec(char *s) { char l, r; r=*s; if(r=='1') { return s; } else { s++; } for(;;s++) { l=r; r=*s; if(!r) return (s-1); if(l!=r) { return(((l=='0')&&(r=='1'))?s:s-1); } } } /* * in-place sort to keep chained facs from migrating... */ static void wave_mergesort(struct lxt2_wr_symbol **a, struct lxt2_wr_symbol **b, int lo, int hi) { int i, j, k; if (loname, a[j]->name) <= 0) { a[k++]=b[i++]; } else { a[k++]=a[j++]; } } while (kitem) { if (t->left == NULL) break; if (i < t->left->item) { y = t->left; /* rotate right */ t->left = y->right; y->right = t; t = y; if (t->left == NULL) break; } r->left = t; /* link right */ r = t; t = t->left; } else if (i > t->item) { if (t->right == NULL) break; if (i > t->right->item) { y = t->right; /* rotate left */ t->right = y->left; y->left = t; t = y; if (t->right == NULL) break; } l->right = t; /* link left */ l = t; t = t->right; } else { break; } } l->right = t->left; /* assemble */ r->left = t->right; t->left = N.right; t->right = N.left; return t; } static lxt2_wr_ds_Tree * lxt2_wr_ds_insert(granmsk_t i, lxt2_wr_ds_Tree * t, int val) { /* Insert i into the tree t, unless it's already there. */ /* Return a pointer to the resulting tree. */ lxt2_wr_ds_Tree * n; n = (lxt2_wr_ds_Tree *) calloc (1, sizeof (lxt2_wr_ds_Tree)); if (n == NULL) { fprintf(stderr, "ds_insert: ran out of memory, exiting.\n"); exit(255); } n->item = i; n->val = val; if (t == NULL) { n->left = n->right = NULL; return n; } t = lxt2_wr_ds_splay(i,t); if (i < t->item) { n->left = t->left; n->right = t; t->left = NULL; return n; } else if (i > t->item) { n->right = t->right; n->left = t; t->right = NULL; return n; } else { /* We get here if it's already in the tree */ /* Don't add it again */ free(n); return t; } } /************************ splay ************************/ static int lxt2_wr_dslxt_success; static lxt2_wr_dslxt_Tree * lxt2_wr_dslxt_splay (char *i, lxt2_wr_dslxt_Tree * t) { /* Simple top down splay, not requiring i to be in the tree t. */ /* What it does is described above. */ lxt2_wr_dslxt_Tree N, *l, *r, *y; int dir; lxt2_wr_dslxt_success = 0; if (t == NULL) return t; N.left = N.right = NULL; l = r = &N; for (;;) { dir = strcmp(i, t->item); if (dir < 0) { if (t->left == NULL) break; if (strcmp(i, t->left->item)<0) { y = t->left; /* rotate right */ t->left = y->right; y->right = t; t = y; if (t->left == NULL) break; } r->left = t; /* link right */ r = t; t = t->left; } else if (dir > 0) { if (t->right == NULL) break; if (strcmp(i, t->right->item)>0) { y = t->right; /* rotate left */ t->right = y->left; y->left = t; t = y; if (t->right == NULL) break; } l->right = t; /* link left */ l = t; t = t->right; } else { lxt2_wr_dslxt_success=1; break; } } l->right = t->left; /* assemble */ r->left = t->right; t->left = N.right; t->right = N.left; return t; } static lxt2_wr_dslxt_Tree * lxt2_wr_dslxt_insert(char *i, lxt2_wr_dslxt_Tree * t, unsigned int val) { /* Insert i into the tree t, unless it's already there. */ /* Return a pointer to the resulting tree. */ lxt2_wr_dslxt_Tree * n; int dir; n = (lxt2_wr_dslxt_Tree *) calloc (1, sizeof (lxt2_wr_dslxt_Tree)); if (n == NULL) { fprintf(stderr, "dslxt_insert: ran out of memory, exiting.\n"); exit(255); } n->item = i; n->val = val; if (t == NULL) { n->left = n->right = NULL; return n; } t = lxt2_wr_dslxt_splay(i,t); dir = strcmp(i,t->item); if (dir<0) { n->left = t->left; n->right = t; t->left = NULL; return n; } else if (dir>0) { n->right = t->right; n->left = t; t->right = NULL; return n; } else { /* We get here if it's already in the tree */ /* Don't add it again */ free(n); return t; } } /************************ splay ************************/ /* * functions which emit various big endian * data to a file */ static int lxt2_wr_emit_u8(struct lxt2_wr_trace *lt, int value) { unsigned char buf[1]; int nmemb; buf[0] = value & 0xff; nmemb=fwrite(buf, sizeof(char), 1, lt->handle); lt->position+=nmemb; return(nmemb); } static int lxt2_wr_emit_u16(struct lxt2_wr_trace *lt, int value) { unsigned char buf[2]; int nmemb; buf[0] = (value>>8) & 0xff; buf[1] = value & 0xff; nmemb = fwrite(buf, sizeof(char), 2, lt->handle); lt->position+=nmemb; return(nmemb); } static int lxt2_wr_emit_u32(struct lxt2_wr_trace *lt, int value) { unsigned char buf[4]; int nmemb; buf[0] = (value>>24) & 0xff; buf[1] = (value>>16) & 0xff; buf[2] = (value>>8) & 0xff; buf[3] = value & 0xff; nmemb=fwrite(buf, sizeof(char), 4, lt->handle); lt->position+=nmemb; return(nmemb); } static int lxt2_wr_emit_u64(struct lxt2_wr_trace *lt, int valueh, int valuel) { int rc; if((rc=lxt2_wr_emit_u32(lt, valueh))) { rc=lxt2_wr_emit_u32(lt, valuel); } return(rc); } /* * gzfunctions which emit various big endian * data to a file. (lt->position needs to be * fixed up on gzclose so the tables don't * get out of sync!) */ static int gzwrite_buffered(struct lxt2_wr_trace *lt) { int rc = 1; if(lt->gzbufpnt > LXT2_WR_GZWRITE_BUFFER) { rc = gzwrite(lt->zhandle, lt->gzdest, lt->gzbufpnt); rc = rc ? 1 : 0; lt->gzbufpnt = 0; } return(rc); } static void gzflush_buffered(struct lxt2_wr_trace *lt, int doclose) { if(lt->gzbufpnt) { gzwrite(lt->zhandle, lt->gzdest, lt->gzbufpnt); lt->gzbufpnt = 0; if(!doclose) { gzflush(lt->zhandle, Z_SYNC_FLUSH); } } if(doclose) { gzclose(lt->zhandle); } } static int lxt2_wr_emit_u8z(struct lxt2_wr_trace *lt, int value) { int nmemb; lt->gzdest[lt->gzbufpnt++] = value & 0xff; nmemb=gzwrite_buffered(lt); lt->zpackcount++; lt->position++; return(nmemb); } static int lxt2_wr_emit_u16z(struct lxt2_wr_trace *lt, int value) { int nmemb; lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; lt->gzdest[lt->gzbufpnt++] = value & 0xff; nmemb = gzwrite_buffered(lt); lt->zpackcount+=2; lt->position+=2; return(nmemb); } static int lxt2_wr_emit_u24z(struct lxt2_wr_trace *lt, int value) { int nmemb; lt->gzdest[lt->gzbufpnt++] = (value>>16) & 0xff; lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; lt->gzdest[lt->gzbufpnt++] = value & 0xff; nmemb=gzwrite_buffered(lt); lt->zpackcount+=3; lt->position+=3; return(nmemb); } static int lxt2_wr_emit_u32z(struct lxt2_wr_trace *lt, int value) { int nmemb; lt->gzdest[lt->gzbufpnt++] = (value>>24) & 0xff; lt->gzdest[lt->gzbufpnt++] = (value>>16) & 0xff; lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; lt->gzdest[lt->gzbufpnt++] = value & 0xff; nmemb=gzwrite_buffered(lt); lt->zpackcount+=4; lt->position+=4; return(nmemb); } static int lxt2_wr_emit_u64z(struct lxt2_wr_trace *lt, int valueh, int valuel) { int rc; if((rc=lxt2_wr_emit_u32z(lt, valueh))) { rc=lxt2_wr_emit_u32z(lt, valuel); } return(rc); } static int lxt2_wr_emit_stringz(struct lxt2_wr_trace *lt, char *value) { int rc=1; do { rc&=lxt2_wr_emit_u8z(lt, *value); } while(*(value++)); return(rc); } /* * hash/symtable manipulation */ static int lxt2_wr_hash(const char *s) { const char *p; char ch; unsigned int h=0, h2=0, pos=0, g; for(p=s;*p;p++) { ch=*p; h2<<=3; h2-=((unsigned int)ch+(pos++)); /* this handles stranded vectors quite well.. */ h=(h<<4)+ch; if((g=h&0xf0000000)) { h=h^(g>>24); h=h^g; } } h^=h2; /* combine the two hashes */ return(h%LXT2_WR_SYMPRIME); } static struct lxt2_wr_symbol *lxt2_wr_symadd(struct lxt2_wr_trace *lt, const char *name, int hv) { struct lxt2_wr_symbol *s; s=(struct lxt2_wr_symbol *)calloc(1,sizeof(struct lxt2_wr_symbol)); strcpy(s->name=(char *)malloc((s->namlen=strlen(name))+1),name); s->next=lt->sym[hv]; lt->sym[hv]=s; return(s); } static struct lxt2_wr_symbol *lxt2_wr_symfind(struct lxt2_wr_trace *lt, const char *s) { int hv; struct lxt2_wr_symbol *temp; hv=lxt2_wr_hash(s); if(!(temp=lt->sym[hv])) return(NULL); /* no hash entry, add here wanted to add */ while(temp) { if(!strcmp(temp->name,s)) { return(temp); /* in table already */ } if(!temp->next) break; temp=temp->next; } return(NULL); /* not found, add here if you want to add*/ } /* * compress facs to a prefix count + string + 0x00 */ static void lxt2_wr_compress_fac(struct lxt2_wr_trace *lt, char *str) { int i; int len = strlen(str); int minlen = (lencompress_fac_len) ? len : lt->compress_fac_len; if(minlen>65535) minlen=65535; /* keep in printable range--most hierarchies won't be this big anyway */ if(lt->compress_fac_str) { for(i=0;icompress_fac_str[i]!=str[i]) break; } lxt2_wr_emit_u16z(lt, i); lxt2_wr_emit_stringz(lt, str+i); free(lt->compress_fac_str); } else { lxt2_wr_emit_u16z(lt, 0); lxt2_wr_emit_stringz(lt, str); } lt->compress_fac_str = (char *) malloc((lt->compress_fac_len=len)+1); strcpy(lt->compress_fac_str, str); } /* * emit facs in sorted order along with geometry * and sync table info */ static void strip_brack(struct lxt2_wr_symbol *s) { char *lastch = s->name+s->namlen - 1; if(*lastch!=']') return; if(s->namlen<3) return; lastch--; while(lastch!=s->name) { if(*lastch=='.') { return; /* MTI SV [0.3] notation for implicit vars */ } if(*lastch=='[') { *lastch=0x00; return; } lastch--; } return; } static void lxt2_wr_emitfacs(struct lxt2_wr_trace *lt) { unsigned int i; if((lt)&&(lt->numfacs)) { struct lxt2_wr_symbol *s = lt->symchain; struct lxt2_wr_symbol **aliascache = calloc(lt->numalias ? lt->numalias : 1, sizeof(struct lxt2_wr_symbol *)); unsigned int aliases_encountered, facs_encountered; lt->sorted_facs = (struct lxt2_wr_symbol **)calloc(lt->numfacs, sizeof(struct lxt2_wr_symbol *)); if(lt->sorted_facs && aliascache) { if(lt->do_strip_brackets) for(i=0;inumfacs;i++) { lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ strip_brack(s); s=s->symchain; } else for(i=0;inumfacs;i++) { lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ s=s->symchain; } wave_msort(lt->sorted_facs, lt->numfacs); if(lt->partial_preference) { /* move preferenced facs up */ struct lxt2_wr_symbol **prefcache = aliascache; int prefs_encountered = 0; facs_encountered = 0; for(i=0;inumfacs;i++) { if((lt->sorted_facs[i]->partial_preference)==0) { lt->sorted_facs[facs_encountered] = lt->sorted_facs[i]; facs_encountered++; } else { prefcache[prefs_encountered] = lt->sorted_facs[i]; prefs_encountered++; } } /* then append the non-preferenced facs */ for(i=0;isorted_facs[i]; } /* now make prefcache the main cache */ aliascache = lt->sorted_facs; lt->sorted_facs = prefcache; } /* move facs up */ aliases_encountered = 0, facs_encountered = 0; for(i=0;inumfacs;i++) { if((lt->sorted_facs[i]->flags&LXT2_WR_SYM_F_ALIAS)==0) { lt->sorted_facs[facs_encountered] = lt->sorted_facs[i]; facs_encountered++; } else { aliascache[aliases_encountered] = lt->sorted_facs[i]; aliases_encountered++; } } /* then append the aliases */ for(i=0;isorted_facs[facs_encountered+i] = aliascache[i]; } for(i=0;inumfacs;i++) { lt->sorted_facs[i]->facnum = i; } if(!lt->timezero) { lxt2_wr_emit_u32(lt, lt->numfacs); /* uncompressed */ } else { lxt2_wr_emit_u32(lt, 0); /* uncompressed, flag to insert extra parameters */ lxt2_wr_emit_u32(lt, 8); /* uncompressed 8 counts timezero and on */ lxt2_wr_emit_u32(lt, lt->numfacs); /* uncompressed */ lxt2_wr_emit_u64(lt, (lt->timezero >> 32) & 0xffffffffL, lt->timezero & 0xffffffffL); /* uncompressed */ } lxt2_wr_emit_u32(lt, lt->numfacbytes); /* uncompressed */ lxt2_wr_emit_u32(lt, lt->longestname); /* uncompressed */ lt->facname_offset=lt->position; lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacnamesize */ lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacname_predec_size */ lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacgeometrysize */ lxt2_wr_emit_u8(lt, lt->timescale); /* timescale (-9 default == nsec) */ fflush(lt->handle); lt->zfacname_size = lt->position; lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt->zpackcount = 0; for(i=0;inumfacs;i++) { lxt2_wr_compress_fac(lt, lt->sorted_facs[i]->name); free(lt->sorted_facs[i]->name); lt->sorted_facs[i]->name = NULL; } free(lt->compress_fac_str); lt->compress_fac_str=NULL; lt->compress_fac_len=0; lt->zfacname_predec_size = lt->zpackcount; gzflush_buffered(lt, 1); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->zfacname_size = lt->position - lt->zfacname_size; lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt->facgeometry_offset = lt->position; for(i=0;inumfacs;i++) { if((lt->sorted_facs[i]->flags&LXT2_WR_SYM_F_ALIAS)==0) { lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->rows); lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->msb); lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->lsb); lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->flags); } else { lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->aliased_to->facnum); lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->msb); lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->lsb); lxt2_wr_emit_u32z(lt, LXT2_WR_SYM_F_ALIAS); } } gzflush_buffered(lt, 1); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->break_header_size = lt->position; /* in case we need to emit multiple lxt2s with same header */ lt->zfacgeometry_size = lt->position - lt->facgeometry_offset; fseeko(lt->handle, lt->facname_offset, SEEK_SET); lxt2_wr_emit_u32(lt, lt->zfacname_size); /* backpatch sizes... */ lxt2_wr_emit_u32(lt, lt->zfacname_predec_size); lxt2_wr_emit_u32(lt, lt->zfacgeometry_size); lt->numfacs = facs_encountered; /* don't process alias value changes ever */ } free(aliascache); } } /* * initialize the trace and get back an lt context */ struct lxt2_wr_trace *lxt2_wr_init(const char *name) { struct lxt2_wr_trace *lt=(struct lxt2_wr_trace *)calloc(1, sizeof(struct lxt2_wr_trace)); if((!name)||(!(lt->handle=fopen(name, "wb")))) { free(lt); lt=NULL; } else { lt->lxtname = strdup(name); lxt2_wr_emit_u16(lt, LXT2_WR_HDRID); lxt2_wr_emit_u16(lt, LXT2_WR_VERSION); lxt2_wr_emit_u8 (lt, LXT2_WR_GRANULE_SIZE); /* currently 32 or 64 */ lt->timescale = -9; lt->maxgranule = LXT2_WR_GRANULE_NUM; lxt2_wr_set_compression_depth(lt, 4); /* set fast/loose compression depth, user can fix this any time after init */ lt->initial_value = 'x'; } return(lt); } /* * setting break size */ void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz) { if(lt) { lt->break_size = siz; } } /* * enable/disable partial dump mode (for faster reads) */ void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt) { if(lt) { lt->partial = 0; lt->partial_zip = 0; } } void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode) { if(lt) { lt->partial = 1; lt->partial_zip = (zipmode != 0); lt->partial_iter = LXT2_WR_PARTIAL_SIZE; } } void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name) { struct lxt2_wr_symbol *s; if((lt)&&(name)&&(!lt->sorted_facs)) { s=lxt2_wr_symfind(lt, name); if(s) { while(s->aliased_to) /* find root alias */ { s=s->aliased_to; } s->partial_preference = 1; } } } /* * enable/disable checkpointing (for smaller files) */ void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt) { if(lt) { lt->no_checkpoint = 1; } } void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt) { if(lt) { lt->no_checkpoint = 0; } } /* * set initial value of trace (0, 1, x, z) only legal vals */ void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value) { if(lt) { switch(value) { case '0': case '1': case 'x': case 'z': break; case 'Z': value = 'z'; break; default: value = 'x'; break; } lt->initial_value = value; } } /* * maint function for finding a symbol if it exists */ struct lxt2_wr_symbol *lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name) { struct lxt2_wr_symbol *s=NULL; if((lt)&&(name)) s=lxt2_wr_symfind(lt, name); return(s); } /* * add a trace (if it doesn't exist already) */ struct lxt2_wr_symbol *lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags) { struct lxt2_wr_symbol *s; int len; int flagcnt; if((!lt)||(lt->sorted_facs)) return(NULL); flagcnt = ((flags&LXT2_WR_SYM_F_INTEGER)!=0) + ((flags&LXT2_WR_SYM_F_DOUBLE)!=0) + ((flags&LXT2_WR_SYM_F_STRING)!=0); if((flagcnt>1)||(!lt)||(!name)||(lxt2_wr_symfind(lt, name))) return (NULL); s=lxt2_wr_symadd(lt, name, lxt2_wr_hash(name)); s->rows = rows; s->flags = flags&(~LXT2_WR_SYM_F_ALIAS); /* aliasing makes no sense here.. */ if(!flagcnt) { s->msb = msb; s->lsb = lsb; s->len = (msbvalue = strdup("NaN"); } else { if(flags & LXT2_WR_SYM_F_INTEGER) { s->len = 32; } s->value = malloc(s->len + 1); memset(s->value, lt->initial_value, s->len); s->value[s->len]=0; s->msk = LXT2_WR_GRAN_1VAL; /* stuff in an initial value */ switch(lt->initial_value) { case '0': s->chg[0] = LXT2_WR_ENC_0; break; case '1': s->chg[0] = LXT2_WR_ENC_1; break; case 'z': s->chg[0] = LXT2_WR_ENC_Z; break; default: s->chg[0] = LXT2_WR_ENC_X; break; } s->chgpos++; /* don't worry that a time doesn't exist as it will soon enough.. */ } s->symchain = lt->symchain; lt->symchain = s; lt->numfacs++; if((len=strlen(name)) > lt->longestname) lt->longestname = len; lt->numfacbytes += (len+1); return(s); } /* * add an alias trace (if it doesn't exist already and orig is found) */ struct lxt2_wr_symbol *lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb) { struct lxt2_wr_symbol *s, *sa; int len; int bitlen; int flagcnt; if((!lt)||(!existing_name)||(!alias)||(!(s=lxt2_wr_symfind(lt, existing_name)))||(lxt2_wr_symfind(lt, alias))) return (NULL); if(lt->sorted_facs) return(NULL); while(s->aliased_to) /* find root alias */ { s=s->aliased_to; } flagcnt = ((s->flags&LXT2_WR_SYM_F_INTEGER)!=0) + ((s->flags&LXT2_WR_SYM_F_DOUBLE)!=0) + ((s->flags&LXT2_WR_SYM_F_STRING)!=0); bitlen = (msblen)) return(NULL); sa=lxt2_wr_symadd(lt, alias, lxt2_wr_hash(alias)); sa->flags = LXT2_WR_SYM_F_ALIAS; /* only point this can get set */ sa->aliased_to = s; if(!flagcnt) { sa->msb = msb; sa->lsb = lsb; sa->len = bitlen; } sa->symchain = lt->symchain; lt->symchain = sa; lt->numfacs++; lt->numalias++; if((len=strlen(alias)) > lt->longestname) lt->longestname = len; lt->numfacbytes += (len+1); return(sa); } /* * set current time/granule updating */ int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval) { return(lxt2_wr_set_time64(lt, lt->maxtime + (lxttime_t)timeval)); } int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval) { return(lxt2_wr_set_time64(lt, (lxttime_t)timeval)); } int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval) { return(lxt2_wr_set_time64(lt, lt->maxtime + timeval)); } /* * file size limiting/header cloning... */ static void lxt2_wr_emit_do_breakfile(struct lxt2_wr_trace *lt) { unsigned int len = strlen(lt->lxtname); int i; char *tname = malloc(len + 30); FILE *f2, *clone; off_t cnt, seg; char buf[32768]; for(i=len;i>0;i--) { if(lt->lxtname[i]=='.') break; } if(!i) { sprintf(tname, "%s_%03d.lxt", lt->lxtname, ++lt->break_number); } else { memcpy(tname, lt->lxtname, i); sprintf(tname+i, "_%03d.lxt", ++lt->break_number); } f2 = fopen(tname, "wb"); if(!f2) /* if error, keep writing to same output file...sorry */ { free(tname); return; } clone = fopen(lt->lxtname, "rb"); if(!clone) { /* this should never happen */ fclose(f2); unlink(tname); free(tname); return; } /* clone original header */ for(cnt = 0; cnt < lt->break_header_size; cnt += sizeof(buf)) { seg = lt->break_header_size - cnt; if(seg > (off_t)sizeof(buf)) { seg = sizeof(buf); } if(fread(buf, seg, 1, clone)) { if(!fwrite(buf, seg, 1, f2)) break; /* write error! */ } } fclose(clone); fclose(lt->handle); lt->handle = f2; free(tname); } /* * emit granule */ void lxt2_wr_flush_granule(struct lxt2_wr_trace *lt, int do_finalize) { unsigned int idx_nbytes, map_nbytes, i, j; struct lxt2_wr_symbol *s; unsigned int partial_iter; unsigned int iter, iter_hi; unsigned char using_partial, using_partial_zip=0; off_t current_iter_pos=0; int early_flush; if(lt->flush_valid) { if(lt->flushtime == lt->lasttime) { return; } lt->flush_valid = 0; } lt->granule_dirty = 0; if((using_partial=(lt->partial)&&(lt->numfacs>lt->partial_iter))) { partial_iter = lt->partial_iter; using_partial_zip = lt->partial_zip; } else { partial_iter = lt->numfacs; } if(!lt->timegranule) { int attempt_break_state = 2; do { fseeko(lt->handle, 0L, SEEK_END); lt->current_chunk=lt->position = ftello(lt->handle); if((lt->break_size)&&(attempt_break_state==2)&&(lt->position >= lt->break_size)&&(lt->position != lt->break_header_size)) { lxt2_wr_emit_do_breakfile(lt); attempt_break_state--; } else { attempt_break_state = 0; } } while(attempt_break_state); /* fprintf(stderr, "First chunk position is %d (0x%08x)\n", lt->current_chunk, lt->current_chunk); */ lxt2_wr_emit_u32(lt, 0); /* size of this section (uncompressed) */ lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ lxt2_wr_emit_u64(lt, 0, 0); /* begin time of section */ lxt2_wr_emit_u64(lt, 0, 0); /* end time of section */ fflush(lt->handle); lt->current_chunkz = lt->position; /* fprintf(stderr, "First chunkz position is %d (0x%08x)\n", lt->current_chunkz, lt->current_chunkz); */ if(!using_partial_zip) { lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); } else { lt->zpackcount_cumulative = 0; } lt->zpackcount = 0; } for(iter=0; iternumfacs; iter=iter_hi) { unsigned int total_chgs; unsigned int partial_length; total_chgs = 0; /* partial_length = 0; */ /* scan-build : never read */ iter_hi = iter + partial_iter; if(iter_hi > lt->numfacs) iter_hi = lt->numfacs; for(j=iter;jsorted_facs[j]->msk; lt->mapdict = lxt2_wr_ds_splay (msk, lt->mapdict); if((!lt->mapdict)||(lt->mapdict->item != msk)) { lt->mapdict = lxt2_wr_ds_insert(msk, lt->mapdict, lt->num_map_entries); lt->num_map_entries++; if(lt->mapdict_curr) { lt->mapdict_curr->next = lt->mapdict; lt->mapdict_curr = lt->mapdict; } else { lt->mapdict_head = lt->mapdict_curr = lt->mapdict; } } } if(lt->num_map_entries <= 256) { map_nbytes = 1; } else if(lt->num_map_entries <= 256*256) { map_nbytes = 2; } else if(lt->num_map_entries <= 256*256*256) { map_nbytes = 3; } else { map_nbytes = 4; } if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256) { idx_nbytes = 1; } else if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256*256) { idx_nbytes = 2; } else if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256*256*256) { idx_nbytes = 3; } else { idx_nbytes = 4; } if(using_partial) { /* skip */ partial_length = 1 + /* lt->timepos */ lt->timepos * sizeof(lxttime_t)+ /* timevals */ 1 + /* map_nbytes */ (iter_hi-iter) * map_nbytes + /* actual map */ 1; /* idx_nbytes */ for(j=iter;jsorted_facs[j]; total_chgs += s->chgpos; } total_chgs *= idx_nbytes; /* vch skip */ partial_length += total_chgs; /* actual changes */ if(using_partial_zip) { fseeko(lt->handle, 0L, SEEK_END); current_iter_pos = ftello(lt->handle); lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ lxt2_wr_emit_u32(lt, partial_length+9); /* size of this section (uncompressed) */ lxt2_wr_emit_u32(lt, iter); /* begin iter of section */ fflush(lt->handle); lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); lt->zpackcount = 0; } lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_TIME_PARTIAL); lxt2_wr_emit_u32z(lt, iter); lxt2_wr_emit_u32z(lt, partial_length); } else { lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_TIME); } lxt2_wr_emit_u8z(lt, lt->timepos); for(i=0;itimepos;i++) { lxt2_wr_emit_u64z(lt, (lt->timetable[i]>>32)&0xffffffff, lt->timetable[i]&0xffffffff); } gzflush_buffered(lt, 0); lxt2_wr_emit_u8z(lt, map_nbytes); for(j=iter;jsorted_facs[j]; lt->mapdict = lxt2_wr_ds_splay (s->msk, lt->mapdict); val = lt->mapdict->val; switch(map_nbytes) { case 1: lxt2_wr_emit_u8z(lt, val); break; case 2: lxt2_wr_emit_u16z(lt, val); break; case 3: lxt2_wr_emit_u24z(lt, val); break; case 4: lxt2_wr_emit_u32z(lt, val); break; } s->msk = LXT2_WR_GRAN_0VAL; } lxt2_wr_emit_u8z(lt, idx_nbytes); gzflush_buffered(lt, 0); for(j=iter;jsorted_facs[j]; for(i=0;ichgpos;i++) { switch(idx_nbytes) { case 1: lxt2_wr_emit_u8z (lt, s->chg[i]); break; case 2: lxt2_wr_emit_u16z(lt, s->chg[i]); break; case 3: lxt2_wr_emit_u24z(lt, s->chg[i]); break; case 4: lxt2_wr_emit_u32z(lt, s->chg[i]); break; } } s->chgpos = 0; } if(using_partial_zip) { off_t clen; gzflush_buffered(lt, 1); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); clen = lt->position - current_iter_pos - 12; fseeko(lt->handle, current_iter_pos, SEEK_SET); lt->zpackcount_cumulative+=lt->zpackcount; lxt2_wr_emit_u32(lt, clen); } else { gzflush_buffered(lt, 0); } } /* ...for(iter) */ lt->timepos = 0; lt->timegranule++; if(lt->break_size) { early_flush = (ftello(lt->handle) >= lt->break_size); } else { early_flush = 0; } if((lt->timegranule>=lt->maxgranule)||(do_finalize)||(early_flush)) { off_t unclen, clen; lxt2_wr_ds_Tree *dt, *dt2; lxt2_wr_dslxt_Tree *ds, *ds2; if(using_partial_zip) { fseeko(lt->handle, 0L, SEEK_END); current_iter_pos = ftello(lt->handle); lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ lxt2_wr_emit_u32(lt, 0); /* size of this section (uncompressed) */ lxt2_wr_emit_u32(lt, ~0); /* control section */ fflush(lt->handle); lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); lt->zpackcount = 0; } /* fprintf(stderr, "reached granule %d, finalizing block for section %d\n", lt->timegranule, lt->numsections); */ lt->numsections++; /* finalize string dictionary */ lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_DICT); ds = lt->dict_head; /* fprintf(stderr, "num_dict_entries: %d\n", lt->num_dict_entries); */ gzflush_buffered(lt, 0); for(i=0;inum_dict_entries;i++) { /* fprintf(stderr, "%8d %8d) '%s'\n", ds->val, i, ds->item); */ if(ds->val != i) { fprintf(stderr, "internal error line %d\n", __LINE__); exit(255); } lxt2_wr_emit_stringz(lt, ds->item); ds2 = ds->next; free(ds->item); free(ds); ds = ds2; } lt->dict_head = lt->dict_curr = lt->dict = NULL; /* finalize map dictionary */ dt = lt->mapdict_head; /* fprintf(stderr, "num_map_entries: %d\n", lt->num_map_entries); */ gzflush_buffered(lt, 0); for(i=0;inum_map_entries;i++) { /* fprintf(stderr, "+++ %08x (%d)(%d)\n", dt->item, i, dt->val); */ if(((unsigned int)dt->val) != i) { fprintf(stderr, "internal error line %d\n", __LINE__); exit(255); } #if LXT2_WR_GRANULE_SIZE > 32 lxt2_wr_emit_u64z(lt, (dt->item>>32)&0xffffffff, dt->item&0xffffffff); #else lxt2_wr_emit_u32z(lt, dt->item); #endif dt2 = dt->next; free(dt); dt = dt2; } lt->mapdict_head = lt->mapdict_curr = lt->mapdict = NULL; lxt2_wr_emit_u32z(lt, lt->num_dict_entries); /* -12 */ lxt2_wr_emit_u32z(lt, lt->dict_string_mem_required); /* -8 */ lxt2_wr_emit_u32z(lt, lt->num_map_entries); /* -4 */ lt->num_map_entries = 0; lt->num_dict_entries = lt->dict_string_mem_required = 0; /* fprintf(stderr, "returned from finalize..\n"); */ if(using_partial_zip) { off_t c_len; gzflush_buffered(lt, 1); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); c_len = lt->position - current_iter_pos - 12; fseeko(lt->handle, current_iter_pos, SEEK_SET); lt->zpackcount_cumulative+=lt->zpackcount; lxt2_wr_emit_u32(lt, c_len); lxt2_wr_emit_u32(lt, lt->zpackcount); } else { gzflush_buffered(lt, 1); } fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); /* fprintf(stderr, "file position after dumping dict: %d 0x%08x\n", lt->position, lt->position); */ unclen = lt->zpackcount; clen = lt->position - lt->current_chunkz; /* fprintf(stderr, "%d/%d un/compressed bytes in section\n", unclen, clen); */ fseeko(lt->handle, lt->current_chunk, SEEK_SET); if(using_partial_zip) { lxt2_wr_emit_u32(lt, lt->zpackcount_cumulative); lxt2_wr_emit_u32(lt, clen); } else { lxt2_wr_emit_u32(lt, unclen); lxt2_wr_emit_u32(lt, clen); } lxt2_wr_emit_u64(lt, (lt->firsttime>>32)&0xffffffff, lt->firsttime&0xffffffff); lxt2_wr_emit_u64(lt, (lt->lasttime>>32)&0xffffffff, lt->lasttime&0xffffffff); /* fprintf(stderr, "start: %lld, end %lld\n", lt->firsttime, lt->lasttime); */ lt->timegranule=0; lt->numblock++; } if(do_finalize) { lt->flush_valid = 1; lt->flushtime = lt->lasttime; } } int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval) { int rc=0; if(lt) { if(lt->timeset) { if(timeval > lt->maxtime) { if(lt->bumptime) { lt->bumptime = 0; if(!lt->flush_valid) { lt->timepos++; } else { lt->flush_valid = 0; } if(lt->timepos == LXT2_WR_GRANULE_SIZE) { /* fprintf(stderr, "flushing granule to disk at time %d\n", (unsigned int)timeval); */ lxt2_wr_flush_granule(lt, 0); } } /* fprintf(stderr, "updating time to %d (%d dict entries/%d bytes)\n", (unsigned int)timeval, lt->num_dict_entries, lt->dict_string_mem_required); */ lt->timetable[lt->timepos] = timeval; lt->lasttime = timeval; } } else { lt->timeset = 1; lt->mintime = lt->maxtime = timeval; lt->timetable[lt->timepos] = timeval; } if( (!lt->timepos) && (!lt->timegranule) ) { lt->firsttime = timeval; lt->lasttime = timeval; } if( (!lt->timepos) && (!lt->timegranule) && ((!lt->numblock)||(!lt->no_checkpoint)) ) { /* fprintf(stderr, "initial value burst timepos==0, timegranule==0\n"); */ if(lt->blackout) { lt->blackout = 0; lxt2_wr_set_dumpoff(lt); } else { struct lxt2_wr_symbol *s = lt->symchain; while(s) { if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) { if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) { lxt2_wr_emit_value_bit_string(lt, s, 0, s->value); } else if (s->flags&LXT2_WR_SYM_F_DOUBLE) { double value = 0; sscanf(s->value, "%lg", &value); errno = 0; lxt2_wr_emit_value_double(lt, s, 0, value); } else if (s->flags&LXT2_WR_SYM_F_STRING) { lxt2_wr_emit_value_string(lt, s, 0, s->value); } } s=s->symchain; } } /* fprintf(stderr, "done initial value burst timepos==0, timegranule==0\n"); */ } lt->granule_dirty = 1; rc = 1; } return(rc); } /* * sets trace timescale as 10**x seconds */ void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale) { if(lt) { lt->timescale = timescale; } } /* * set number of granules per section * (can modify dynamically) */ void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule) { if(lt) { if(!maxgranule) maxgranule = ~0; lt->maxgranule = maxgranule; } } /* * Sets bracket stripping (useful for VCD conversions of * bitblasted nets) */ void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit) { if(lt) { lt->do_strip_brackets = (doit!=0); } } static char *lxt2_wr_expand_integer_to_bits(unsigned int len, int value) { static char s[33]; char *p = s; unsigned int i; if(len>32) len=32; len--; for(i=0;i<=len;i++) { *(p++) = '0' | ((value & (1<<(len-i)))!=0); } *p = 0; return(s); } int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value) { int rc=0; if((!lt)||(lt->blackout)||(!s)||(row)) return(rc); return(lxt2_wr_emit_value_bit_string(lt, s, row, lxt2_wr_expand_integer_to_bits(s->len, value))); } int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value) { int rc=0; if((!lt)||(lt->blackout)||(!s)||(row)) return(rc); if(!lt->emitted) { lxt2_wr_emitfacs(lt); lt->emitted = 1; if(!lt->timeset) { lxt2_wr_set_time(lt, 0); } } while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } if(s->flags&LXT2_WR_SYM_F_DOUBLE) { char d_buf[32]; unsigned int idx; rc = 1; sprintf(d_buf, "%.16g", value); if(!strcmp(d_buf, s->value)) return(rc); lt->bumptime = 1; free(s->value); s->value = strdup(d_buf); lt->dict = lxt2_wr_dslxt_splay (s->value, lt->dict); if(!lxt2_wr_dslxt_success) { unsigned int vlen = strlen(d_buf)+1; char *vcopy = (char *)malloc(vlen); strcpy(vcopy, d_buf); lt->dict_string_mem_required += vlen; lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); if(lt->dict_curr) { lt->dict_curr->next = lt->dict; lt->dict_curr = lt->dict; } else { lt->dict_head = lt->dict_curr = lt->dict; } idx = lt->num_dict_entries + LXT2_WR_DICT_START; lt->num_dict_entries++; } else { idx = lt->dict->val + LXT2_WR_DICT_START; } if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) { s->msk |= (LXT2_WR_GRAN_1VAL<timepos); s->chg[s->chgpos] = idx; s->chgpos++; } else { s->chg[s->chgpos-1] = idx; } lt->granule_dirty = 1; } return(rc); } int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value) { int rc=0; if((!lt)||(lt->blackout)||(!s)||(!value)||(row)) return(rc); if(!lt->emitted) { lxt2_wr_emitfacs(lt); lt->emitted = 1; if(!lt->timeset) { lxt2_wr_set_time(lt, 0); } } while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } if(s->flags&LXT2_WR_SYM_F_STRING) { unsigned int idx; rc = 1; if(!strcmp(value, s->value)) return(rc); lt->bumptime = 1; free(s->value); s->value = strdup(value); lt->dict = lxt2_wr_dslxt_splay (s->value, lt->dict); if(!lxt2_wr_dslxt_success) { unsigned int vlen = strlen(value)+1; char *vcopy = (char *)malloc(vlen); strcpy(vcopy, value); lt->dict_string_mem_required += vlen; lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); if(lt->dict_curr) { lt->dict_curr->next = lt->dict; lt->dict_curr = lt->dict; } else { lt->dict_head = lt->dict_curr = lt->dict; } idx = lt->num_dict_entries + LXT2_WR_DICT_START; lt->num_dict_entries++; } else { idx = lt->dict->val + LXT2_WR_DICT_START; } if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) { s->msk |= (LXT2_WR_GRAN_1VAL<timepos); s->chg[s->chgpos] = idx; s->chgpos++; } else { s->chg[s->chgpos-1] = idx; } lt->granule_dirty = 1; } return(rc); } int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value) { int rc=0; char *vpnt; char *vfix; int valuelen; int i; if((!lt)||(lt->blackout)||(!s)||(!value)||(!*value)||(row)) return(rc); if(!lt->emitted) { lxt2_wr_emitfacs(lt); lt->emitted = 1; if(!lt->timeset) { lxt2_wr_set_time(lt, 0); } } while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } valuelen = strlen(value); /* ensure string is proper length */ if(valuelen == s->len) { vfix = wave_alloca(s->len+1); strcpy(vfix, value); value = vfix; } else { vfix = wave_alloca(s->len+1); if(valuelen < s->len) { int lendelta = s->len - valuelen; memset(vfix, (value[0]!='1') ? value[0] : '0', lendelta); strcpy(vfix+lendelta, value); } else { memcpy(vfix, value, s->len); vfix[s->len] = 0; } value = vfix; } for(i=0;ilen;i++) { unsigned char ch = value[i]; if((ch>='A')&&(ch<='Z')) value[i] = ch + ('a'-'A'); } if ( (lt->timepos || lt->timegranule) && !strcmp(value, s->value) ) { return(1); /* redundant value change */ } if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) { char prevch; int idx; lt->bumptime = 1; vpnt = value; prevch = *vpnt; while(*vpnt) { if(prevch == *vpnt) { vpnt++; } else { prevch = 0; break; } } switch(prevch) { case '0': idx = LXT2_WR_ENC_0; break; case '1': idx = LXT2_WR_ENC_1; break; case 'X': case 'x': idx = LXT2_WR_ENC_X; break; case 'Z': case 'z': idx = LXT2_WR_ENC_Z; break; default: idx = -1; break; } if((lt->timepos)||(lt->timegranule)) { for(i=0;ilen;i++) { char ch = value[i]; switch(ch) { case '0': if(s->value[i]!='1') goto nextalg; else break; case '1': if(s->value[i]!='0') goto nextalg; else break; default: goto nextalg; } } idx = LXT2_WR_ENC_INV; goto do_enc; nextalg: if(s->len > 1) { if(!memcmp(s->value+1, value, s->len-1)) { if((value[s->len-1]&0xfe)=='0') { idx = LXT2_WR_ENC_LSH0 + (value[s->len-1]&0x01); goto do_enc; } } else if(!memcmp(s->value, value+1, s->len-1)) { if((value[0]&0xfe)=='0') { idx = LXT2_WR_ENC_RSH0 + (value[0]&0x01); goto do_enc; } } if(s->len <= 32) { unsigned int intval_old = 0, intval_new = 0; unsigned int msk; for(i=0;ilen;i++) { char ch = value[i]; if((ch!='0')&&(ch!='1')) goto idxchk; intval_new <<= 1; intval_new |= ((unsigned int)(ch&1)); ch = s->value[i]; if((ch!='0')&&(ch!='1')) goto idxchk; intval_old <<= 1; intval_old |= ((unsigned int)(ch&1)); } msk = (~0)>>(32-s->len); if( ((intval_old+1)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD1; goto do_enc; } if( ((intval_old-1)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB1; goto do_enc; } if( ((intval_old+2)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD2; goto do_enc; } if( ((intval_old-2)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB2; goto do_enc; } if( ((intval_old+3)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD3; goto do_enc; } if( ((intval_old-3)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB3; goto do_enc; } if(s->len > 2) { if( ((intval_old+4)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD4; goto do_enc; } if( ((intval_old-4)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB4; goto do_enc; } } } } } idxchk: if(idx<0) { vpnt = lxt2_wr_vcd_truncate_bitvec(value); lt->dict = lxt2_wr_dslxt_splay (vpnt, lt->dict); if(!lxt2_wr_dslxt_success) { unsigned int vlen = strlen(vpnt)+1; char *vcopy = (char *)malloc(vlen); strcpy(vcopy, vpnt); lt->dict_string_mem_required += vlen; lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); if(lt->dict_curr) { lt->dict_curr->next = lt->dict; lt->dict_curr = lt->dict; } else { lt->dict_head = lt->dict_curr = lt->dict; } idx = lt->num_dict_entries + LXT2_WR_DICT_START; lt->num_dict_entries++; } else { idx = lt->dict->val + LXT2_WR_DICT_START; } } do_enc: if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) { s->msk |= (LXT2_WR_GRAN_1VAL<timepos); s->chg[s->chgpos] = idx; s->chgpos++; } else { s->chg[s->chgpos-1] = idx; } strncpy(s->value, value, s->len); lt->granule_dirty = 1; } return(rc); } /* * dumping control */ void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt) { struct lxt2_wr_symbol *s; if((lt)&&(!lt->blackout)) { if(!lt->emitted) { lxt2_wr_emitfacs(lt); lt->emitted = 1; if(!lt->timeset) { lxt2_wr_set_time(lt, 0); } } s = lt->symchain; while(s) { if(!(s->flags&LXT2_WR_SYM_F_ALIAS)) { if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) { s->msk |= (LXT2_WR_GRAN_1VAL<timepos); s->chg[s->chgpos] = LXT2_WR_ENC_BLACKOUT; s->chgpos++; } else { s->chg[s->chgpos-1] = LXT2_WR_ENC_BLACKOUT; } } s=s->symchain; } lt->bumptime = 1; lt->blackout = 1; lt->granule_dirty = 1; } } void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt) { int i; struct lxt2_wr_symbol *s; if((lt)&&(lt->blackout)) { lt->blackout = 0; s = lt->symchain; while(s) { if(!(s->flags&LXT2_WR_SYM_F_ALIAS)) { if(s->flags&LXT2_WR_SYM_F_DOUBLE) { free(s->value); s->value = strdup("0"); /* will cause mismatch then flush */ } else { if(!(s->flags&LXT2_WR_SYM_F_STRING)) { s->value[0] = '-'; /* will cause mismatch then flush */ for(i=1;ilen;i++) { s->value[i] = 'x'; /* initial value */ } s->value[i]=0; } else { free(s->value); s->value = calloc(1, 1*sizeof(char)); } } } s=s->symchain; } s = lt->symchain; while(s) { if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) { if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) { lxt2_wr_emit_value_bit_string(lt, s, 0, "x"); } else if (s->flags&LXT2_WR_SYM_F_DOUBLE) { double value; sscanf("NaN", "%lg", &value); lxt2_wr_emit_value_double(lt, s, 0, value); } else if (s->flags&LXT2_WR_SYM_F_STRING) { lxt2_wr_emit_value_string(lt, s, 0, "UNDEF"); } } s=s->symchain; } } } /* * flush the trace... */ void lxt2_wr_flush(struct lxt2_wr_trace *lt) { if(lt) { if((lt->timegranule)||(lt->timepos > 0)) { if(lt->granule_dirty) { lt->timepos++; lxt2_wr_flush_granule(lt, 1); } } } } /* * close out the trace and fixate it */ void lxt2_wr_close(struct lxt2_wr_trace *lt) { if(lt) { if(lt->granule_dirty) { lt->timepos++; lxt2_wr_flush_granule(lt, 1); } if(lt->symchain) { struct lxt2_wr_symbol *s = lt->symchain; struct lxt2_wr_symbol *s2; while(s) { free(s->name); free(s->value); s2=s->symchain; free(s); s=s2; } lt->symchain=NULL; } free(lt->lxtname); free(lt->sorted_facs); fclose(lt->handle); free(lt); } } /* * set compression depth */ void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth) { if(lt) { if(depth > 9) depth = 9; sprintf(lt->zmode, "wb%u", depth); } } /* * time zero offset */ void lxt2_wr_set_timezero(struct lxt2_wr_trace *lt, lxtstime_t timeval) { if(lt) { lt->timezero = timeval; } } iverilog-10_1/vpi/lxt2_write.h000066400000000000000000000232151265551621300164170ustar00rootroot00000000000000/* * Copyright (c) 2003-2012 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef DEFS_LXTW_H #define DEFS_LXTW_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #ifdef HAVE_INTTYPES_H #include #endif #include #ifndef HAVE_FSEEKO #define fseeko fseek #define ftello ftell #endif #include #define LXT2_WR_HDRID (0x1380) #define LXT2_WR_VERSION (0x0001) #define LXT2_WR_GRANULE_SIZE (64) #define LXT2_WR_GRANULE_NUM (256) #define LXT2_WR_PARTIAL_SIZE (2048) #define LXT2_WR_GRAN_SECT_TIME 0 #define LXT2_WR_GRAN_SECT_DICT 1 #define LXT2_WR_GRAN_SECT_TIME_PARTIAL 2 #define LXT2_WR_GZWRITE_BUFFER 4096 #define LXT2_WR_SYMPRIME 500009 typedef uint64_t lxttime_t; typedef int64_t lxtstime_t; #ifndef _MSC_VER #ifdef __MINGW32__ #define LXT2_WR_LLD "%I64d" #else #define LXT2_WR_LLD "%lld" #endif #define LXT2_WR_LLDESC(x) x##LL #define LXT2_WR_ULLDESC(x) x##ULL #else #define LXT2_WR_LLD "%I64d" #define LXT2_WR_LLDESC(x) x##i64 #define LXT2_WR_ULLDESC(x) x##i64 #endif #if LXT2_WR_GRANULE_SIZE > 32 typedef unsigned long long granmsk_t; #define LXT2_WR_GRAN_0VAL (LXT2_WR_ULLDESC(0)) #define LXT2_WR_GRAN_1VAL (LXT2_WR_ULLDESC(1)) #else typedef unsigned int granmsk_t; #define LXT2_WR_GRAN_0VAL (0) #define LXT2_WR_GRAN_1VAL (1) #endif enum LXT2_WR_Encodings { LXT2_WR_ENC_0, LXT2_WR_ENC_1, LXT2_WR_ENC_INV, LXT2_WR_ENC_LSH0, LXT2_WR_ENC_LSH1, LXT2_WR_ENC_RSH0, LXT2_WR_ENC_RSH1, LXT2_WR_ENC_ADD1, LXT2_WR_ENC_ADD2, LXT2_WR_ENC_ADD3, LXT2_WR_ENC_ADD4, LXT2_WR_ENC_SUB1, LXT2_WR_ENC_SUB2, LXT2_WR_ENC_SUB3, LXT2_WR_ENC_SUB4, LXT2_WR_ENC_X, LXT2_WR_ENC_Z, LXT2_WR_ENC_BLACKOUT, LXT2_WR_DICT_START }; /* * integer splay */ typedef struct lxt2_wr_ds_tree_node lxt2_wr_ds_Tree; struct lxt2_wr_ds_tree_node { lxt2_wr_ds_Tree * left, * right; granmsk_t item; int val; lxt2_wr_ds_Tree * next; }; /* * string splay */ typedef struct lxt2_wr_dslxt_tree_node lxt2_wr_dslxt_Tree; struct lxt2_wr_dslxt_tree_node { lxt2_wr_dslxt_Tree * left, * right; char *item; unsigned int val; lxt2_wr_dslxt_Tree * next; }; struct lxt2_wr_trace { FILE *handle; gzFile zhandle; lxt2_wr_dslxt_Tree *dict; /* dictionary manipulation */ unsigned int num_dict_entries; unsigned int dict_string_mem_required; lxt2_wr_dslxt_Tree *dict_head; lxt2_wr_dslxt_Tree *dict_curr; lxt2_wr_ds_Tree *mapdict; /* bitmap compression */ unsigned int num_map_entries; lxt2_wr_ds_Tree *mapdict_head; lxt2_wr_ds_Tree *mapdict_curr; off_t position; off_t zfacname_predec_size, zfacname_size, zfacgeometry_size; off_t zpackcount, zpackcount_cumulative; off_t current_chunk, current_chunkz; struct lxt2_wr_symbol *sym[LXT2_WR_SYMPRIME]; struct lxt2_wr_symbol **sorted_facs; struct lxt2_wr_symbol *symchain; unsigned int numfacs, numalias; int numfacbytes; int longestname; int numsections, numblock; off_t facname_offset, facgeometry_offset; lxttime_t mintime, maxtime; lxtstime_t timezero; unsigned int timegranule; int timescale; unsigned int timepos; unsigned int maxgranule; lxttime_t firsttime, lasttime; lxttime_t timetable[LXT2_WR_GRANULE_SIZE]; unsigned int partial_iter; char *compress_fac_str; int compress_fac_len; lxttime_t flushtime; unsigned flush_valid : 1; unsigned do_strip_brackets : 1; unsigned emitted : 1; /* gate off change field zmode changes when set */ unsigned timeset : 1; /* time has been modified from 0..0 */ unsigned bumptime : 1; /* says that must go to next time position in granule as value change exists for current time */ unsigned granule_dirty : 1; /* for flushing out final block */ unsigned blackout : 1; /* blackout on/off */ unsigned partial : 1; /* partial (vertical) trace support */ unsigned partial_zip : 1; /* partial (vertical) trace support for zip subregions */ unsigned no_checkpoint : 1; /* turns off interblock checkpointing */ unsigned partial_preference : 1; /* partial preference encountered on some facs */ char initial_value; char zmode[4]; /* fills in with "wb0".."wb9" */ unsigned int gzbufpnt; unsigned char gzdest[LXT2_WR_GZWRITE_BUFFER + 4]; /* enough for zlib buffering */ char *lxtname; off_t break_size; off_t break_header_size; unsigned int break_number; }; struct lxt2_wr_symbol { struct lxt2_wr_symbol *next; struct lxt2_wr_symbol *symchain; char *name; int namlen; int facnum; struct lxt2_wr_symbol *aliased_to; char *value; /* fac's actual value */ unsigned int rows; int msb, lsb; int len; int flags; unsigned partial_preference : 1; /* in order to shove nets to the first partial group */ unsigned int chgpos; granmsk_t msk; /* must contain LXT2_WR_GRANULE_SIZE bits! */ unsigned int chg[LXT2_WR_GRANULE_SIZE]; }; #define LXT2_WR_SYM_F_BITS (0) #define LXT2_WR_SYM_F_INTEGER (1<<0) #define LXT2_WR_SYM_F_DOUBLE (1<<1) #define LXT2_WR_SYM_F_STRING (1<<2) #define LXT2_WR_SYM_F_TIME (LXT2_WR_SYM_F_STRING) /* user must correctly format this as a string */ #define LXT2_WR_SYM_F_ALIAS (1<<3) #define LXT2_WR_SYM_F_SIGNED (1<<4) #define LXT2_WR_SYM_F_BOOLEAN (1<<5) #define LXT2_WR_SYM_F_NATURAL ((1<<6)|(LXT2_WR_SYM_F_INTEGER)) #define LXT2_WR_SYM_F_POSITIVE ((1<<7)|(LXT2_WR_SYM_F_INTEGER)) #define LXT2_WR_SYM_F_CHARACTER (1<<8) #define LXT2_WR_SYM_F_CONSTANT (1<<9) #define LXT2_WR_SYM_F_VARIABLE (1<<10) #define LXT2_WR_SYM_F_SIGNAL (1<<11) #define LXT2_WR_SYM_F_IN (1<<12) #define LXT2_WR_SYM_F_OUT (1<<13) #define LXT2_WR_SYM_F_INOUT (1<<14) #define LXT2_WR_SYM_F_WIRE (1<<15) #define LXT2_WR_SYM_F_REG (1<<16) /* file I/O */ struct lxt2_wr_trace * lxt2_wr_init(const char *name); void lxt2_wr_flush(struct lxt2_wr_trace *lt); void lxt2_wr_close(struct lxt2_wr_trace *lt); /* for dealing with very large traces, split into multiple files approximately "siz" in length */ void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz); /* 0 = no compression, 9 = best compression, 4 = default */ void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth); /* default is partial off, turning on makes for faster trace reads, nonzero zipmode causes vertical compression */ void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt); void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode); void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name); /* turning off checkpointing makes for smaller files */ void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt); void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt); /* facility creation */ void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value); struct lxt2_wr_symbol * lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name); struct lxt2_wr_symbol * lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags); struct lxt2_wr_symbol * lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb); void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit); /* each granule is LXT2_WR_GRANULE_SIZE (32 or 64) timesteps, default is 256 per section */ void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule); /* time ops */ void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale); void lxt2_wr_set_timezero(struct lxt2_wr_trace *lt, lxtstime_t timeval); int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval); int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval); int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval); int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval); /* allows blackout regions in LXT files */ void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt); void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt); /* left fill on bit_string uses vcd semantics (left fill with value[0] unless value[0]=='1', then use '0') */ int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value); int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value); int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); #ifdef __cplusplus } #endif #endif iverilog-10_1/vpi/lxt_write.c000066400000000000000000001632061265551621300163350ustar00rootroot00000000000000/* * Copyright (c) 2001-2012 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include "lxt_write.h" /* * in-place sort to keep chained facs from migrating... */ static void wave_mergesort(struct lt_symbol **a, struct lt_symbol **b, int lo, int hi) { int i, j, k; if (loname, a[j]->name) <= 0) { a[k++]=b[i++]; } else { a[k++]=a[j++]; } } while (kitem); if (dir < 0) { if (t->left == NULL) break; if (strcmp(i, t->left->item)<0) { y = t->left; /* rotate right */ t->left = y->right; y->right = t; t = y; if (t->left == NULL) break; } r->left = t; /* link right */ r = t; t = t->left; } else if (dir > 0) { if (t->right == NULL) break; if (strcmp(i, t->right->item)>0) { y = t->right; /* rotate left */ t->right = y->left; y->left = t; t = y; if (t->right == NULL) break; } l->right = t; /* link left */ l = t; t = t->right; } else { dslxt_success=1; break; } } l->right = t->left; /* assemble */ r->left = t->right; t->left = N.right; t->right = N.left; return t; } static dslxt_Tree * dslxt_insert(char *i, dslxt_Tree * t, unsigned int val) { /* Insert i into the tree t, unless it's already there. */ /* Return a pointer to the resulting tree. */ dslxt_Tree * n; int dir; n = (dslxt_Tree *) calloc (1, sizeof (dslxt_Tree)); if (n == NULL) { fprintf(stderr, "dslxt_insert: ran out of memory, exiting.\n"); exit(255); } n->item = i; n->val = val; if (t == NULL) { n->left = n->right = NULL; return n; } t = dslxt_splay(i,t); dir = strcmp(i,t->item); if (dir<0) { n->left = t->left; n->right = t; t->left = NULL; return n; } else if (dir>0) { n->right = t->right; n->left = t; t->right = NULL; return n; } else { /* We get here if it's already in the tree */ /* Don't add it again */ free(n); return t; } } #if 0 /* unused for now but delete is here for completeness */ static dslxt_Tree * dslxt_delete(char *i, dslxt_Tree * t) { /* Deletes i from the tree if it's there. */ /* Return a pointer to the resulting tree. */ dslxt_Tree * x; if (t==NULL) return NULL; t = dslxt_splay(i,t); if (!strcmp(i, t->item)) { /* found it */ if (t->left == NULL) { x = t->right; } else { x = dslxt_splay(i, t->left); x->right = t->right; } free(t); return x; } return t; /* It wasn't there */ } #endif /************************ splay ************************/ /* * functions which emit various big endian * data to a file */ static int lt_emit_u8(struct lt_trace *lt, int value) { unsigned char buf[1]; int nmemb; buf[0] = value & 0xff; nmemb=fwrite(buf, sizeof(char), 1, lt->handle); lt->position+=nmemb; return(nmemb); } static int lt_emit_u16(struct lt_trace *lt, int value) { unsigned char buf[2]; int nmemb; buf[0] = (value>>8) & 0xff; buf[1] = value & 0xff; nmemb = fwrite(buf, sizeof(char), 2, lt->handle); lt->position+=nmemb; return(nmemb); } static int lt_emit_u24(struct lt_trace *lt, int value) { unsigned char buf[3]; int nmemb; buf[0] = (value>>16) & 0xff; buf[1] = (value>>8) & 0xff; buf[2] = value & 0xff; nmemb=fwrite(buf, sizeof(char), 3, lt->handle); lt->position+=nmemb; return(nmemb); } static int lt_emit_u32(struct lt_trace *lt, int value) { unsigned char buf[4]; int nmemb; buf[0] = (value>>24) & 0xff; buf[1] = (value>>16) & 0xff; buf[2] = (value>>8) & 0xff; buf[3] = value & 0xff; nmemb=fwrite(buf, sizeof(char), 4, lt->handle); lt->position+=nmemb; return(nmemb); } static int lt_emit_u64(struct lt_trace *lt, int valueh, int valuel) { int rc; if((rc=lt_emit_u32(lt, valueh))) { rc=lt_emit_u32(lt, valuel); } return(rc); } static int lt_emit_double(struct lt_trace *lt, double value) { int nmemb; nmemb=fwrite(&value, sizeof(char), sizeof(double)/sizeof(char), lt->handle); lt->position+=nmemb; return(nmemb); } static int lt_emit_string(struct lt_trace *lt, char *value) { int rc=1; do { rc&=lt_emit_u8(lt, *value); } while(*(value++)); return(rc); } /* * gzfunctions which emit various big endian * data to a file. (lt->position needs to be * fixed up on gzclose so the tables don't * get out of sync!) */ static int lt_emit_u8z(struct lt_trace *lt, int value) { unsigned char buf[1]; int nmemb; buf[0] = value & 0xff; nmemb=gzwrite(lt->zhandle, buf, 1); lt->zpackcount++; lt->position++; return(nmemb); } static int lt_emit_u16z(struct lt_trace *lt, int value) { unsigned char buf[2]; int nmemb; buf[0] = (value>>8) & 0xff; buf[1] = value & 0xff; nmemb = gzwrite(lt->zhandle, buf, 2); lt->zpackcount+=2; lt->position+=2; return(nmemb); } static int lt_emit_u24z(struct lt_trace *lt, int value) { unsigned char buf[3]; int nmemb; buf[0] = (value>>16) & 0xff; buf[1] = (value>>8) & 0xff; buf[2] = value & 0xff; nmemb=gzwrite(lt->zhandle, buf, 3); lt->zpackcount+=3; lt->position+=3; return(nmemb); } static int lt_emit_u32z(struct lt_trace *lt, int value) { unsigned char buf[4]; int nmemb; buf[0] = (value>>24) & 0xff; buf[1] = (value>>16) & 0xff; buf[2] = (value>>8) & 0xff; buf[3] = value & 0xff; nmemb=gzwrite(lt->zhandle, buf, 4); lt->zpackcount+=4; lt->position+=4; return(nmemb); } static int lt_emit_u64z(struct lt_trace *lt, int valueh, int valuel) { int rc; if((rc=lt_emit_u32z(lt, valueh))) { rc=lt_emit_u32z(lt, valuel); } return(rc); } static int lt_emit_doublez(struct lt_trace *lt, double value) { int nmemb; nmemb=gzwrite(lt->zhandle, &value, sizeof(double)/sizeof(char)); lt->zpackcount+=(sizeof(double)/sizeof(char)); lt->position+=(sizeof(double)/sizeof(char));; return(nmemb); } static int lt_emit_stringz(struct lt_trace *lt, char *value) { int rc=1; do { rc&=lt_emit_u8z(lt, *value); } while(*(value++)); return(rc); } /* * bz2functions which emit various big endian * data to a file. (lt->position needs to be * fixed up on BZ2_bzclose so the tables don't * get out of sync!) */ static int lt_emit_u8bz(struct lt_trace *lt, int value) { unsigned char buf[1]; int nmemb; buf[0] = value & 0xff; nmemb=BZ2_bzwrite(lt->zhandle, buf, 1); lt->zpackcount++; lt->position++; return(nmemb); } static int lt_emit_u16bz(struct lt_trace *lt, int value) { unsigned char buf[2]; int nmemb; buf[0] = (value>>8) & 0xff; buf[1] = value & 0xff; nmemb = BZ2_bzwrite(lt->zhandle, buf, 2); lt->zpackcount+=2; lt->position+=2; return(nmemb); } static int lt_emit_u24bz(struct lt_trace *lt, int value) { unsigned char buf[3]; int nmemb; buf[0] = (value>>16) & 0xff; buf[1] = (value>>8) & 0xff; buf[2] = value & 0xff; nmemb=BZ2_bzwrite(lt->zhandle, buf, 3); lt->zpackcount+=3; lt->position+=3; return(nmemb); } static int lt_emit_u32bz(struct lt_trace *lt, int value) { unsigned char buf[4]; int nmemb; buf[0] = (value>>24) & 0xff; buf[1] = (value>>16) & 0xff; buf[2] = (value>>8) & 0xff; buf[3] = value & 0xff; nmemb=BZ2_bzwrite(lt->zhandle, buf, 4); lt->zpackcount+=4; lt->position+=4; return(nmemb); } static int lt_emit_u64bz(struct lt_trace *lt, int valueh, int valuel) { int rc; if((rc=lt_emit_u32bz(lt, valueh))) { rc=lt_emit_u32bz(lt, valuel); } return(rc); } static int lt_emit_doublebz(struct lt_trace *lt, double value) { int nmemb; nmemb=BZ2_bzwrite(lt->zhandle, &value, sizeof(double)/sizeof(char)); lt->zpackcount+=(sizeof(double)/sizeof(char)); lt->position+=(sizeof(double)/sizeof(char));; return(nmemb); } static int lt_emit_stringbz(struct lt_trace *lt, char *value) { int rc=1; do { rc&=lt_emit_u8bz(lt, *value); } while(*(value++)); return(rc); } /* * switch between compression modes above */ static void lt_set_zmode(struct lt_trace *lt, int mode) { switch(mode) { case LT_ZMODE_NONE: lt->lt_emit_u8 = lt_emit_u8; lt->lt_emit_u16 = lt_emit_u16; lt->lt_emit_u24 = lt_emit_u24; lt->lt_emit_u32 = lt_emit_u32; lt->lt_emit_u64 = lt_emit_u64; lt->lt_emit_double = lt_emit_double; lt->lt_emit_string = lt_emit_string; break; case LT_ZMODE_GZIP: lt->lt_emit_u8 = lt_emit_u8z; lt->lt_emit_u16 = lt_emit_u16z; lt->lt_emit_u24 = lt_emit_u24z; lt->lt_emit_u32 = lt_emit_u32z; lt->lt_emit_u64 = lt_emit_u64z; lt->lt_emit_double = lt_emit_doublez; lt->lt_emit_string = lt_emit_stringz; break; case LT_ZMODE_BZIP2: lt->lt_emit_u8 = lt_emit_u8bz; lt->lt_emit_u16 = lt_emit_u16bz; lt->lt_emit_u24 = lt_emit_u24bz; lt->lt_emit_u32 = lt_emit_u32bz; lt->lt_emit_u64 = lt_emit_u64bz; lt->lt_emit_double = lt_emit_doublebz; lt->lt_emit_string = lt_emit_stringbz; break; } } /* * hash/symtable manipulation */ static int lt_hash(const char *s) { const char *p; char ch; unsigned int h=0, h2=0, pos=0, g; for(p=s;*p;p++) { ch=*p; h2<<=3; h2-=((unsigned int)ch+(pos++)); /* this handles stranded vectors quite well.. */ h=(h<<4)+ch; if((g=h&0xf0000000)) { h=h^(g>>24); h=h^g; } } h^=h2; /* combine the two hashes */ return(h%LT_SYMPRIME); } static struct lt_symbol *lt_symadd(struct lt_trace *lt, const char *name, int hv) { struct lt_symbol *s; s=(struct lt_symbol *)calloc(1,sizeof(struct lt_symbol)); strcpy(s->name=(char *)malloc((s->namlen=strlen(name))+1),name); s->next=lt->sym[hv]; lt->sym[hv]=s; return(s); } static struct lt_symbol *lt_symfind(struct lt_trace *lt, const char *s) { int hv; struct lt_symbol *temp; hv=lt_hash(s); if(!(temp=lt->sym[hv])) return(NULL); /* no hash entry, add here wanted to add */ while(temp) { if(!strcmp(temp->name,s)) { return(temp); /* in table already */ } if(!temp->next) break; temp=temp->next; } return(NULL); /* not found, add here if you want to add*/ } /* * compress facs to a prefix count + string + 0x00 */ static void lt_compress_fac(struct lt_trace *lt, char *str) { int i; int len = strlen(str); int minlen = (lencompress_fac_len) ? len : lt->compress_fac_len; if(minlen>65535) minlen=65535; /* keep in printable range--most hierarchies won't be this big anyway */ if(lt->compress_fac_str) { for(i=0;icompress_fac_str[i]!=str[i]) break; } lt_emit_u16z(lt, i); lt_emit_stringz(lt, str+i); free(lt->compress_fac_str); } else { lt_emit_u16z(lt, 0); lt_emit_stringz(lt, str); } lt->compress_fac_str = (char *) malloc((lt->compress_fac_len=len)+1); strcpy(lt->compress_fac_str, str); } static void strip_brack(struct lt_symbol *s) { char *lastch = s->name+s->namlen - 1; if(*lastch!=']') return; if(s->namlen<3) return; lastch--; while(lastch!=s->name) { if(*lastch=='.') { return; /* MTI SV [0.3] notation for implicit vars */ } if(*lastch=='[') { *lastch=0x00; return; } lastch--; } return; } static void lt_emitfacs(struct lt_trace *lt) { int i; if((lt)&&(lt->numfacs)) { struct lt_symbol *s = lt->symchain; char is_interlaced_trace = (lt->sorted_facs==NULL); if(!lt->sorted_facs) { if((lt->sorted_facs = (struct lt_symbol **)calloc(lt->numfacs, sizeof(struct lt_symbol *)))) { if(lt->do_strip_brackets) for(i=0;inumfacs;i++) { lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ strip_brack(s); s=s->symchain; } else for(i=0;inumfacs;i++) { lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing*/ s=s->symchain; } wave_msort(lt->sorted_facs, lt->numfacs); for(i=0;inumfacs;i++) { lt->sorted_facs[i]->facnum = i; } } } if(lt->sorted_facs) { lt->facname_offset=lt->position; lt_emit_u32(lt, lt->numfacs); /* uncompressed */ lt_emit_u32(lt, lt->numfacbytes); /* uncompressed */ fflush(lt->handle); lt->zfacname_size = lt->position; lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt->zpackcount = 0; for(i=0;inumfacs;i++) { lt_compress_fac(lt, lt->sorted_facs[i]->name); } free(lt->compress_fac_str); lt->compress_fac_str=NULL; lt->compress_fac_len=0; lt->zfacname_predec_size = lt->zpackcount; gzclose(lt->zhandle); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->zfacname_size = lt->position - lt->zfacname_size; lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt->facgeometry_offset = lt->position; for(i=0;inumfacs;i++) { if((lt->sorted_facs[i]->flags<_SYM_F_ALIAS)==0) { lt_emit_u32z(lt, lt->sorted_facs[i]->rows); lt_emit_u32z(lt, lt->sorted_facs[i]->msb); lt_emit_u32z(lt, lt->sorted_facs[i]->lsb); lt_emit_u32z(lt, lt->sorted_facs[i]->flags); } else { lt_emit_u32z(lt, lt->sorted_facs[i]->aliased_to->facnum); lt_emit_u32z(lt, lt->sorted_facs[i]->msb); lt_emit_u32z(lt, lt->sorted_facs[i]->lsb); lt_emit_u32z(lt, LT_SYM_F_ALIAS); } } gzclose(lt->zhandle); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->zfacgeometry_size = lt->position - lt->facgeometry_offset; if(is_interlaced_trace) { lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt->sync_table_offset = lt->position; for(i=0;inumfacs;i++) { lt_emit_u32z(lt, lt->sorted_facs[i]->last_change); } gzclose(lt->zhandle); lt->zhandle = NULL; fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->zsync_table_size = lt->position - lt->sync_table_offset; } } } } /* * initialize the trace and get back an lt context */ struct lt_trace *lt_init(const char *name) { struct lt_trace *lt=(struct lt_trace *)calloc(1, sizeof(struct lt_trace)); if(!(lt->handle=fopen(name, "wb"))) { free(lt); lt=NULL; } else { lt_emit_u16(lt, LT_HDRID); lt_emit_u16(lt, LT_VERSION); lt->change_field_offset = lt->position; lt->initial_value = -1; /* if a user sets this it will have a different POSITIVE val */ lt->timescale = -256; /* will be in range of -128<=x<=127 if set */ lt_set_zmode(lt, LT_ZMODE_NONE); lt->mintime=ULLDescriptor(1); lt->maxtime=ULLDescriptor(0); } return(lt); } /* * clock flushing.. */ static void lt_flushclock(struct lt_trace *lt, struct lt_symbol *s) { unsigned int last_change_delta = lt->position - s->last_change - 2; unsigned int start_position = lt->position; int tag; int numbytes, numbytes_trans; int numtrans = s->clk_numtrans - LT_CLKPACK - 1; if(numtrans<0) { /* it never got around to caching */ fprintf(stderr, "Possible Problem with %s with %d?\n", s->name, s->clk_numtrans); return; } if(numtrans >= 256*65536) { numbytes_trans = 3; } else if(numtrans >= 65536) { numbytes_trans = 2; } else if(numtrans >= 256) { numbytes_trans = 1; } else { numbytes_trans = 0; } if(!lt->numfacs_bytes) { if(last_change_delta >= 256*65536) { numbytes = 3; } else if(last_change_delta >= 65536) { numbytes = 2; } else if(last_change_delta >= 256) { numbytes = 1; } else { numbytes = 0; } tag = (numbytes<<4) + 0xC + numbytes_trans; /* yields xC..xF */ lt->lt_emit_u8(lt, tag); switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, last_change_delta); break; case 1: lt->lt_emit_u16(lt, last_change_delta); break; case 2: lt->lt_emit_u24(lt, last_change_delta); break; case 3: lt->lt_emit_u32(lt, last_change_delta); break; } } else { tag = 0xC + numbytes_trans; /* yields C..F */ switch(lt->numfacs_bytes) { case 1: lt->lt_emit_u8(lt, s->facnum); break; case 2: lt->lt_emit_u16(lt, s->facnum); break; case 3: lt->lt_emit_u24(lt, s->facnum); break; case 4: lt->lt_emit_u32(lt, s->facnum); break; } lt->lt_emit_u8(lt, tag); } s->last_change = start_position; /* s->clk_prevval CAN BE INFERRED! */ /* s->clk_prevtrans CAN BE INFERRED! */ /* s->clk_delta CAN BE INFERRED! */ switch(numbytes_trans&3) { case 0: lt->lt_emit_u8(lt, numtrans); break; case 1: lt->lt_emit_u16(lt, numtrans); break; case 2: lt->lt_emit_u24(lt, numtrans); break; case 3: lt->lt_emit_u32(lt, numtrans); break; } /* printf("Clock finish for '%s' at %lld ending with '%c' for %d repeats over a switch delta of %d\n", s->name, lt->timeval, s->clk_prevval, s->clk_numtrans - LT_CLKPACK, s->clk_delta); */ s->clk_prevtrans = ULLDescriptor(~0); s->clk_numtrans = 0; } static void lt_flushclock_m(struct lt_trace *lt, struct lt_symbol *s) { unsigned int last_change_delta = lt->position - s->last_change - 2; unsigned int start_position = lt->position; int tag; int numbytes, numbytes_trans; int numtrans = s->clk_numtrans - LT_CLKPACK_M - 1; if(numtrans<0) { /* it never got around to caching */ fprintf(stderr, "Possible Problem with %s with %d?\n", s->name, s->clk_numtrans); return; } if(numtrans >= 256*65536) { numbytes_trans = 3; } else if(numtrans >= 65536) { numbytes_trans = 2; } else if(numtrans >= 256) { numbytes_trans = 1; } else { numbytes_trans = 0; } if(!lt->numfacs_bytes) { if(last_change_delta >= 256*65536) { numbytes = 3; } else if(last_change_delta >= 65536) { numbytes = 2; } else if(last_change_delta >= 256) { numbytes = 1; } else { numbytes = 0; } tag = (numbytes<<4) + 0xC + numbytes_trans; /* yields xC..xF */ lt->lt_emit_u8(lt, tag); switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, last_change_delta); break; case 1: lt->lt_emit_u16(lt, last_change_delta); break; case 2: lt->lt_emit_u24(lt, last_change_delta); break; case 3: lt->lt_emit_u32(lt, last_change_delta); break; } } else { tag = 0xC + numbytes_trans; /* yields C..F */ switch(lt->numfacs_bytes) { case 1: lt->lt_emit_u8(lt, s->facnum); break; case 2: lt->lt_emit_u16(lt, s->facnum); break; case 3: lt->lt_emit_u24(lt, s->facnum); break; case 4: lt->lt_emit_u32(lt, s->facnum); break; } lt->lt_emit_u8(lt, tag); } s->last_change = start_position; /* s->clk_prevval CAN BE INFERRED! */ /* s->clk_prevtrans CAN BE INFERRED! */ /* s->clk_delta CAN BE INFERRED! */ switch(numbytes_trans&3) { case 0: lt->lt_emit_u8(lt, numtrans); break; case 1: lt->lt_emit_u16(lt, numtrans); break; case 2: lt->lt_emit_u24(lt, numtrans); break; case 3: lt->lt_emit_u32(lt, numtrans); break; } /* printf("Clock finish for '%s' at %lld ending with '%08x' for %d repeats over a switch delta of %lld\n", s->name, lt->timeval, s->clk_prevval, s->clk_numtrans - LT_CLKPACK_M, s->clk_delta); */ s->clk_prevtrans = ULLDescriptor(~0); s->clk_numtrans = 0; } /* * recurse through dictionary */ static void lt_recurse_dictionary(struct lt_trace *lt, dslxt_Tree *ds) { if(ds->left) lt_recurse_dictionary(lt, ds->left); lt->sorted_dict[ds->val] = ds; if(ds->right) lt_recurse_dictionary(lt, ds->right); } static void lt_recurse_dictionary_free(struct lt_trace *lt, dslxt_Tree *ds) { dslxt_Tree *lft = ds->left; dslxt_Tree *rgh = ds->right; if(lft) lt_recurse_dictionary_free(lt, lft); free(ds->item); free(ds); if(rgh) lt_recurse_dictionary_free(lt, rgh); } static int lt_dictval_compare(const void *v1, const void *v2) { const dslxt_Tree *s1 = *(const dslxt_Tree * const *)v1; const dslxt_Tree *s2 = *(const dslxt_Tree * const *)v2; if(s1->val > s2->val) return(1); else return(-1); /* they're *never* equal */ } static void lt_finalize_dictionary(struct lt_trace *lt) { unsigned int i; lt->sorted_dict = calloc(lt->num_dict_entries, sizeof(dslxt_Tree *)); lt->dictionary_offset=lt->position; lt_emit_u32(lt, lt->num_dict_entries); /* uncompressed */ lt_emit_u32(lt, lt->dict_string_mem_required - lt->num_dict_entries); /* uncompressed : minus because leading '1' is implied so its stripped */ lt_emit_u32(lt, lt->dict16_offset); /* uncompressed */ lt_emit_u32(lt, lt->dict24_offset); /* uncompressed */ lt_emit_u32(lt, lt->dict32_offset); /* uncompressed */ lt_emit_u32(lt, lt->mindictwidth); /* uncompressed */ fflush(lt->handle); #if 0 fprintf(stderr, "*** dictionary_offset = %08x\n", lt->dictionary_offset); fprintf(stderr, "*** num_dict_entries = %d\n", lt->num_dict_entries); #endif lt->zdictionary_size = lt->position; lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt_recurse_dictionary(lt, lt->dict); qsort((void *)lt->sorted_dict, lt->num_dict_entries, sizeof(struct dsTree **), lt_dictval_compare); for(i=0;inum_dict_entries;i++) { dslxt_Tree *ds = lt->sorted_dict[i]; /* fprintf(stderr, "%8d) '%s'\n", ds->val, ds->item); */ lt_emit_stringz(lt, ds->item+1); } gzclose(lt->zhandle); fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->zdictionary_size = lt->position - lt->zdictionary_size; free(lt->sorted_dict); lt->sorted_dict = NULL; lt_recurse_dictionary_free(lt, lt->dict); lt->dict = NULL; } /* * close out the trace and fixate it */ void lt_close(struct lt_trace *lt) { lxttime_t lasttime=ULLDescriptor(0); int lastposition=0; char is64=0; if(lt) { struct lt_symbol *s = lt->symchain; if(lt->clock_compress) while(s) { if(s->clk_prevtrans!=ULLDescriptor(~0)) { int len = ((s->flags)<_SYM_F_INTEGER) ? 32 : s->len; if(len>1) { if(s->clk_numtrans > LT_CLKPACK_M) lt_flushclock_m(lt, s); } else { if(s->clk_numtrans > LT_CLKPACK) lt_flushclock(lt, s); } } s=s->symchain; } lt_set_dumpon(lt); /* in case it was turned off */ if(lt->zmode!=LT_ZMODE_NONE) { lt->chg_table_size = lt->position - lt->change_field_offset; switch(lt->zmode) { case LT_ZMODE_GZIP: gzclose(lt->zhandle); break; case LT_ZMODE_BZIP2: BZ2_bzclose(lt->zhandle); break; } lt->zhandle = NULL; fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt_set_zmode(lt, LT_ZMODE_NONE); lt->zchg_table_size = lt->position - lt->change_field_offset; } lt_emitfacs(lt); if(lt->dict) lt_finalize_dictionary(lt); free(lt->timebuff); lt->timebuff=NULL; if(lt->timehead) { struct lt_timetrail *t=lt->timehead; struct lt_timetrail *t2; lt->time_table_offset = lt->position; lt_emit_u32(lt, lt->timechangecount); /* this is uncompressed! */ fflush(lt->handle); lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); lt->ztime_table_size = lt->position; is64=(lt->maxtime > ULLDescriptor(0xFFFFFFFF)); if(is64) { lt_emit_u64z(lt, (int)((lt->mintime)>>32), (int)lt->mintime); lt_emit_u64z(lt, (int)((lt->maxtime)>>32), (int)lt->maxtime); } else { lt_emit_u32z(lt, (int)lt->mintime); lt_emit_u32z(lt, (int)lt->maxtime); } while(t) { lt_emit_u32z(lt, t->position - lastposition); lastposition = t->position; t=t->next; } t=lt->timehead; if(is64) { while(t) { lxttime_t delta = t->timeval - lasttime; lt_emit_u64z(lt, (int)(delta>>32), (int)delta); lasttime = t->timeval; t2=t->next; free(t); t=t2; } } else { while(t) { lt_emit_u32z(lt, (int)(t->timeval - lasttime)); lasttime = t->timeval; t2=t->next; free(t); t=t2; } lt->timehead = lt->timecurr = NULL; } gzclose(lt->zhandle); lt->zhandle = NULL; fseeko(lt->handle, 0L, SEEK_END); lt->position=ftello(lt->handle); lt->ztime_table_size = lt->position - lt->ztime_table_size; } if(lt->initial_value>=0) { lt->initial_value_offset = lt->position; lt_emit_u8(lt, lt->initial_value); } if((lt->timescale>-129)&&(lt->timescale<128)) { lt->timescale_offset = lt->position; lt_emit_u8(lt, lt->timescale); } if(lt->double_used) { lt->double_test_offset = lt->position; lt_emit_double(lt, 3.14159); } if(lt->dumpoffcount) { struct lt_timetrail *ltt = lt->dumpoffhead; struct lt_timetrail *ltt2; lt->exclude_offset = lt->position; lt_emit_u32(lt, lt->dumpoffcount); while(ltt) { lt_emit_u64(lt, (int)((ltt->timeval)>>32), (int)ltt->timeval); ltt2 = ltt; ltt=ltt->next; free(ltt2); } lt->dumpoffhead = lt->dumpoffcurr = NULL; lt->dumpoffcount = 0; } if(lt->timezero) { lt->timezero_offset = lt->position; lt_emit_u64(lt, (int)((lt->timezero)>>32), (int)lt->timezero); } /* prefix */ lt_emit_u8(lt, LT_SECTION_END); /* Version 1 */ if(lt->change_field_offset) { lt_emit_u32(lt, lt->change_field_offset); lt_emit_u8(lt, LT_SECTION_CHG); lt->change_field_offset = 0; } if(lt->sync_table_offset) { lt_emit_u32(lt, lt->sync_table_offset); lt_emit_u8(lt, LT_SECTION_SYNC_TABLE); lt->sync_table_offset = 0; } if(lt->facname_offset) { lt_emit_u32(lt, lt->facname_offset); lt_emit_u8(lt, LT_SECTION_FACNAME); lt->facname_offset = 0; } if(lt->facgeometry_offset) { lt_emit_u32(lt, lt->facgeometry_offset); lt_emit_u8(lt, LT_SECTION_FACNAME_GEOMETRY); lt->facgeometry_offset = 0; } if(lt->timescale_offset) { lt_emit_u32(lt, lt->timescale_offset); lt_emit_u8(lt, LT_SECTION_TIMESCALE); lt->timescale_offset = 0; } if(lt->time_table_offset) { lt_emit_u32(lt, lt->time_table_offset); lt_emit_u8(lt, is64 ? LT_SECTION_TIME_TABLE64 : LT_SECTION_TIME_TABLE); lt->time_table_offset = 0; } if(lt->initial_value_offset) { lt_emit_u32(lt, lt->initial_value_offset); lt_emit_u8(lt, LT_SECTION_INITIAL_VALUE); lt->initial_value_offset = 0; } if(lt->double_test_offset) { lt_emit_u32(lt, lt->double_test_offset); lt_emit_u8(lt, LT_SECTION_DOUBLE_TEST); lt->double_test_offset = 0; } /* Version 2 adds */ if(lt->zfacname_predec_size) { lt_emit_u32(lt, lt->zfacname_predec_size); lt_emit_u8(lt, LT_SECTION_ZFACNAME_PREDEC_SIZE); lt->zfacname_predec_size = 0; } if(lt->zfacname_size) { lt_emit_u32(lt, lt->zfacname_size); lt_emit_u8(lt, LT_SECTION_ZFACNAME_SIZE); lt->zfacname_size = 0; } if(lt->zfacgeometry_size) { lt_emit_u32(lt, lt->zfacgeometry_size); lt_emit_u8(lt, LT_SECTION_ZFACNAME_GEOMETRY_SIZE); lt->zfacgeometry_size = 0; } if(lt->zsync_table_size) { lt_emit_u32(lt, lt->zsync_table_size); lt_emit_u8(lt, LT_SECTION_ZSYNC_SIZE); lt->zsync_table_size = 0; } if(lt->ztime_table_size) { lt_emit_u32(lt, lt->ztime_table_size); lt_emit_u8(lt, LT_SECTION_ZTIME_TABLE_SIZE); lt->ztime_table_size = 0; } if(lt->chg_table_size) { lt_emit_u32(lt, lt->chg_table_size); lt_emit_u8(lt, LT_SECTION_ZCHG_PREDEC_SIZE); lt->chg_table_size = 0; } if(lt->zchg_table_size) { lt_emit_u32(lt, lt->zchg_table_size); lt_emit_u8(lt, LT_SECTION_ZCHG_SIZE); lt->zchg_table_size = 0; } /* Version 4 adds */ if(lt->dictionary_offset) { lt_emit_u32(lt, lt->dictionary_offset); lt_emit_u8(lt, LT_SECTION_ZDICTIONARY); lt->dictionary_offset = 0; } if(lt->zdictionary_size) { lt_emit_u32(lt, lt->zdictionary_size); lt_emit_u8(lt, LT_SECTION_ZDICTIONARY_SIZE); lt->zdictionary_size = 0; } /* Version 5 adds */ if(lt->exclude_offset) { lt_emit_u32(lt, lt->exclude_offset); lt_emit_u8(lt, LT_SECTION_EXCLUDE_TABLE); lt->exclude_offset = 0; } /* Version 6 adds */ if(lt->timezero_offset) { lt_emit_u32(lt, lt->timezero_offset); lt_emit_u8(lt, LT_SECTION_TIMEZERO); lt->timezero_offset = 0; } /* suffix */ lt_emit_u8(lt, LT_TRLID); if(lt->symchain) { struct lt_symbol *sc = lt->symchain; struct lt_symbol *s2; while(sc) { free(sc->name); s2=sc->symchain; free(sc); sc=s2; } } free(lt->sorted_facs); fclose(lt->handle); free(lt); } } /* * maint function for finding a symbol if it exists */ struct lt_symbol *lt_symbol_find(struct lt_trace *lt, const char *name) { struct lt_symbol *s=NULL; if((lt)&&(name)) s=lt_symfind(lt, name); return(s); } /* * add a trace (if it doesn't exist already) */ struct lt_symbol *lt_symbol_add(struct lt_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags) { struct lt_symbol *s; int len; int flagcnt; if((!lt)||(lt->sorted_facs)) return(NULL); flagcnt = ((flags<_SYM_F_INTEGER)!=0) + ((flags<_SYM_F_DOUBLE)!=0) + ((flags<_SYM_F_STRING)!=0); if((flagcnt>1)||(!lt)||(!name)||(lt_symfind(lt, name))) return (NULL); if(flags<_SYM_F_DOUBLE) lt->double_used = 1; s=lt_symadd(lt, name, lt_hash(name)); s->rows = rows; s->flags = flags&(~LT_SYM_F_ALIAS); /* aliasing makes no sense here.. */ if(!flagcnt) { s->msb = msb; s->lsb = lsb; s->len = (msblen==1)&&(s->rows==0)) s->clk_prevtrans = ULLDescriptor(~0); } s->symchain = lt->symchain; lt->symchain = s; lt->numfacs++; if((len=strlen(name)) > lt->longestname) lt->longestname = len; lt->numfacbytes += (len+1); return(s); } /* * add an alias trace (if it doesn't exist already and orig is found) */ struct lt_symbol *lt_symbol_alias(struct lt_trace *lt, const char *existing_name, const char *alias, int msb, int lsb) { struct lt_symbol *s, *sa; int len; int bitlen; int flagcnt; if((!lt)||(!existing_name)||(!alias)||(!(s=lt_symfind(lt, existing_name)))||(lt_symfind(lt, alias))) return (NULL); if(lt->sorted_facs) return(NULL); while(s->aliased_to) /* find root alias */ { s=s->aliased_to; } flagcnt = ((s->flags<_SYM_F_INTEGER)!=0) + ((s->flags<_SYM_F_DOUBLE)!=0) + ((s->flags<_SYM_F_STRING)!=0); bitlen = (msblen)) return(NULL); sa=lt_symadd(lt, alias, lt_hash(alias)); sa->flags = LT_SYM_F_ALIAS; /* only point this can get set */ sa->aliased_to = s; if(!flagcnt) { sa->msb = msb; sa->lsb = lsb; sa->len = bitlen; } sa->symchain = lt->symchain; lt->symchain = sa; lt->numfacs++; if((len=strlen(alias)) > lt->longestname) lt->longestname = len; lt->numfacbytes += (len+1); return(sa); } /* * set current time */ int lt_inc_time_by_delta(struct lt_trace *lt, unsigned int timeval) { return(lt_set_time64(lt, lt->maxtime + (lxttime_t)timeval)); } int lt_set_time(struct lt_trace *lt, unsigned int timeval) { return(lt_set_time64(lt, (lxttime_t)timeval)); } int lt_inc_time_by_delta64(struct lt_trace *lt, lxttime_t timeval) { return(lt_set_time64(lt, lt->maxtime + timeval)); } int lt_set_time64(struct lt_trace *lt, lxttime_t timeval) { int rc=0; if(lt) { struct lt_timetrail *trl=(struct lt_timetrail *)calloc(1, sizeof(struct lt_timetrail)); if(trl) { trl->timeval = timeval; trl->position = lt->position; if((lt->timecurr)||(lt->timebuff)) { if(((timeval>lt->mintime)&&(timeval>lt->maxtime))||((lt->mintime==ULLDescriptor(1))&&(lt->maxtime==ULLDescriptor(0)))) { lt->maxtime = timeval; } else { free(trl); goto bail; } } else { lt->mintime = lt->maxtime = timeval; } free(lt->timebuff); lt->timebuff = trl; lt->timeval = timeval; rc=1; } } bail: return(rc); } /* * sets trace timescale as 10**x seconds */ void lt_set_timescale(struct lt_trace *lt, int timescale) { if(lt) { lt->timescale = timescale; } } /* * sets clock compression heuristic */ void lt_set_clock_compress(struct lt_trace *lt) { if(lt) { lt->clock_compress = 1; } } /* * sets change dump compression */ void lt_set_chg_compress(struct lt_trace *lt) { if(lt) { if((lt->zmode==LT_ZMODE_NONE)&&(!lt->emitted)) { lt_set_zmode(lt, lt->zmode = LT_ZMODE_GZIP); fflush(lt->handle); lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); } } } /* * sets change dictionary compression */ void lt_set_dict_compress(struct lt_trace *lt, unsigned int minwidth) { if((lt)&&(!lt->emitted)) { lt->dictmode = 1; if(minwidth>1) { lt->mindictwidth = minwidth; } } } /* * sets change interlace */ void lt_set_no_interlace(struct lt_trace *lt) { if((lt)&&(!lt->emitted)&&(!lt->sorted_facs)) { if(lt->zmode==LT_ZMODE_NONE) /* this mode implies BZIP2 compression! */ { lt_set_zmode(lt, lt->zmode = LT_ZMODE_BZIP2); fflush(lt->handle); lt->zhandle = BZ2_bzdopen(dup(fileno(lt->handle)), "wb9"); } if((lt->sorted_facs = (struct lt_symbol **)calloc(lt->numfacs, sizeof(struct lt_symbol *)))) { struct lt_symbol *s = lt->symchain; int i; if(lt->do_strip_brackets) for(i=0;inumfacs;i++) { lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ strip_brack(s); s=s->symchain; } else for(i=0;inumfacs;i++) { lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ s=s->symchain; } wave_msort(lt->sorted_facs, lt->numfacs); for(i=0;inumfacs;i++) { lt->sorted_facs[i]->facnum = i; } if(lt->numfacs >= 256*65536) { lt->numfacs_bytes = 4; } else if(lt->numfacs >= 65536) { lt->numfacs_bytes = 3; } else if(lt->numfacs >= 256) { lt->numfacs_bytes = 2; } else { lt->numfacs_bytes = 1; } } } } /* * sets trace initial value */ void lt_set_initial_value(struct lt_trace *lt, char value) { if(lt) { int tag; switch(value) { case '0': tag = 0; break; case '1': tag = 1; break; case 'Z': case 'z': tag = 2; break; case 'X': case 'x': tag = 3; break; case 'H': case 'h': tag = 4; break; case 'U': case 'u': tag = 5; break; case 'W': case 'w': tag = 6; break; case 'L': case 'l': tag = 0x7; break; case '-': tag = 0x8; break; default: tag = -1; break; } lt->initial_value = tag; } } /* * Sets bracket stripping (useful for VCD conversions of * bitblasted nets) */ void lt_symbol_bracket_stripping(struct lt_trace *lt, int doit) { if(lt) { lt->do_strip_brackets = (doit!=0); } } /* * emission for trace values.. */ static int lt_optimask[]= { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff }; static char *lt_expand_integer_to_bits(int len, int value) { static char s[33]; char *p = s; int i; len--; for(i=0;i<=len;i++) { *(p++) = '0' | ((value & (1<<(len-i)))!=0); } *p = 0; return(s); } int lt_emit_value_int(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, int value) { int rc=0; if((!lt)||(!s)) return(rc); if(!lt->emitted) lt->emitted = 1; while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } if(!(s->flags&(LT_SYM_F_DOUBLE|LT_SYM_F_STRING))) { int numbytes; /* number of bytes to store value minus one */ unsigned int len = ((s->flags)<_SYM_F_INTEGER) ? 32 : s->len; unsigned int last_change_delta; if((lt->clock_compress)&&(s->rows==0)) { if((len>1)&&(len<=32)) { int ivalue = value; int delta1, delta2; s->clk_mask <<= 1; s->clk_mask |= 1; if( ((s->clk_mask&0x1f)==0x1f) && ( (delta1=(ivalue - s->clk_prevval1) & lt_optimask[s->len]) == ((s->clk_prevval1 - s->clk_prevval3) & lt_optimask[s->len]) ) && ( (delta2=(s->clk_prevval - s->clk_prevval2) & lt_optimask[s->len]) == ((s->clk_prevval2 - s->clk_prevval4) & lt_optimask[s->len]) ) && ( (delta1==delta2) || ((!delta1)&&(!delta2)) ) ) { if(s->clk_prevtrans==ULLDescriptor(~0)) { s->clk_prevtrans = lt->timeval; s->clk_numtrans = 0; } else if(s->clk_numtrans == 0) { s->clk_delta = lt->timeval - s->clk_prevtrans; s->clk_prevtrans = lt->timeval; s->clk_numtrans++; } else { if(s->clk_delta == (lt->timeval - s->clk_prevtrans)) { s->clk_numtrans++; s->clk_prevtrans = lt->timeval; if(s->clk_numtrans > LT_CLKPACK_M) { s->clk_prevval4 = s->clk_prevval3; s->clk_prevval3 = s->clk_prevval2; s->clk_prevval2 = s->clk_prevval1; s->clk_prevval1 = s->clk_prevval; s->clk_prevval = ivalue; /* printf("Clock value '%08x' for '%s' at %lld (#%d)\n", ivalue, s->name, lt->timeval, s->clk_numtrans); */ return(1); } } else { if(s->clk_numtrans > LT_CLKPACK_M) { lt_flushclock_m(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } } } else { if(s->clk_numtrans > LT_CLKPACK_M) { lt_flushclock_m(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } s->clk_prevval4 = s->clk_prevval3; s->clk_prevval3 = s->clk_prevval2; s->clk_prevval2 = s->clk_prevval1; s->clk_prevval1 = s->clk_prevval; s->clk_prevval = ivalue; } else if(len==1) /* possible clock handling */ { int ivalue = value&1; if(((s->clk_prevval == '1') && (ivalue==0)) || ((s->clk_prevval == '0') && (ivalue==1))) { if(s->clk_prevtrans==ULLDescriptor(~0)) { s->clk_prevtrans = lt->timeval; s->clk_numtrans = 0; } else if(s->clk_numtrans == 0) { s->clk_delta = lt->timeval - s->clk_prevtrans; s->clk_prevtrans = lt->timeval; s->clk_numtrans++; } else { if(s->clk_delta == (lt->timeval - s->clk_prevtrans)) { s->clk_numtrans++; s->clk_prevtrans = lt->timeval; if(s->clk_numtrans > LT_CLKPACK) { s->clk_prevval = ivalue + '0'; /* printf("Clock value '%d' for '%s' at %d (#%d)\n", ivalue, s->name, lt->timeval, s->clk_numtrans); */ return(1); } } else { if(s->clk_numtrans > LT_CLKPACK) { lt_flushclock(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } } } else { if(s->clk_numtrans > LT_CLKPACK) { lt_flushclock(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } s->clk_prevval = ivalue + '0'; } } /* normal trace handling */ last_change_delta = lt->position - s->last_change - 2; if(last_change_delta >= 256*65536) { numbytes = 3; } else if(last_change_delta >= 65536) { numbytes = 2; } else if(last_change_delta >= 256) { numbytes = 1; } else { numbytes = 0; } if(len<=32) { int start_position = lt->position; int tag; int optimized0 = ((value<_optimask[len])==0); int optimized1 = ((value<_optimask[len])==lt_optimask[len]); int optimized = optimized0|optimized1; if(!lt->numfacs_bytes) { if(optimized) { tag = (numbytes<<4) | (3+optimized1); /* for x3 and x4 cases */ } else { tag = (numbytes<<4); } lt->lt_emit_u8(lt, tag); switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, last_change_delta); break; case 1: lt->lt_emit_u16(lt, last_change_delta); break; case 2: lt->lt_emit_u24(lt, last_change_delta); break; case 3: lt->lt_emit_u32(lt, last_change_delta); break; } } else { switch(lt->numfacs_bytes) { case 1: lt->lt_emit_u8(lt, s->facnum); break; case 2: lt->lt_emit_u16(lt, s->facnum); break; case 3: lt->lt_emit_u24(lt, s->facnum); break; case 4: lt->lt_emit_u32(lt, s->facnum); break; } lt->lt_emit_u8(lt, optimized ? (3+optimized1) : 0); } s->last_change = start_position; if(s->rows>0) { if(s->rows >= 256*65536) { numbytes = 3; } else if(s->rows >= 65536) { numbytes = 2; } else if(s->rows >= 256) { numbytes = 1; } else { numbytes = 0; } switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, row); break; case 1: lt->lt_emit_u16(lt, row); break; case 2: lt->lt_emit_u24(lt, row); break; case 3: lt->lt_emit_u32(lt, row); break; } } if(!optimized) { if((lt->dictmode)&&(len>lt->mindictwidth)) { char *vpnt_orig = lt_expand_integer_to_bits(len, value); char *vpnt = vpnt_orig; while ( (*vpnt == '0') && (*(vpnt+1)) ) vpnt++; lt->dict = dslxt_splay (vpnt, lt->dict); if(!dslxt_success) { unsigned int vlen = strlen(vpnt)+1; char *vcopy = (char *)malloc(vlen); strcpy(vcopy, vpnt); lt->dict_string_mem_required += vlen; lt->dict = dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); if(!lt->dict16_offset) { if(lt->num_dict_entries==256) lt->dict16_offset = lt->position; } else if(!lt->dict24_offset) { if(lt->num_dict_entries==65536) lt->dict24_offset = lt->position; } else if(!lt->dict32_offset) { if(lt->num_dict_entries==(256*65536)) lt->dict32_offset = lt->position; } lt->num_dict_entries++; } if(lt->dict24_offset) { if(lt->dict32_offset) { lt->lt_emit_u32(lt, lt->dict->val); } else { lt->lt_emit_u24(lt, lt->dict->val); } } else { if(lt->dict16_offset) { lt->lt_emit_u16(lt, lt->dict->val); } else { lt->lt_emit_u8(lt, lt->dict->val); } } } else if(len<9) { value <<= (8-len); rc=lt->lt_emit_u8(lt, value); } else if(len<17) { value <<= (16-len); rc=lt->lt_emit_u16(lt, value); } else if(len<25) { value <<= (24-len); rc=lt->lt_emit_u24(lt, value); } else { value <<= (32-len); rc=lt->lt_emit_u32(lt, value); } } } if(lt->timebuff) { lt->timechangecount++; if(lt->timecurr) { lt->timecurr->next = lt->timebuff; lt->timecurr = lt->timebuff; } else { lt->timehead = lt->timecurr = lt->timebuff; } lt->timebuff=NULL; } } return(rc); } int lt_emit_value_double(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, double value) { int rc=0; int start_position; int tag; if((!lt)||(!s)) return(rc); if(!lt->emitted) lt->emitted = 1; while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } if((s->flags)<_SYM_F_DOUBLE) { int numbytes; /* number of bytes to store value minus one */ unsigned int last_change_delta = lt->position - s->last_change - 2; if(!lt->numfacs_bytes) { if(last_change_delta >= 256*65536) { numbytes = 3; } else if(last_change_delta >= 65536) { numbytes = 2; } else if(last_change_delta >= 256) { numbytes = 1; } else { numbytes = 0; } start_position = lt->position; s->last_change = start_position; tag = (numbytes<<4); lt->lt_emit_u8(lt, tag); switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, last_change_delta); break; case 1: lt->lt_emit_u16(lt, last_change_delta); break; case 2: lt->lt_emit_u24(lt, last_change_delta); break; case 3: lt->lt_emit_u32(lt, last_change_delta); break; } } else { switch(lt->numfacs_bytes) { case 1: lt->lt_emit_u8(lt, s->facnum); break; case 2: lt->lt_emit_u16(lt, s->facnum); break; case 3: lt->lt_emit_u24(lt, s->facnum); break; case 4: lt->lt_emit_u32(lt, s->facnum); break; } } if(s->rows>0) { if(s->rows >= 256*65536) { numbytes = 3; } else if(s->rows >= 65536) { numbytes = 2; } else if(s->rows >= 256) { numbytes = 1; } else { numbytes = 0; } switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, row); break; case 1: lt->lt_emit_u16(lt, row); break; case 2: lt->lt_emit_u24(lt, row); break; case 3: lt->lt_emit_u32(lt, row); break; } } rc=lt->lt_emit_double(lt, value); if(lt->timebuff) { lt->timechangecount++; if(lt->timecurr) { lt->timecurr->next = lt->timebuff; lt->timecurr = lt->timebuff; } else { lt->timehead = lt->timecurr = lt->timebuff; } lt->timebuff=NULL; } } return(rc); } int lt_emit_value_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value) { int rc=0; int start_position; int tag; if((!lt)||(!s)||(!value)) return(rc); if(!lt->emitted) lt->emitted = 1; while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } if((s->flags)<_SYM_F_STRING) { int numbytes; /* number of bytes to store value minus one */ unsigned int last_change_delta = lt->position - s->last_change - 2; if(!lt->numfacs_bytes) { if(last_change_delta >= 256*65536) { numbytes = 3; } else if(last_change_delta >= 65536) { numbytes = 2; } else if(last_change_delta >= 256) { numbytes = 1; } else { numbytes = 0; } start_position = lt->position; s->last_change = start_position; tag = (numbytes<<4); lt->lt_emit_u8(lt, tag); switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, last_change_delta); break; case 1: lt->lt_emit_u16(lt, last_change_delta); break; case 2: lt->lt_emit_u24(lt, last_change_delta); break; case 3: lt->lt_emit_u32(lt, last_change_delta); break; } } else { switch(lt->numfacs_bytes) { case 1: lt->lt_emit_u8(lt, s->facnum); break; case 2: lt->lt_emit_u16(lt, s->facnum); break; case 3: lt->lt_emit_u24(lt, s->facnum); break; case 4: lt->lt_emit_u32(lt, s->facnum); break; } } if(s->rows>0) { if(s->rows >= 256*65536) { numbytes = 3; } else if(s->rows >= 65536) { numbytes = 2; } else if(s->rows >= 256) { numbytes = 1; } else { numbytes = 0; } switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, row); break; case 1: lt->lt_emit_u16(lt, row); break; case 2: lt->lt_emit_u24(lt, row); break; case 3: lt->lt_emit_u32(lt, row); break; } } rc=lt->lt_emit_string(lt, value); if(lt->timebuff) { lt->timechangecount++; if(lt->timecurr) { lt->timecurr->next = lt->timebuff; lt->timecurr = lt->timebuff; } else { lt->timehead = lt->timecurr = lt->timebuff; } lt->timebuff=NULL; } } return(rc); } int lt_emit_value_bit_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value) { int rc=0; int start_position; int tag, tagadd; if((!lt)||(!s)||(!value)||(!*value)) return(rc); if(!lt->emitted) lt->emitted = 1; while(s->aliased_to) /* find root alias if exists */ { s=s->aliased_to; } if(!(s->flags&(LT_SYM_F_DOUBLE|LT_SYM_F_STRING))) { int numbytes; /* number of bytes to store value minus one */ char *pnt; int mvl=0; char ch; char prevch; unsigned int last_change_delta; unsigned int len = ((s->flags)<_SYM_F_INTEGER) ? 32 : s->len; if((lt->clock_compress)&&(s->rows==0)) { if((len>1)&&(len<=32)) { int legal = 0; int ivalue = 0; unsigned int i; char *pntv = value; int delta1, delta2; for(i=0;i0)) { pntv--; } else { legal = 0; break; } } ivalue = (((unsigned int)ivalue) << 1); ivalue |= (*pntv & 1); legal = 1; pntv++; } s->clk_mask <<= 1; s->clk_mask |= legal; if( ((s->clk_mask&0x1f)==0x1f) && ( (delta1=(ivalue - s->clk_prevval1) & lt_optimask[s->len]) == ((s->clk_prevval1 - s->clk_prevval3) & lt_optimask[s->len]) ) && ( (delta2=(s->clk_prevval - s->clk_prevval2) & lt_optimask[s->len]) == ((s->clk_prevval2 - s->clk_prevval4) & lt_optimask[s->len]) ) && ( (delta1==delta2) || ((!delta1)&&(!delta2)) ) ) { if(s->clk_prevtrans==ULLDescriptor(~0)) { s->clk_prevtrans = lt->timeval; s->clk_numtrans = 0; } else if(s->clk_numtrans == 0) { s->clk_delta = lt->timeval - s->clk_prevtrans; s->clk_prevtrans = lt->timeval; s->clk_numtrans++; } else { if(s->clk_delta == (lt->timeval - s->clk_prevtrans)) { s->clk_numtrans++; s->clk_prevtrans = lt->timeval; if(s->clk_numtrans > LT_CLKPACK_M) { s->clk_prevval4 = s->clk_prevval3; s->clk_prevval3 = s->clk_prevval2; s->clk_prevval2 = s->clk_prevval1; s->clk_prevval1 = s->clk_prevval; s->clk_prevval = ivalue; /* printf("Clock value '%08x' for '%s' [len=%d] at %lld (#%d)\n", ivalue, s->name, len, lt->timeval, s->clk_numtrans); */ return(1); } } else { if(s->clk_numtrans > LT_CLKPACK_M) { lt_flushclock_m(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } } } else { if(s->clk_numtrans > LT_CLKPACK_M) { lt_flushclock_m(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } s->clk_prevval4 = s->clk_prevval3; s->clk_prevval3 = s->clk_prevval2; s->clk_prevval2 = s->clk_prevval1; s->clk_prevval1 = s->clk_prevval; s->clk_prevval = ivalue; } else if(len==1) /* possible clock handling */ { if(((s->clk_prevval == '1') && (value[0]=='0')) || ((s->clk_prevval == '0') && (value[0]=='1'))) { if(s->clk_prevtrans==ULLDescriptor(~0)) { s->clk_prevtrans = lt->timeval; s->clk_numtrans = 0; } else if(s->clk_numtrans == 0) { s->clk_delta = lt->timeval - s->clk_prevtrans; s->clk_prevtrans = lt->timeval; s->clk_numtrans++; } else { if(s->clk_delta == (lt->timeval - s->clk_prevtrans)) { s->clk_numtrans++; s->clk_prevtrans = lt->timeval; if(s->clk_numtrans > LT_CLKPACK) { s->clk_prevval = value[0]; /* printf("Clock value '%c' for '%s' at %lld (#%d)\n", value[0], s->name, lt->timeval, s->clk_numtrans); */ return(1); } } else { if(s->clk_numtrans > LT_CLKPACK) { lt_flushclock(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } } } else { if(s->clk_numtrans > LT_CLKPACK) { lt_flushclock(lt, s); /* flush clock then continue below! */ } else { s->clk_prevtrans=ULLDescriptor(~0); } } s->clk_prevval = value[0]; } } /* normal trace handling */ last_change_delta = lt->position - s->last_change - 2; if(last_change_delta >= 256*65536) { numbytes = 3; } else if(last_change_delta >= 65536) { numbytes = 2; } else if(last_change_delta >= 256) { numbytes = 1; } else { numbytes = 0; } pnt = value; prevch = *pnt; while((ch=*(pnt++))) { switch(ch) { case '0': case '1': mvl|=LT_MVL_2; break; case 'Z': case 'z': case 'X': case 'x': mvl|=LT_MVL_4; break; default: mvl|=LT_MVL_9; break; } if(prevch!=ch) prevch = 0; } switch(prevch) { case 0x00: tagadd = 0; break; case '0': tagadd = 3; break; case '1': tagadd = 4; break; case 'Z': case 'z': tagadd = 5; break; case 'X': case 'x': tagadd = 6; break; case 'H': case 'h': tagadd = 7; break; case 'U': case 'u': tagadd = 8; break; case 'W': case 'w': tagadd = 9; break; case 'L': case 'l': tagadd = 0xa; break; default: tagadd = 0xb; break; } if(mvl) { start_position = lt->position; if(!lt->numfacs_bytes) { if(tagadd) { tag = (numbytes<<4) + tagadd; } else { tag = (numbytes<<4) + ((mvl<_MVL_9)? 2 : ((mvl<_MVL_4)? 1 : 0)); } lt->lt_emit_u8(lt, tag); switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, last_change_delta); break; case 1: lt->lt_emit_u16(lt, last_change_delta); break; case 2: lt->lt_emit_u24(lt, last_change_delta); break; case 3: lt->lt_emit_u32(lt, last_change_delta); break; } } else { switch(lt->numfacs_bytes) { case 1: lt->lt_emit_u8(lt, s->facnum); break; case 2: lt->lt_emit_u16(lt, s->facnum); break; case 3: lt->lt_emit_u24(lt, s->facnum); break; case 4: lt->lt_emit_u32(lt, s->facnum); break; } if(tagadd) { lt->lt_emit_u8(lt, tagadd); } else { lt->lt_emit_u8(lt, (mvl<_MVL_9)? 2 : ((mvl<_MVL_4)? 1 : 0) ); } } s->last_change = start_position; if(s->rows>0) { if(s->rows >= 256*65536) { numbytes = 3; } else if(s->rows >= 65536) { numbytes = 2; } else if(s->rows >= 256) { numbytes = 1; } else { numbytes = 0; } switch(numbytes&3) { case 0: lt->lt_emit_u8(lt, row); break; case 1: lt->lt_emit_u16(lt, row); break; case 2: lt->lt_emit_u24(lt, row); break; case 3: lt->lt_emit_u32(lt, row); break; } } if(!tagadd) { unsigned int len2 = ((s->flags)<_SYM_F_INTEGER) ? 32 : s->len; if((mvl & (LT_MVL_2|LT_MVL_4|LT_MVL_9)) == LT_MVL_2) { unsigned int i; int bitpos = 7; int outval = 0; int thisval= 0; pnt = value; if((lt->dictmode)&&(len2>lt->mindictwidth)) { char *vpnt = value; while ( (*vpnt == '0') && (*(vpnt+1)) ) vpnt++; lt->dict = dslxt_splay (vpnt, lt->dict); if(!dslxt_success) { unsigned int vlen = strlen(vpnt)+1; char *vcopy = (char *)malloc(vlen); strcpy(vcopy, vpnt); lt->dict_string_mem_required += vlen; lt->dict = dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); if(!lt->dict16_offset) { if(lt->num_dict_entries==256) lt->dict16_offset = lt->position; } else if(!lt->dict24_offset) { if(lt->num_dict_entries==65536) lt->dict24_offset = lt->position; } else if(!lt->dict32_offset) { if(lt->num_dict_entries==(256*65536)) lt->dict32_offset = lt->position; } lt->num_dict_entries++; } if(lt->dict24_offset) { if(lt->dict32_offset) { lt->lt_emit_u32(lt, lt->dict->val); } else { lt->lt_emit_u24(lt, lt->dict->val); } } else { if(lt->dict16_offset) { lt->lt_emit_u16(lt, lt->dict->val); } else { lt->lt_emit_u8(lt, lt->dict->val); } } } else for(i=0;ilt_emit_u8(lt, outval); outval = 0; bitpos = 7; } } } else if((mvl & (LT_MVL_4|LT_MVL_9)) == LT_MVL_4) { unsigned int i; int bitpos = 6; int outval = 0; int thisval= 0; pnt = value; for(i=0;ilt_emit_u8(lt, outval); outval = 0; bitpos = 6; } } } else /* if(mvl & LT_MVL_9) */ { unsigned int i; int bitpos = 4; int outval = 0; int thisval= 0; pnt = value; for(i=0;ilt_emit_u8(lt, outval); outval = 0; bitpos = 4; } } } } rc=1; } if(lt->timebuff) { lt->timechangecount++; if(lt->timecurr) { lt->timecurr->next = lt->timebuff; lt->timecurr = lt->timebuff; } else { lt->timehead = lt->timecurr = lt->timebuff; } lt->timebuff=NULL; } } return(rc); } /* * blackout functions */ void lt_set_dumpoff(struct lt_trace *lt) { if((lt)&&(!lt->dumpoff_active)) { struct lt_timetrail *ltt = calloc(1, sizeof(struct lt_timetrail)); ltt->timeval = lt->timeval; if(lt->dumpoffhead) { lt->dumpoffcurr->next = ltt; lt->dumpoffcurr = ltt; } else { lt->dumpoffhead = lt->dumpoffcurr = ltt; } lt->dumpoff_active = 1; lt->dumpoffcount++; } } void lt_set_dumpon(struct lt_trace *lt) { if((lt)&&(lt->dumpoff_active)) { struct lt_timetrail *ltt = calloc(1, sizeof(struct lt_timetrail)); ltt->timeval = lt->timeval; lt->dumpoffcurr->next = ltt; lt->dumpoffcurr = ltt; lt->dumpoff_active = 0; } } void lt_set_timezero(struct lt_trace *lt, lxtotime_t timeval) { if(lt) { lt->timezero = timeval; } } iverilog-10_1/vpi/lxt_write.h000066400000000000000000000201441265551621300163330ustar00rootroot00000000000000/* * Copyright (c) 2001-2012 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef DEFS_LXT_H #define DEFS_LXT_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #ifdef HAVE_INTTYPES_H #include #endif #ifndef HAVE_FSEEKO #define fseeko fseek #define ftello ftell #endif typedef struct dslxt_tree_node dslxt_Tree; struct dslxt_tree_node { dslxt_Tree * left, * right; char *item; unsigned int val; }; #define LT_HDRID (0x0138) #define LT_VERSION (0x0004) #define LT_TRLID (0xB4) #define LT_CLKPACK (4) #define LT_CLKPACK_M (2) #define LT_MVL_2 (1<<0) #define LT_MVL_4 (1<<1) #define LT_MVL_9 (1<<2) #define LT_MINDICTWIDTH (16) enum lt_zmode_types { LT_ZMODE_NONE, LT_ZMODE_GZIP, LT_ZMODE_BZIP2 }; #ifndef _MSC_VER typedef uint64_t lxttime_t; #define ULLDescriptor(x) x##ULL typedef int64_t lxtotime_t; #else typedef unsigned __int64 lxttime_t; #define ULLDescriptor(x) x##i64 typedef __int64 lxtotime_t; #endif struct lt_timetrail { struct lt_timetrail *next; lxttime_t timeval; unsigned int position; }; #define LT_SYMPRIME 500009 #define LT_SECTION_END (0) #define LT_SECTION_CHG (1) #define LT_SECTION_SYNC_TABLE (2) #define LT_SECTION_FACNAME (3) #define LT_SECTION_FACNAME_GEOMETRY (4) #define LT_SECTION_TIMESCALE (5) #define LT_SECTION_TIME_TABLE (6) #define LT_SECTION_INITIAL_VALUE (7) #define LT_SECTION_DOUBLE_TEST (8) #define LT_SECTION_TIME_TABLE64 (9) #define LT_SECTION_ZFACNAME_PREDEC_SIZE (10) #define LT_SECTION_ZFACNAME_SIZE (11) #define LT_SECTION_ZFACNAME_GEOMETRY_SIZE (12) #define LT_SECTION_ZSYNC_SIZE (13) #define LT_SECTION_ZTIME_TABLE_SIZE (14) #define LT_SECTION_ZCHG_PREDEC_SIZE (15) #define LT_SECTION_ZCHG_SIZE (16) #define LT_SECTION_ZDICTIONARY (17) #define LT_SECTION_ZDICTIONARY_SIZE (18) #define LT_SECTION_EXCLUDE_TABLE (19) #define LT_SECTION_TIMEZERO (20) struct lt_trace { FILE *handle; gzFile zhandle; dslxt_Tree *dict; /* dictionary manipulation */ unsigned int mindictwidth; unsigned int num_dict_entries; unsigned int dict_string_mem_required; dslxt_Tree **sorted_dict; /* assume dict8_offset == filepos zero */ unsigned int dict16_offset; unsigned int dict24_offset; unsigned int dict32_offset; int (*lt_emit_u8)(struct lt_trace *lt, int value); int (*lt_emit_u16)(struct lt_trace *lt, int value); int (*lt_emit_u24)(struct lt_trace *lt, int value); int (*lt_emit_u32)(struct lt_trace *lt, int value); int (*lt_emit_u64)(struct lt_trace *lt, int valueh, int valuel); int (*lt_emit_double)(struct lt_trace *lt, double value); int (*lt_emit_string)(struct lt_trace *lt, char *value); unsigned int position; unsigned int zfacname_predec_size, zfacname_size, zfacgeometry_size, zsync_table_size, ztime_table_size, zdictionary_size; unsigned int zpackcount, zchg_table_size, chg_table_size; struct lt_symbol *sym[LT_SYMPRIME]; struct lt_symbol **sorted_facs; struct lt_symbol *symchain; int numfacs, numfacs_bytes; int numfacbytes; int longestname; lxttime_t mintime, maxtime; int timescale; int initial_value; struct lt_timetrail *timehead, *timecurr, *timebuff; int timechangecount; struct lt_timetrail *dumpoffhead, *dumpoffcurr; int dumpoffcount; unsigned int change_field_offset; unsigned int facname_offset; unsigned int facgeometry_offset; unsigned int time_table_offset; unsigned int sync_table_offset; unsigned int initial_value_offset; unsigned int timescale_offset; unsigned int double_test_offset; unsigned int dictionary_offset; unsigned int exclude_offset; unsigned int timezero_offset; char *compress_fac_str; int compress_fac_len; lxttime_t timeval; /* for clock induction, current time */ lxtotime_t timezero; /* for allowing negative values */ unsigned dumpoff_active : 1; /* when set we're not dumping */ unsigned double_used : 1; unsigned do_strip_brackets : 1; unsigned clock_compress : 1; unsigned dictmode : 1; /* dictionary compression enabled */ unsigned zmode : 2; /* for value changes */ unsigned emitted : 1; /* gate off change field zmode changes when set */ }; struct lt_symbol { struct lt_symbol *next; struct lt_symbol *symchain; char *name; int namlen; int facnum; struct lt_symbol *aliased_to; unsigned int rows; int msb, lsb; int len; int flags; unsigned int last_change; lxttime_t clk_delta; lxttime_t clk_prevtrans; int clk_numtrans; int clk_prevval; int clk_prevval1; int clk_prevval2; int clk_prevval3; int clk_prevval4; unsigned char clk_mask; }; #define LT_SYM_F_BITS (0) #define LT_SYM_F_INTEGER (1<<0) #define LT_SYM_F_DOUBLE (1<<1) #define LT_SYM_F_STRING (1<<2) #define LT_SYM_F_ALIAS (1<<3) struct lt_trace * lt_init(const char *name); void lt_close(struct lt_trace *lt); struct lt_symbol * lt_symbol_find(struct lt_trace *lt, const char *name); struct lt_symbol * lt_symbol_add(struct lt_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags); struct lt_symbol * lt_symbol_alias(struct lt_trace *lt, const char *existing_name, const char *alias, int msb, int lsb); void lt_symbol_bracket_stripping(struct lt_trace *lt, int doit); /* lt_set_no_interlace implies bzip2 compression. if you use lt_set_chg_compress before this, */ /* less efficient gzip compression will be used instead so make sure lt_set_no_interlace is first */ /* if you are using it! */ void lt_set_no_interlace(struct lt_trace *lt); void lt_set_chg_compress(struct lt_trace *lt); void lt_set_clock_compress(struct lt_trace *lt); void lt_set_dict_compress(struct lt_trace *lt, unsigned int minwidth); void lt_set_initial_value(struct lt_trace *lt, char value); void lt_set_timescale(struct lt_trace *lt, int timescale); void lt_set_timezero(struct lt_trace *lt, lxtotime_t timeval); int lt_set_time(struct lt_trace *lt, unsigned int timeval); int lt_inc_time_by_delta(struct lt_trace *lt, unsigned int timeval); int lt_set_time64(struct lt_trace *lt, lxttime_t timeval); int lt_inc_time_by_delta64(struct lt_trace *lt, lxttime_t timeval); /* allows blackout regions in LXT files */ void lt_set_dumpoff(struct lt_trace *lt); void lt_set_dumpon(struct lt_trace *lt); /* * value change functions..note that if the value string len for * lt_emit_value_bit_string() is shorter than the symbol length * it will be left justified with the rightmost character used as * a repeat value that will be propagated to pad the value string out: * * "10x" for 8 bits becomes "10xxxxxx" * "z" for 8 bits becomes "zzzzzzzz" */ int lt_emit_value_int(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, int value); int lt_emit_value_double(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, double value); int lt_emit_value_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value); int lt_emit_value_bit_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value); #ifdef __cplusplus } #endif #endif iverilog-10_1/vpi/lz4.c000066400000000000000000001531321265551621300150220ustar00rootroot00000000000000/* LZ4 - Fast LZ compression algorithm Copyright (C) 2011-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ /************************************** * Tuning parameters **************************************/ /* * HEAPMODE : * Select how default compression functions will allocate memory for their hash table, * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). */ #define HEAPMODE 0 /* * ACCELERATION_DEFAULT : * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 */ #define ACCELERATION_DEFAULT 1 /************************************** * CPU Feature Detection **************************************/ /* * LZ4_FORCE_SW_BITCOUNT * Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ # define LZ4_FORCE_SW_BITCOUNT #endif /************************************** * Includes **************************************/ #include "lz4.h" /************************************** * Compiler Options **************************************/ #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ #else # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # if defined(__GNUC__) || defined(__clang__) # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif # else # define FORCE_INLINE static # endif /* __STDC_VERSION__ */ #endif /* _MSC_VER */ /* LZ4_GCC_VERSION is defined into lz4.h */ #if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else # define expect(expr,value) (expr) #endif #define likely(expr) expect((expr) != 0, 1) #define unlikely(expr) expect((expr) != 0, 0) /************************************** * Memory routines **************************************/ #include /* malloc, calloc, free */ #define ALLOCATOR(n,s) calloc(n,s) #define FREEMEM free #include /* memset, memcpy */ #define MEM_INIT memset /************************************** * Basic Types **************************************/ #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif /************************************** * Reading and writing into memory **************************************/ #define STEPSIZE sizeof(size_t) static unsigned LZ4_64bits(void) { return sizeof(void*)==8; } static unsigned LZ4_isLittleEndian(void) { const union { U32 i; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } static U16 LZ4_read16(const void* memPtr) { U16 val16; memcpy(&val16, memPtr, 2); return val16; } static U16 LZ4_readLE16(const void* memPtr) { if (LZ4_isLittleEndian()) { return LZ4_read16(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; return (U16)((U16)p[0] + (p[1]<<8)); } } static void LZ4_writeLE16(void* memPtr, U16 value) { if (LZ4_isLittleEndian()) { memcpy(memPtr, &value, 2); } else { BYTE* p = (BYTE*)memPtr; p[0] = (BYTE) value; p[1] = (BYTE)(value>>8); } } static U32 LZ4_read32(const void* memPtr) { U32 val32; memcpy(&val32, memPtr, 4); return val32; } static U64 LZ4_read64(const void* memPtr) { U64 val64; memcpy(&val64, memPtr, 8); return val64; } static size_t LZ4_read_ARCH(const void* p) { if (LZ4_64bits()) return (size_t)LZ4_read64(p); else return (size_t)LZ4_read32(p); } static void LZ4_copy4(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 4); } static void LZ4_copy8(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 8); } /* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */ static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* e = (BYTE*)dstEnd; do { LZ4_copy8(d,s); d+=8; s+=8; } while (d>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctzll((U64)val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanForward( &r, (U32)val ); return (int)(r>>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctz((U32)val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif } } else /* Big Endian CPU */ { if (LZ4_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse64( &r, val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clzll((U64)val) >> 3); # else unsigned r; if (!(val>>32)) { r=4; } else { r=0; val>>=32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse( &r, (unsigned long)val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clz((U32)val) >> 3); # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif } } } static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) { const BYTE* const pStart = pIn; while (likely(pIn compression run slower on incompressible data */ /************************************** * Local Structures and types **************************************/ typedef struct { U32 hashTable[HASH_SIZE_U32]; U32 currentOffset; U32 initCheck; const BYTE* dictionary; BYTE* bufferStart; /* obsolete, used for slideInputBuffer */ U32 dictSize; } LZ4_stream_t_internal; typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive; typedef enum { byPtr, byU32, byU16 } tableType_t; typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive; typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { full = 0, partial = 1 } earlyEnd_directive; /************************************** * Local Utils **************************************/ int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } int LZ4_sizeofState() { return LZ4_STREAMSIZE; } /******************************** * Compression functions ********************************/ static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType) { if (tableType == byU16) return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); else return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } static const U64 prime5bytes = 889523592379ULL; static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType) { const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; const U32 hashMask = (1<> (40 - hashLog)) & hashMask; } static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType) { if (LZ4_64bits()) return LZ4_hashSequence64(sequence, tableType); return LZ4_hashSequence((U32)sequence, tableType); } static U32 LZ4_hashPosition(const void* p, tableType_t tableType) { return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); } static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) { switch (tableType) { case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } } } static void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 h = LZ4_hashPosition(p, tableType); LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); } static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) { if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; } if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; } { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ } static const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 h = LZ4_hashPosition(p, tableType); return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); } FORCE_INLINE int LZ4_compress_generic( void* const ctx, const char* const source, char* const dest, const int inputSize, const int maxOutputSize, const limitedOutput_directive outputLimited, const tableType_t tableType, const dict_directive dict, const dictIssue_directive dictIssue, const U32 acceleration) { LZ4_stream_t_internal* const dictPtr = (LZ4_stream_t_internal*)ctx; const BYTE* ip = (const BYTE*) source; const BYTE* base; const BYTE* lowLimit; const BYTE* const lowRefLimit = ip - dictPtr->dictSize; const BYTE* const dictionary = dictPtr->dictionary; const BYTE* const dictEnd = dictionary + dictPtr->dictSize; const size_t dictDelta = dictEnd - (const BYTE*)source; const BYTE* anchor = (const BYTE*) source; const BYTE* const iend = ip + inputSize; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = iend - LASTLITERALS; BYTE* op = (BYTE*) dest; BYTE* const olimit = op + maxOutputSize; U32 forwardH; size_t refDelta=0; /* Init conditions */ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ switch(dict) { case noDict: default: base = (const BYTE*)source; lowLimit = (const BYTE*)source; break; case withPrefix64k: base = (const BYTE*)source - dictPtr->currentOffset; lowLimit = (const BYTE*)source - dictPtr->dictSize; break; case usingExtDict: base = (const BYTE*)source - dictPtr->currentOffset; lowLimit = (const BYTE*)source; break; } if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ if (inputSize> LZ4_skipTrigger); if (unlikely(forwardIp > mflimit)) goto _last_literals; match = LZ4_getPositionOnHash(h, ctx, tableType, base); if (dict==usingExtDict) { if (match<(const BYTE*)source) { refDelta = dictDelta; lowLimit = dictionary; } else { refDelta = 0; lowLimit = (const BYTE*)source; } } forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, ctx, tableType, base); } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0) || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) ); } /* Catch up */ while ((ip>anchor) && (match+refDelta > lowLimit) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; } { /* Encode Literal length */ unsigned litLength = (unsigned)(ip - anchor); token = op++; if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit))) return 0; /* Check output limit */ if (litLength>=RUN_MASK) { int len = (int)litLength-RUN_MASK; *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(litLength< matchlimit) limit = matchlimit; matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); ip += MINMATCH + matchLength; if (ip==limit) { unsigned more = LZ4_count(ip, (const BYTE*)source, matchlimit); matchLength += more; ip += more; } } else { matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); ip += MINMATCH + matchLength; } if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength>>8) > olimit))) return 0; /* Check output limit */ if (matchLength>=ML_MASK) { *token += ML_MASK; matchLength -= ML_MASK; for (; matchLength >= 510 ; matchLength-=510) { *op++ = 255; *op++ = 255; } if (matchLength >= 255) { matchLength-=255; *op++ = 255; } *op++ = (BYTE)matchLength; } else *token += (BYTE)(matchLength); } anchor = ip; /* Test end of chunk */ if (ip > mflimit) break; /* Fill table */ LZ4_putPosition(ip-2, ctx, tableType, base); /* Test next position */ match = LZ4_getPosition(ip, ctx, tableType, base); if (dict==usingExtDict) { if (match<(const BYTE*)source) { refDelta = dictDelta; lowLimit = dictionary; } else { refDelta = 0; lowLimit = (const BYTE*)source; } } LZ4_putPosition(ip, ctx, tableType, base); if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1) && (match+MAX_DISTANCE>=ip) && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) ) { token=op++; *token=0; goto _next_match; } /* Prepare next loop */ forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: /* Encode Last Literals */ { const size_t lastRun = (size_t)(iend - anchor); if ((outputLimited) && ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */ if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRun<= LZ4_compressBound(inputSize)) { if (inputSize < LZ4_64Klimit) return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration); else return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); } else { if (inputSize < LZ4_64Klimit) return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); else return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); } } int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { #if (HEAPMODE) void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ #else LZ4_stream_t ctx; void* ctxPtr = &ctx; #endif int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); #if (HEAPMODE) FREEMEM(ctxPtr); #endif return result; } int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); } /* hidden debug function */ /* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t ctx; LZ4_resetStream(&ctx); if (inputSize < LZ4_64Klimit) return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); else return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); } /******************************** * destSize variant ********************************/ static int LZ4_compress_destSize_generic( void* const ctx, const char* const src, char* const dst, int* const srcSizePtr, const int targetDstSize, const tableType_t tableType) { const BYTE* ip = (const BYTE*) src; const BYTE* base = (const BYTE*) src; const BYTE* lowLimit = (const BYTE*) src; const BYTE* anchor = ip; const BYTE* const iend = ip + *srcSizePtr; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = iend - LASTLITERALS; BYTE* op = (BYTE*) dst; BYTE* const oend = op + targetDstSize; BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); BYTE* const oMaxSeq = oMaxLit - 1 /* token */; U32 forwardH; /* Init conditions */ if (targetDstSize < 1) return 0; /* Impossible to store anything */ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ if (*srcSizePtr> LZ4_skipTrigger); if (unlikely(forwardIp > mflimit)) goto _last_literals; match = LZ4_getPositionOnHash(h, ctx, tableType, base); forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, ctx, tableType, base); } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) || (LZ4_read32(match) != LZ4_read32(ip)) ); } /* Catch up */ while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } { /* Encode Literal length */ unsigned litLength = (unsigned)(ip - anchor); token = op++; if (op + ((litLength+240)/255) + litLength > oMaxLit) { /* Not enough space for a last match */ op--; goto _last_literals; } if (litLength>=RUN_MASK) { unsigned len = litLength - RUN_MASK; *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(litLength< oMaxMatch) { /* Match description too long : reduce it */ matchLength = (15-1) + (oMaxMatch-op) * 255; } //printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH); ip += MINMATCH + matchLength; if (matchLength>=ML_MASK) { *token += ML_MASK; matchLength -= ML_MASK; while (matchLength >= 255) { matchLength-=255; *op++ = 255; } *op++ = (BYTE)matchLength; } else *token += (BYTE)(matchLength); } anchor = ip; /* Test end of block */ if (ip > mflimit) break; if (op > oMaxSeq) break; /* Fill table */ LZ4_putPosition(ip-2, ctx, tableType, base); /* Test next position */ match = LZ4_getPosition(ip, ctx, tableType, base); LZ4_putPosition(ip, ctx, tableType, base); if ( (match+MAX_DISTANCE>=ip) && (LZ4_read32(match)==LZ4_read32(ip)) ) { token=op++; *token=0; goto _next_match; } /* Prepare next loop */ forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: /* Encode Last Literals */ { size_t lastRunSize = (size_t)(iend - anchor); if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) { /* adapt lastRunSize to fill 'dst' */ lastRunSize = (oend-op) - 1; lastRunSize -= (lastRunSize+240)/255; } ip = anchor + lastRunSize; if (lastRunSize >= RUN_MASK) { size_t accumulator = lastRunSize - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */ { return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); } else { if (*srcSizePtr < LZ4_64Klimit) return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16); else return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr); } } int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) { #if (HEAPMODE) void* ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ #else LZ4_stream_t ctxBody; void* ctx = &ctxBody; #endif int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); #if (HEAPMODE) FREEMEM(ctx); #endif return result; } /******************************** * Streaming functions ********************************/ LZ4_stream_t* LZ4_createStream(void) { LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64); LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ LZ4_resetStream(lz4s); return lz4s; } void LZ4_resetStream (LZ4_stream_t* LZ4_stream) { MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); } int LZ4_freeStream (LZ4_stream_t* LZ4_stream) { FREEMEM(LZ4_stream); return (0); } #define HASH_UNIT sizeof(size_t) int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) { LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; const BYTE* p = (const BYTE*)dictionary; const BYTE* const dictEnd = p + dictSize; const BYTE* base; if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */ LZ4_resetStream(LZ4_dict); if (dictSize < (int)HASH_UNIT) { dict->dictionary = NULL; dict->dictSize = 0; return 0; } if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; dict->currentOffset += 64 KB; base = p - dict->currentOffset; dict->dictionary = p; dict->dictSize = (U32)(dictEnd - p); dict->currentOffset += dict->dictSize; while (p <= dictEnd-HASH_UNIT) { LZ4_putPosition(p, dict->hashTable, byU32, base); p+=3; } return dict->dictSize; } static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src) { if ((LZ4_dict->currentOffset > 0x80000000) || ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */ { /* rescale hash table */ U32 delta = LZ4_dict->currentOffset - 64 KB; const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; int i; for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; else LZ4_dict->hashTable[i] -= delta; } LZ4_dict->currentOffset = 64 KB; if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; } } int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_stream; const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; const BYTE* smallest = (const BYTE*) source; if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */ if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd; LZ4_renormDictT(streamPtr, smallest); if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; /* Check overlapping input/dictionary space */ { const BYTE* sourceEnd = (const BYTE*) source + inputSize; if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { streamPtr->dictSize = (U32)(dictEnd - sourceEnd); if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; streamPtr->dictionary = dictEnd - streamPtr->dictSize; } } /* prefix mode : source data follows dictionary */ if (dictEnd == (const BYTE*)source) { int result; if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration); else result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration); streamPtr->dictSize += (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; } /* external dictionary mode */ { int result; if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration); else result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration); streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; } } /* Hidden debug function, to force external dictionary mode */ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize) { LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_dict; int result; const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; const BYTE* smallest = dictEnd; if (smallest > (const BYTE*) source) smallest = (const BYTE*) source; LZ4_renormDictT((LZ4_stream_t_internal*)LZ4_dict, smallest); result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; } int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) { LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; const BYTE* previousDictEnd = dict->dictionary + dict->dictSize; if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */ if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize; memmove(safeBuffer, previousDictEnd - dictSize, dictSize); dict->dictionary = (const BYTE*)safeBuffer; dict->dictSize = (U32)dictSize; return dictSize; } /******************************* * Decompression functions *******************************/ /* * This generic decompression function cover all use cases. * It shall be instantiated several times, using different sets of directives * Note that it is essential this generic function is really inlined, * in order to remove useless branches during compilation optimization. */ FORCE_INLINE int LZ4_decompress_generic( const char* const source, char* const dest, int inputSize, int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ int endOnInput, /* endOnOutputSize, endOnInputSize */ int partialDecoding, /* full, partial */ int targetOutputSize, /* only used if partialDecoding==partial */ int dict, /* noDict, withPrefix64k, usingExtDict */ const BYTE* const lowPrefix, /* == dest if dict == noDict */ const BYTE* const dictStart, /* only if dict==usingExtDict */ const size_t dictSize /* note : = 0 if noDict */ ) { /* Local Variables */ const BYTE* ip = (const BYTE*) source; const BYTE* const iend = ip + inputSize; BYTE* op = (BYTE*) dest; BYTE* const oend = op + outputSize; BYTE* cpy; BYTE* oexit = op + targetOutputSize; const BYTE* const lowLimit = lowPrefix - dictSize; const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize; const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4}; const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3}; const int safeDecode = (endOnInput==endOnInputSize); const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); /* Special cases */ if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); /* Main Loop */ while (1) { unsigned token; size_t length; const BYTE* match; /* get literal length */ token = *ip++; if ((length=(token>>ML_BITS)) == RUN_MASK) { unsigned s; do { s = *ip++; length += s; } while (likely((endOnInput)?ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) || ((!endOnInput) && (cpy>oend-COPYLENGTH))) { if (partialDecoding) { if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ } else { if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ } memcpy(op, ip, length); ip += length; op += length; break; /* Necessarily EOF, due to parsing restrictions */ } LZ4_wildCopy(op, ip, cpy); ip += length; op = cpy; /* get offset */ match = cpy - LZ4_readLE16(ip); ip+=2; if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */ /* get matchlength */ length = token & ML_MASK; if (length == ML_MASK) { unsigned s; do { if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error; s = *ip++; length += s; } while (s==255); if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */ } length += MINMATCH; /* check external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */ if (length <= (size_t)(lowPrefix-match)) { /* match can be copied as a single segment from external dictionary */ match = dictEnd - (lowPrefix-match); memmove(op, match, length); op += length; } else { /* match encompass external dictionary and current segment */ size_t copySize = (size_t)(lowPrefix-match); memcpy(op, dictEnd - copySize, copySize); op += copySize; copySize = length - copySize; if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */ { BYTE* const endOfMatch = op + copySize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) *op++ = *copyFrom++; } else { memcpy(op, lowPrefix, copySize); op += copySize; } } continue; } /* copy repeated sequence */ cpy = op + length; if (unlikely((op-match)<8)) { const size_t dec64 = dec64table[op-match]; op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += dec32table[op-match]; LZ4_copy4(op+4, match); op += 8; match -= dec64; } else { LZ4_copy8(op, match); op+=8; match+=8; } if (unlikely(cpy>oend-12)) { if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */ if (op < oend-8) { LZ4_wildCopy(op, match, oend-8); match += (oend-8) - op; op = oend-8; } while (opprefixSize = (size_t) dictSize; lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; lz4sd->externalDict = NULL; lz4sd->extDictSize = 0; return 1; } /* *_continue() : These decoding functions allow decompression of multiple blocks in "streaming" mode. Previously decoded blocks must still be available at the memory position where they were decoded. If it's not possible, save the relevant part of decoded data into a safe buffer, and indicate where it stands using LZ4_setStreamDecode() */ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) { LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; int result; if (lz4sd->prefixEnd == (BYTE*)dest) { result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += result; lz4sd->prefixEnd += result; } else { lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize = result; lz4sd->prefixEnd = (BYTE*)dest + result; } return result; } int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) { LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; int result; if (lz4sd->prefixEnd == (BYTE*)dest) { result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += originalSize; lz4sd->prefixEnd += originalSize; } else { lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = (BYTE*)dest - lz4sd->extDictSize; result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize = originalSize; lz4sd->prefixEnd = (BYTE*)dest + originalSize; } return result; } /* Advanced decoding functions : *_usingDict() : These decoding functions work the same as "_continue" ones, the dictionary must be explicitly provided within parameters */ FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize) { if (dictSize==0) return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0); if (dictStart+dictSize == dest) { if (dictSize >= (int)(64 KB - 1)) return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0); return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0); } return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); } int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) { return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); } /* debug function */ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } /*************************************************** * Obsolete Functions ***************************************************/ /* obsolete compression functions */ int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); } int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); } int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } /* These function names are deprecated and should no longer be used. They are only provided here for compatibility with older user programs. - LZ4_uncompress is totally equivalent to LZ4_decompress_fast - LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe */ int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } /* Obsolete Streaming functions */ int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } static void LZ4_init(LZ4_stream_t_internal* lz4ds, BYTE* base) { MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE); lz4ds->bufferStart = base; } int LZ4_resetStreamState(void* state, char* inputBuffer) { if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */ LZ4_init((LZ4_stream_t_internal*)state, (BYTE*)inputBuffer); return 0; } void* LZ4_create (char* inputBuffer) { void* lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64); LZ4_init ((LZ4_stream_t_internal*)lz4ds, (BYTE*)inputBuffer); return lz4ds; } char* LZ4_slideInputBuffer (void* LZ4_Data) { LZ4_stream_t_internal* ctx = (LZ4_stream_t_internal*)LZ4_Data; int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB); return (char*)(ctx->bufferStart + dictSize); } /* Obsolete streaming decompression functions */ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); } int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); } #endif /* LZ4_COMMONDEFS_ONLY */ iverilog-10_1/vpi/lz4.h000066400000000000000000000446161265551621300150350ustar00rootroot00000000000000/* LZ4 - Fast LZ compression algorithm Header File Copyright (C) 2011-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ #pragma once #if defined (__cplusplus) extern "C" { #endif /* * lz4.h provides block compression functions, and gives full buffer control to programmer. * If you need to generate inter-operable compressed data (respecting LZ4 frame specification), * and can let the library handle its own memory, please use lz4frame.h instead. */ /************************************** * Version **************************************/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ #define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) int LZ4_versionNumber (void); /************************************** * Tuning parameter **************************************/ /* * LZ4_MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #define LZ4_MEMORY_USAGE 14 /************************************** * Simple Functions **************************************/ int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize); int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize); /* LZ4_compress_default() : Compresses 'sourceSize' bytes from buffer 'source' into already allocated 'dest' buffer of size 'maxDestSize'. Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). It also runs faster, so it's a recommended setting. If the function cannot compress 'source' into a more limited 'dest' budget, compression stops *immediately*, and the function result is zero. As a consequence, 'dest' content is not valid. This function never writes outside 'dest' buffer, nor read outside 'source' buffer. sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) or 0 if compression fails LZ4_decompress_safe() : compressedSize : is the precise full size of the compressed block. maxDecompressedSize : is the size of destination buffer, which must be already allocated. return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) If destination buffer is not large enough, decoding will stop and output an error code (<0). If the source stream is detected malformed, the function will stop decoding and return a negative result. This function is protected against buffer overflow exploits, including malicious data packets. It never writes outside output buffer, nor reads outside input buffer. */ /************************************** * Advanced Functions **************************************/ #define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ #define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) /* LZ4_compressBound() : Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) This function is primarily useful for memory allocation purposes (destination buffer size). Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) inputSize : max supported value is LZ4_MAX_INPUT_SIZE return : maximum output size in a "worst case" scenario or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) */ int LZ4_compressBound(int inputSize); /* LZ4_compress_fast() : Same as LZ4_compress_default(), but allows to select an "acceleration" factor. The larger the acceleration value, the faster the algorithm, but also the lesser the compression. It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. An acceleration value of "1" is the same as regular LZ4_compress_default() Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. */ int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration); /* LZ4_compress_fast_extState() : Same compression function, just using an externally allocated memory space to store compression state. Use LZ4_sizeofState() to know how much memory must be allocated, and allocate it on 8-bytes boundaries (using malloc() typically). Then, provide it as 'void* state' to compression function. */ int LZ4_sizeofState(void); int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration); /* LZ4_compress_destSize() : Reverse the logic, by compressing as much data as possible from 'source' buffer into already allocated buffer 'dest' of size 'targetDestSize'. This function either compresses the entire 'source' content into 'dest' if it's large enough, or fill 'dest' buffer completely with as much data as possible from 'source'. *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. New value is necessarily <= old value. return : Nb bytes written into 'dest' (necessarily <= targetDestSize) or 0 if compression fails */ int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize); /* LZ4_decompress_fast() : originalSize : is the original and therefore uncompressed size return : the number of bytes read from the source buffer (in other words, the compressed size) If the source stream is detected malformed, the function will stop decoding and return a negative result. Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes. note : This function fully respect memory boundaries for properly formed compressed data. It is a bit faster than LZ4_decompress_safe(). However, it does not provide any protection against intentionally modified data stream (malicious input). Use this function in trusted environment only (data to decode comes from a trusted source). */ int LZ4_decompress_fast (const char* source, char* dest, int originalSize); /* LZ4_decompress_safe_partial() : This function decompress a compressed block of size 'compressedSize' at position 'source' into destination buffer 'dest' of size 'maxDecompressedSize'. The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, reducing decompression time. return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize) Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. Always control how many bytes were decoded. If the source stream is detected malformed, the function will stop decoding and return a negative result. This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets */ int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize); /*********************************************** * Streaming Compression Functions ***********************************************/ #define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) #define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long)) /* * LZ4_stream_t * information structure to track an LZ4 stream. * important : init this structure content before first use ! * note : only allocated directly the structure if you are statically linking LZ4 * If you are using liblz4 as a DLL, please use below construction methods instead. */ typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t; /* * LZ4_resetStream * Use this function to init an allocated LZ4_stream_t structure */ void LZ4_resetStream (LZ4_stream_t* streamPtr); /* * LZ4_createStream will allocate and initialize an LZ4_stream_t structure * LZ4_freeStream releases its memory. * In the context of a DLL (liblz4), please use these methods rather than the static struct. * They are more future proof, in case of a change of LZ4_stream_t size. */ LZ4_stream_t* LZ4_createStream(void); int LZ4_freeStream (LZ4_stream_t* streamPtr); /* * LZ4_loadDict * Use this function to load a static dictionary into LZ4_stream. * Any previous data will be forgotten, only 'dictionary' will remain in memory. * Loading a size of 0 is allowed. * Return : dictionary size, in bytes (necessarily <= 64 KB) */ int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); /* * LZ4_compress_fast_continue * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio. * Important : Previous data blocks are assumed to still be present and unmodified ! * 'dst' buffer must be already allocated. * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. */ int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration); /* * LZ4_saveDict * If previously compressed data block is not guaranteed to remain available at its memory location * save it into a safer place (char* safeBuffer) * Note : you don't need to call LZ4_loadDict() afterwards, * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue() * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error */ int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize); /************************************************ * Streaming Decompression Functions ************************************************/ #define LZ4_STREAMDECODESIZE_U64 4 #define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t; /* * LZ4_streamDecode_t * information structure to track an LZ4 stream. * init this structure content using LZ4_setStreamDecode or memset() before first use ! * * In the context of a DLL (liblz4) please prefer usage of construction methods below. * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future. * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure * LZ4_freeStreamDecode releases its memory. */ LZ4_streamDecode_t* LZ4_createStreamDecode(void); int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); /* * LZ4_setStreamDecode * Use this function to instruct where to find the dictionary. * Setting a size of 0 is allowed (same effect as reset). * Return : 1 if OK, 0 if error */ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); /* *_continue() : These decoding functions allow decompression of multiple blocks in "streaming" mode. Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) In the case of a ring buffers, decoding buffer must be either : - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. In which case, encoding and decoding buffers do not need to be synchronized, and encoding ring buffer can have any size, including small ones ( < 64 KB). - _At least_ 64 KB + 8 bytes + maxBlockSize. In which case, encoding and decoding buffers do not need to be synchronized, and encoding ring buffer can have any size, including larger than decoding buffer. Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, and indicate where it is saved using LZ4_setStreamDecode() */ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize); int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize); /* Advanced decoding functions : *_usingDict() : These decoding functions work the same as a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue() They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure. */ int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize); int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize); /************************************** * Obsolete Functions **************************************/ /* Deprecate Warnings */ /* Should these warnings messages be a problem, it is generally possible to disable them, with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual for example. You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */ #ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK # define LZ4_DEPRECATE_WARNING_DEFBLOCK # define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if (LZ4_GCC_VERSION >= 405) || defined(__clang__) # define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) # elif (LZ4_GCC_VERSION >= 301) # define LZ4_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) # define LZ4_DEPRECATED(message) __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") # define LZ4_DEPRECATED(message) # endif #endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */ /* Obsolete compression functions */ /* These functions are planned to start generate warnings by r131 approximately */ int LZ4_compress (const char* source, char* dest, int sourceSize); int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); /* Obsolete decompression functions */ /* These function names are completely deprecated and must no longer be used. They are only provided here for compatibility with older programs. - LZ4_uncompress is the same as LZ4_decompress_fast - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe These function prototypes are now disabled; uncomment them only if you really need them. It is highly recommended to stop using these prototypes and migrate to maintained ones */ /* int LZ4_uncompress (const char* source, char* dest, int outputSize); */ /* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */ /* Obsolete streaming functions; use new streaming interface whenever possible */ LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer); LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer); LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state); /* Obsolete streaming decoding functions */ LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); #if defined (__cplusplus) } #endif iverilog-10_1/vpi/mt19937int.c000066400000000000000000000110141265551621300160510ustar00rootroot00000000000000/* * NOTE: This code as been slightly modified to interface with the * PLI implementations of $random. The copyright and license * information are given in the comment block below. * * The Modifications include: * * Remove the "main" function, as this is being used for its functions. * * Change the function prototypes to use ANSI/ISO C syntax. */ /* A C-program for MT19937: Integer version (1998/4/6) */ /* genrand() generates one pseudorandom unsigned integer (32bit) */ /* which is uniformly distributed among 0 to 2^32-1 for each */ /* call. sgenrand(seed) set initial values to the working area */ /* of 624 words. Before genrand(), sgenrand(seed) must be */ /* called once. (seed is any 32-bit integer except for 0). */ /* Coded by Takuji Nishimura, considering the suggestions by */ /* Topher Cooper and Marc Rieffel in July-Aug. 1997. */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public */ /* License as published by the Free Software Foundation; either */ /* version 2 of the License, or (at your option) any later */ /* version. */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* See the GNU Library General Public License for more details. */ /* You should have received a copy of the GNU Library General */ /* Public License along with this library; if not, write to the */ /* Free Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, */ /* MA 02110-1301, USA */ /* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */ /* When you use this, send an email to: matumoto@math.keio.ac.jp */ /* with an appropriate reference to your work. */ /* REFERENCE */ /* M. Matsumoto and T. Nishimura, */ /* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform */ /* Pseudo-Random Number Generator", */ /* ACM Transactions on Modeling and Computer Simulation, */ /* Vol. 8, No. 1, January 1998, pp 3--30. */ #include "sys_priv.h" /* Period parameters */ #define N 624 #define M 397 #define MATRIX_A 0x9908b0df /* constant vector a */ #define UPPER_MASK 0x80000000 /* most significant w-r bits */ #define LOWER_MASK 0x7fffffff /* least significant r bits */ /* Tempering parameters */ #define TEMPERING_MASK_B 0x9d2c5680 #define TEMPERING_MASK_C 0xefc60000 #define TEMPERING_SHIFT_U(y) (y >> 11) #define TEMPERING_SHIFT_S(y) (y << 7) #define TEMPERING_SHIFT_T(y) (y << 15) #define TEMPERING_SHIFT_L(y) (y >> 18) /* initializing the array with a NONZERO seed */ void sgenrand(struct context_s *context, unsigned long seed) { unsigned long *mt = context->mt; int mti; /* setting initial seeds to mt[N] using */ /* the generator Line 25 of Table 1 in */ /* [KNUTH 1981, The Art of Computer Programming */ /* Vol. 2 (2nd Ed.), pp102] */ mt[0]= seed & 0xffffffff; for (mti=1; mtimti = mti; } unsigned long genrand(struct context_s *context) { unsigned long y; unsigned long *mt = context->mt; int mti = context->mti; if (mti >= N) { /* generate N words at one time */ /* mag01[x] = x * MATRIX_A for x=0,1 */ static unsigned long mag01[2]={0x0, MATRIX_A}; int kk; if (mti == N+1) /* if sgenrand() has not been called, */ sgenrand(context, 4357); /* a default initial seed is used */ for (kk=0;kk> 1) ^ mag01[y & 0x1]; } for (;kk> 1) ^ mag01[y & 0x1]; } y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; mti = 0; } y = mt[mti++]; y ^= TEMPERING_SHIFT_U(y); y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; y ^= TEMPERING_SHIFT_L(y); context->mti = mti; return y; } iverilog-10_1/vpi/sdf_lexor.lex000066400000000000000000000134041265551621300166410ustar00rootroot00000000000000%option prefix="sdf" %option never-interactive %option nounput %option noinput %{ /* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sdf_priv.h" # include "sdf_parse_priv.h" # include "sdf_parse.h" # include # include # include # include static void process_quoted_string(void); static int lookup_keyword(const char*text); const char*sdf_parse_path = 0; static int yywrap(void) { return 1; } # define yylval sdflval %} %x CCOMMENT %x COND_EDGE_ID %x EDGE_ID %% /* Skip C++-style comments. */ "//".* { ; } /* Skip C-style comments. */ "/*" { BEGIN(CCOMMENT); } . { ; } \n { sdflloc.first_line += 1; } "*/" { BEGIN(0); } [ \m\t] { /* Skip white space. */; } /* Count lines so that the parser can assign line numbers. */ \n { sdflloc.first_line += 1; } /* The other edge identifiers. */ "01" {return K_01; } "10" {return K_10; } "0"[zZ] {return K_0Z; } [zZ]"1" {return K_Z1; } "1"[zZ] {return K_1Z; } [zZ]"0" {return K_Z0; } [pP][oO][sS][eE][dD][gG][eE] {return K_POSEDGE; } [nN][eE][gG][eE][dD][gG][eE] {return K_NEGEDGE; } [cC][oO][nN][dD] {return K_COND; } /* Integer values */ [0-9]+ { yylval.int_val = strtoul(yytext, 0, 10); return INTEGER; } /* Real values */ [0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)? { yylval.real_val = strtod(yytext, 0); return REAL_NUMBER; } ([a-zA-Z_]|(\\[^ \t\b\f\r\n]))([a-zA-Z0-9$_]|(\\[^ \t\b\f\r\n]))* { return lookup_keyword(yytext); } \"[^\"]*\" { process_quoted_string(); return QSTRING; } /* Scalar constants. */ ("1"?"'"[bB])?"0" { return K_LOGICAL_ZERO; } ("1"?"'"[bB])?"1" { return K_LOGICAL_ONE; } /* Equality operators. */ "==" { return K_EQ; } "!=" { return K_NE; } "===" { return K_CEQ; } "!==" { return K_CNE; } /* Other operators. */ "&&" { return K_LAND; } "||" { return K_LOR; } /* The HCHAR (hierarchy separator) is set by the SDF file itself. We recognize here the HCHAR. */ [./] { if (sdf_use_hchar==yytext[0]) return HCHAR; else return yytext[0]; } . { return yytext[0]; } %% static struct { const char*name; int code; } keywords[] = { { "ABSOLUTE", K_ABSOLUTE }, { "CELL", K_CELL }, { "CELLTYPE", K_CELLTYPE }, { "DATE", K_DATE }, { "COND", K_COND }, { "CONDELSE", K_CONDELSE }, { "DELAY", K_DELAY }, { "DELAYFILE", K_DELAYFILE }, { "DESIGN", K_DESIGN }, { "DIVIDER", K_DIVIDER }, { "HOLD", K_HOLD }, { "INCREMENT", K_INCREMENT }, { "INTERCONNECT", K_INTERCONNECT }, { "INSTANCE", K_INSTANCE }, { "IOPATH", K_IOPATH }, { "PROCESS", K_PROCESS }, { "PROGRAM", K_PROGRAM }, { "RECREM", K_RECREM }, { "RECOVERY", K_RECOVERY }, { "REMOVAL", K_REMOVAL }, { "SDFVERSION", K_SDFVERSION }, { "SETUP", K_SETUP }, { "SETUPHOLD", K_SETUPHOLD }, { "TEMPERATURE", K_TEMPERATURE }, { "TIMESCALE", K_TIMESCALE }, { "TIMINGCHECK", K_TIMINGCHECK }, { "VENDOR", K_VENDOR }, { "VERSION", K_VERSION }, { "VOLTAGE", K_VOLTAGE }, { "WIDTH", K_WIDTH }, { 0, IDENTIFIER } }; void start_edge_id(unsigned cond) { if (cond) BEGIN(COND_EDGE_ID); else BEGIN(EDGE_ID); } void stop_edge_id(void) { BEGIN(0); } static int lookup_keyword(const char*text) { unsigned idx, len, skip; for (idx = 0 ; keywords[idx].name ; idx += 1) { if (strcasecmp(text, keywords[idx].name) == 0) return keywords[idx].code; } /* Process any escaped characters. */ skip = 0; len = strlen(yytext); for (idx = 0; idx < len; idx += 1) { if (yytext[idx] == '\\') { skip += 1; idx += 1; } yytext[idx-skip] = yytext[idx]; } yytext[idx-skip] = 0; yylval.string_val = strdup(yytext); return IDENTIFIER; } /* * Create a string without the leading and trailing quotes. */ static void process_quoted_string(void) { char*endp; yylval.string_val = strdup(yytext+1); endp = yylval.string_val+strlen(yylval.string_val); assert(endp[-1] == '"'); endp[-1] = 0; } /* * Modern version of flex (>=2.5.9) can clean up the scanner data. */ static void destroy_sdf_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif } extern int sdfparse(void); void sdf_process_file(FILE*fd, const char*path) { yyrestart(fd); sdf_parse_path = path; sdfparse(); destroy_sdf_lexor(); sdf_parse_path = 0; } iverilog-10_1/vpi/sdf_parse.y000066400000000000000000000316301265551621300163030ustar00rootroot00000000000000 %{ /* * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern int sdflex(void); static void yyerror(const char*msg); # include "vpi_user.h" # include "sdf_parse_priv.h" # include "sdf_priv.h" # include # include # include # include "ivl_alloc.h" /* This is the hierarchy separator to use. */ char sdf_use_hchar = '.'; %} %union { unsigned long int_val; double real_val; char* string_val; struct sdf_delay_s delay; struct port_with_edge_s port_with_edge; struct sdf_delval_list_s delval_list; }; %token K_ABSOLUTE K_CELL K_CELLTYPE K_COND K_CONDELSE K_DATE K_DELAYFILE %token K_DELAY K_DESIGN K_DIVIDER K_HOLD K_INCREMENT K_INSTANCE %token K_INTERCONNECT K_IOPATH K_NEGEDGE K_POSEDGE K_PROCESS K_PROGRAM %token K_RECREM K_RECOVERY K_REMOVAL K_SDFVERSION K_SETUP K_SETUPHOLD %token K_TEMPERATURE K_TIMESCALE K_TIMINGCHECK K_VENDOR K_VERSION %token K_VOLTAGE K_WIDTH %token K_01 K_10 K_0Z K_Z1 K_1Z K_Z0 %token K_EQ K_NE K_CEQ K_CNE K_LOGICAL_ONE K_LOGICAL_ZERO %token K_LAND K_LOR %token HCHAR %token QSTRING IDENTIFIER %token REAL_NUMBER %token INTEGER %type celltype %type cell_instance %type hierarchical_identifier %type port port_instance port_interconnect %type signed_real_number %type delval rvalue rtriple signed_real_number_opt %type edge_identifier %type port_edge port_spec %type delval_list %left K_LOR %left K_LAND %left K_EQ K_NE K_CEQ K_CNE %% source_file : '(' K_DELAYFILE sdf_header_list cell_list ')' | '(' K_DELAYFILE error ')' { vpi_printf("%s:%d:SDF ERROR: Invalid DELAYFILE format\n", sdf_parse_path, @2.first_line); } ; sdf_header_list : sdf_header_list sdf_header_item | sdf_header_item ; sdf_header_item : sdfversion | design_name | date | vendor | program_name | program_version | hierarchy_divider | voltage | process | temperature | time_scale ; sdfversion : '(' K_SDFVERSION QSTRING ')' { free($3); } ; design_name : '(' K_DESIGN QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Design: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; date : '(' K_DATE QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Date: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; vendor : '(' K_VENDOR QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Vendor: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; program_name : '(' K_PROGRAM QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Program: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; program_version : '(' K_VERSION QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Program Version: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; hierarchy_divider : '(' K_DIVIDER '.' ')' { sdf_use_hchar = '.'; } | '(' K_DIVIDER '/' ')' { sdf_use_hchar = '/'; } | '(' K_DIVIDER HCHAR ')' { /* sdf_use_hchar no-change */; } ; voltage : '(' K_VOLTAGE rtriple ')' | '(' K_VOLTAGE signed_real_number ')' ; process : '(' K_PROCESS QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Process: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; temperature : '(' K_TEMPERATURE rtriple ')' | '(' K_TEMPERATURE signed_real_number ')' ; time_scale : '(' K_TIMESCALE REAL_NUMBER IDENTIFIER ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: TIMESCALE : %f%s\n", sdf_parse_path, @2.first_line, $3, $4); free($4); } | '(' K_TIMESCALE INTEGER IDENTIFIER ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: TIMESCALE : %lu%s\n", sdf_parse_path, @2.first_line, $3, $4); free($4); } ; cell_list : cell_list cell | cell ; cell : '(' K_CELL celltype cell_instance { sdf_select_instance($3, $4); /* find the instance in the design */} timing_spec_list_opt ')' { free($3); if ($4) free($4); } | '(' K_CELL error ')' { vpi_printf("%s:%d: Syntax error in CELL\n", sdf_parse_path, @2.first_line); } ; celltype : '(' K_CELLTYPE QSTRING ')' { $$ = $3; } ; cell_instance : '(' K_INSTANCE hierarchical_identifier ')' { $$ = $3; } | '(' K_INSTANCE ')' { $$ = strdup(""); } | '(' K_INSTANCE '*' ')' { $$ = 0; } | '(' K_INSTANCE error ')' { vpi_printf("%s:%d:SDF ERROR: Invalid/malformed INSTANCE argument\n", sdf_parse_path, @2.first_line); $$ = strdup(""); } ; timing_spec_list_opt : /* Empty */ | timing_spec_list_opt timing_spec ; timing_spec : '(' K_DELAY deltype_list ')' | '(' K_DELAY error ')' { vpi_printf("%s:%d: Syntax error in CELL DELAY SPEC\n", sdf_parse_path, @2.first_line); } | '(' K_TIMINGCHECK tchk_def_list ')' | '(' K_TIMINGCHECK error ')' { vpi_printf("%s:%d: Syntax error in TIMINGCHECK SPEC\n", sdf_parse_path, @2.first_line); } ; deltype_list : deltype_list deltype | deltype ; deltype : '(' K_ABSOLUTE del_def_list ')' | '(' K_INCREMENT del_def_list ')' | '(' error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed delay type\n", sdf_parse_path, @1.first_line); } ; del_def_list : del_def_list del_def | del_def ; del_def : '(' K_IOPATH port_spec port_instance delval_list ')' { sdf_iopath_delays($3.vpi_edge, $3.string_val, $4, &$5); free($3.string_val); free($4); } | '(' K_IOPATH error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed IOPATH\n", sdf_parse_path, @2.first_line); } | '(' K_COND conditional_port_expr '(' K_IOPATH port_spec port_instance delval_list ')' ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "COND not supported.\n", sdf_parse_path, @2.first_line); free($6.string_val); free($7); } | '(' K_COND QSTRING conditional_port_expr '(' K_IOPATH port_spec port_instance delval_list ')' ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "COND not supported.\n", sdf_parse_path, @2.first_line); free($3); free($7.string_val); free($8); } | '(' K_COND error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed COND\n", sdf_parse_path, @2.first_line); } | '(' K_CONDELSE '(' K_IOPATH port_spec port_instance delval_list ')' ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "CONDELSE not supported.\n", sdf_parse_path, @2.first_line); free($5.string_val); free($6); } | '(' K_CONDELSE error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed CONDELSE\n", sdf_parse_path, @2.first_line); } /* | '(' K_INTERCONNECT port_instance port_instance delval_list ')' */ | '(' K_INTERCONNECT port_interconnect port_interconnect delval_list ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "INTERCONNECT not supported.\n", sdf_parse_path, @2.first_line); free($3); free($4); } | '(' K_INTERCONNECT error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed INTERCONNECT\n", sdf_parse_path, @2.first_line); } ; tchk_def_list : tchk_def_list tchk_def | tchk_def ; /* Timing checks are ignored. */ tchk_def : '(' K_SETUP port_tchk port_tchk rvalue ')' | '(' K_HOLD port_tchk port_tchk rvalue ')' | '(' K_SETUPHOLD port_tchk port_tchk rvalue rvalue ')' | '(' K_RECOVERY port_tchk port_tchk rvalue ')' | '(' K_RECREM port_tchk port_tchk rvalue rvalue ')' | '(' K_REMOVAL port_tchk port_tchk rvalue ')' | '(' K_WIDTH port_tchk rvalue ')' ; port_tchk : port_instance { free($1); } /* This must only be an edge. For now we just accept everything. */ | cond_edge_start port_instance ')' { free($2); } /* These must only be a cond. For now we just accept everything. */ | cond_edge_start timing_check_condition port_spec ')' { free($3.string_val); } | cond_edge_start QSTRING timing_check_condition port_spec ')' { free($2); free($4.string_val); } ; cond_edge_start : '(' { start_edge_id(1); } cond_edge_identifier { stop_edge_id(); } ; cond_edge_identifier : K_POSEDGE | K_NEGEDGE | K_01 | K_10 | K_0Z | K_Z1 | K_1Z | K_Z0 | K_COND ; timing_check_condition : port_interconnect { free($1); } | '~' port_interconnect { free($2); } | '!' port_interconnect { free($2); } | port_interconnect equality_operator scalar_constant { free($1); } ; /* This is not complete! */ conditional_port_expr : port { free($1); } | scalar_constant | '(' conditional_port_expr ')' | conditional_port_expr K_LAND conditional_port_expr | conditional_port_expr K_LOR conditional_port_expr | conditional_port_expr K_EQ conditional_port_expr | conditional_port_expr K_NE conditional_port_expr | conditional_port_expr K_CEQ conditional_port_expr | conditional_port_expr K_CNE conditional_port_expr ; equality_operator : K_EQ | K_NE | K_CEQ | K_CNE ; scalar_constant : K_LOGICAL_ONE | K_LOGICAL_ZERO ; port_spec : port_instance { $$.vpi_edge = vpiNoEdge; $$.string_val = $1; } | port_edge { $$ = $1; } ; port_instance : port { $$ = $1; } ; port : hierarchical_identifier { $$ = $1; } /* | hierarchical_identifier '[' INTEGER ']' */ ; /* Since INTERCONNECT is ignored we can also ignore a vector bit. */ port_interconnect : hierarchical_identifier { $$ = $1; } | hierarchical_identifier '[' INTEGER ']' { $$ = $1;} ; port_edge : '(' {start_edge_id(0);} edge_identifier {stop_edge_id();} port_instance ')' { $$.vpi_edge = $3; $$.string_val = $5; } ; edge_identifier : K_POSEDGE { $$ = vpiPosedge; } | K_NEGEDGE { $$ = vpiNegedge; } | K_01 { $$ = vpiEdge01; } | K_10 { $$ = vpiEdge10; } | K_0Z { $$ = vpiEdge0x; } | K_Z1 { $$ = vpiEdgex1; } | K_1Z { $$ = vpiEdge1x; } | K_Z0 { $$ = vpiEdgex0; } ; delval_list : delval_list delval { int idx; $$.count = $1.count; for (idx = 0 ; idx < $$.count ; idx += 1) $$.val[idx] = $1.val[idx]; // Is this correct? if ($$.count < 12) { $$.val[$$.count] = $2; $$.count += 1; } } | delval { $$.count = 1; $$.val[0] = $1; } ; delval : rvalue { $$ = $1; } | '(' rvalue rvalue ')' { $$ = $2; vpi_printf("%s:%d: SDF WARNING: Pulse rejection limits ignored\n", sdf_parse_path, @3.first_line); } | '(' rvalue rvalue rvalue ')' { $$ = $2; vpi_printf("%s:%d: SDF WARNING: Pulse rejection limits ignored\n", sdf_parse_path, @3.first_line); } ; rvalue : '(' signed_real_number ')' { $$.defined = 1; $$.value = $2; } | '(' rtriple ')' { $$ = $2; } | '(' ')' { $$.defined = 0; $$.value = 0.0; } ; hierarchical_identifier : IDENTIFIER { $$ = $1; } | hierarchical_identifier HCHAR IDENTIFIER { int len = strlen($1) + strlen($3) + 2; char*tmp = realloc($1, len); strcat(tmp, "."); strcat(tmp, $3); free($3); $$ = tmp; } ; rtriple : signed_real_number_opt ':' signed_real_number_opt ':' signed_real_number_opt { switch(sdf_min_typ_max) { case _vpiDelaySelMinimum: $$ = $1; break; case _vpiDelaySelTypical: $$ = $3; break; case _vpiDelaySelMaximum: $$ = $5; break; } /* At least one of the values must be defined. */ if (! ($1.defined || $3.defined || $5.defined)) { vpi_printf("%s:%d: SDF ERROR: rtriple must have at least one " "defined value.\n", sdf_parse_path, @1.first_line); } } ; signed_real_number_opt : /* When missing. */ { $$.value = 0.0; $$.defined = 0; } | signed_real_number { $$.value = $1; $$.defined = 1; } ; signed_real_number : REAL_NUMBER { $$ = $1; } | '+' REAL_NUMBER { $$ = $2; } | '-' REAL_NUMBER { $$ = -$2; } | INTEGER { $$ = $1; } | '+' INTEGER { $$ = $2; } | '-' INTEGER { $$ = -$2; } ; %% void yyerror(const char*msg) { vpi_printf("%s:SDF ERROR: Too many errors: %s\n", sdf_parse_path, msg); } iverilog-10_1/vpi/sdf_parse_priv.h000066400000000000000000000025521265551621300173230ustar00rootroot00000000000000#ifndef IVL_sdf_parse_priv_h #define IVL_sdf_parse_priv_h /* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file is only included by sdf_parse.y and sdf_lexor.lex. It is * used to share declarations between the parse and the lexor. */ struct port_with_edge_s { int vpi_edge; char*string_val; }; /* Path to source for error messages. */ extern const char*sdf_parse_path; /* Hierarchy separator character to use. */ extern char sdf_use_hchar; extern void start_edge_id(unsigned cond); extern void stop_edge_id(void); #endif /* IVL_sdf_parse_priv_h */ iverilog-10_1/vpi/sdf_priv.h000066400000000000000000000033061265551621300161270ustar00rootroot00000000000000#ifndef IVL_sdf_priv_h #define IVL_sdf_priv_h /* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include /* * Invoke the parser to parse the opened SDF file. The fd is the SDF * file already opened and ready for reading. The path is the path to * the file and is only used for error messages. */ extern void sdf_process_file(FILE*fd, const char*path); extern int sdf_flag_warning; extern int sdf_flag_inform; extern int sdf_min_typ_max; /* **** * These functions are called by the parser to process the SDF file as * it is parsed. */ struct sdf_delay_s { int defined; double value; }; struct sdf_delval_list_s { int count; struct sdf_delay_s val[12]; }; extern void sdf_select_instance(const char*celltype, const char*inst); extern void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst, const struct sdf_delval_list_s*delval); #endif /* IVL_sdf_priv_h */ iverilog-10_1/vpi/stringheap.c000066400000000000000000000037521265551621300164570ustar00rootroot00000000000000/* * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include "stringheap.h" # include # include # include # include "ivl_alloc.h" struct stringheap_cell { struct stringheap_cell*next; }; # define PAGE_SIZE 8192 # define STRINGHEAP_SIZE (PAGE_SIZE - sizeof(struct stringheap_cell)) const char*strdup_sh(struct stringheap_s*hp, const char*txt) { char*res; unsigned len = strlen(txt); assert(len < STRINGHEAP_SIZE); if (hp->cell_lst == 0) { hp->cell_lst = malloc(PAGE_SIZE); hp->cell_lst->next = 0; hp->cell_off = 0; } if ((STRINGHEAP_SIZE - hp->cell_off - 1) <= len) { struct stringheap_cell*tmp = malloc(PAGE_SIZE); tmp->next = hp->cell_lst; hp->cell_lst = tmp; hp->cell_off = 0; } res = (char*) (hp->cell_lst + 1); res += hp->cell_off; strcpy(res, txt); hp->cell_off += len + 1; return res; } void string_heap_delete(struct stringheap_s*hp) { struct stringheap_cell *cur, *tmp; for (cur = hp->cell_lst; cur ; cur = tmp) { tmp = cur->next; free((char *)cur); } hp->cell_lst = 0; hp->cell_off = 0; } iverilog-10_1/vpi/stringheap.h000066400000000000000000000022701265551621300164560ustar00rootroot00000000000000#ifndef IVL_stringheap_H #define IVL_stringheap_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ struct stringheap_cell; struct stringheap_s { struct stringheap_cell*cell_lst; unsigned cell_off; }; /* * Allocate the string from the heap. */ const char*strdup_sh(struct stringheap_s*hp, const char*str); void string_heap_delete(struct stringheap_s*hp); #endif /* IVL_stringheap_H */ iverilog-10_1/vpi/sys_clog2.c000066400000000000000000000103211265551621300162050ustar00rootroot00000000000000/* * Copyright (C) 2008-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include "vpi_user.h" #include "sv_vpi_user.h" /* * This routine returns 1 if the argument supports has a numeric value, * otherwise it returns 0. * * This is copied from sys_priv.c. */ static unsigned is_numeric_obj(vpiHandle obj) { unsigned rtn = 0; assert(obj); switch(vpi_get(vpiType, obj)) { case vpiConstant: case vpiParameter: /* These cannot be a string constant. */ if (vpi_get(vpiConstType, obj) != vpiStringConst) rtn = 1; break; /* These can have a valid numeric value. */ case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiMemoryWord: case vpiNet: case vpiPartSelect: case vpiRealVar: case vpiReg: case vpiTimeVar: rtn = 1; break; } return rtn; } /* * Check that the function is called with the correct argument. */ static PLI_INT32 sys_clog2_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, arg; assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); (void)name; /* Parameter is not used. */ /* We must have an argument. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("$clog2 requires one numeric argument.\n"); vpi_control(vpiFinish, 1); return 0; } /* The argument must be numeric. */ arg = vpi_scan(argv); if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("The first argument to $clog2 must be numeric.\n"); vpi_control(vpiFinish, 1); } /* We can have a maximum of one argument. */ if (vpi_scan(argv) != 0) { char msg[64]; unsigned argc; snprintf(msg, sizeof(msg), "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; argc = 1; while (vpi_scan(argv)) argc += 1; vpi_printf("%s $clog2 takes at most one argument.\n", msg); vpi_printf("%*s Found %u extra argument%s.\n", (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); vpi_control(vpiFinish, 1); } return 0; } static PLI_INT32 sys_clog2_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; s_vpi_vecval vec; (void)name; /* Parameter is not used. */ /* Get the argument. */ arg = vpi_scan(argv); vpi_free_object(argv); vec = vpip_calc_clog2(arg); val.format = vpiVectorVal; val.value.vector = &vec; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Register the function with Verilog. */ void sys_clog2_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.calltf = sys_clog2_calltf; tf_data.compiletf = sys_clog2_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$clog2"; tf_data.user_data = 0; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_convert.c000066400000000000000000000200631265551621300166630ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Michael Ruff (mruff at chiaro.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include static double bits2double(PLI_UINT32 bits[2]) { union conv { double rval; unsigned char bval[sizeof(double)]; } conv; #ifdef WORDS_BIGENDIAN conv.bval[7] = (bits[0] >> 0) & 0xff; conv.bval[6] = (bits[0] >> 8) & 0xff; conv.bval[5] = (bits[0] >> 16) & 0xff; conv.bval[4] = (bits[0] >> 24) & 0xff; conv.bval[3] = (bits[1] >> 0) & 0xff; conv.bval[2] = (bits[1] >> 8) & 0xff; conv.bval[1] = (bits[1] >> 16) & 0xff; conv.bval[0] = (bits[1] >> 24) & 0xff; #else conv.bval[0] = (bits[0] >> 0) & 0xff; conv.bval[1] = (bits[0] >> 8) & 0xff; conv.bval[2] = (bits[0] >> 16) & 0xff; conv.bval[3] = (bits[0] >> 24) & 0xff; conv.bval[4] = (bits[1] >> 0) & 0xff; conv.bval[5] = (bits[1] >> 8) & 0xff; conv.bval[6] = (bits[1] >> 16) & 0xff; conv.bval[7] = (bits[1] >> 24) & 0xff; #endif return conv.rval; } static void double2bits(double real, PLI_UINT32 bits[2]) { union conv { double rval; unsigned char bval[sizeof(double)]; } conv; conv.rval = real; #ifdef WORDS_BIGENDIAN bits[0] = conv.bval[7] | (conv.bval[6] << 8) | (conv.bval[5] <<16) | (conv.bval[4] <<24); bits[1] = conv.bval[3] | (conv.bval[2] << 8) | (conv.bval[1] <<16) | (conv.bval[0] <<24); #else bits[0] = conv.bval[0] | (conv.bval[1] << 8) | (conv.bval[2] <<16) | (conv.bval[3] <<24); bits[1] = conv.bval[4] | (conv.bval[5] << 8) | (conv.bval[6] <<16) | (conv.bval[7] <<24); #endif } static void error_message(vpiHandle callh, const char* msg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf(msg, vpi_get_str(vpiName, callh)); vpi_control(vpiFinish, 1); } static PLI_INT32 sizetf_32 (PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 32; } static PLI_INT32 sizetf_64 (PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 64; } static PLI_INT32 sys_convert_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there is an argument. */ if (argv == 0) { error_message(callh, "%s requires one argument.\n"); return 0; } /* In Icarus if we have an argv we have at least one argument. */ arg = vpi_scan(argv); /* Validate the argument. Only $bitstoreal for now. */ if (!strcmp("$bitstoreal", name) && vpi_get(vpiSize, arg) != 64) { error_message(callh, "%s requires a 64-bit argument.\n"); return 0; } /* Save the argument away to make the calltf faster. */ vpi_put_userdata(callh, (void *) arg); /* These functions only take one argument. */ check_for_extra_args(argv, callh, name, "one argument", 0); return 0; } static PLI_INT32 sys_bitstoreal_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle arg = (vpiHandle) vpi_get_userdata(callh); s_vpi_value value; PLI_UINT32 bits[2]; (void)name; /* Parameter is not used. */ /* get value */ value.format = vpiVectorVal; vpi_get_value(arg, &value); /* convert */ bits[0] = (value.value.vector[0]).aval; bits[1] = (value.value.vector[1]).aval; value.value.real = bits2double(bits); value.format = vpiRealVal; /* return converted value */ vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_itor_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle arg = (vpiHandle) vpi_get_userdata(callh); s_vpi_value value; (void)name; /* Parameter is not used. */ /* get value */ value.format = vpiIntVal; vpi_get_value(arg, &value); /* convert */ value.value.real = value.value.integer; value.format = vpiRealVal; /* return converted value */ vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_realtobits_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle arg = (vpiHandle) vpi_get_userdata(callh); s_vpi_value value; static struct t_vpi_vecval res[2]; PLI_UINT32 bits[2]; (void)name; /* Parameter is not used. */ /* get value */ value.format = vpiRealVal; vpi_get_value(arg, &value); /* convert */ double2bits(value.value.real, bits); res[0].aval = bits[0]; res[0].bval = 0; res[1].aval = bits[1]; res[1].bval = 0; value.format = vpiVectorVal; value.value.vector = res; /* return converted value */ vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_rtoi_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle arg = (vpiHandle) vpi_get_userdata(callh); s_vpi_value value; static struct t_vpi_vecval res; double val; (void)name; /* Parameter is not used. */ /* get value */ value.format = vpiRealVal; vpi_get_value(arg, &value); /* If the value is NaN or +/- infinity then return 'bx */ val = value.value.real; if (val != val || (val && (val == 0.5*val))) { res.aval = ~(PLI_INT32)0; res.bval = ~(PLI_INT32)0; } else { /* This is not 100% correct since large real values may break this * code. See the verinum code for a more rigorous implementation. */ if (val >= 0.0) res.aval = (PLI_UINT64) val; else res.aval = - (PLI_UINT64) -val; res.bval = 0; } value.format = vpiVectorVal; value.value.vector = &res; /* return converted value */ vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } void sys_convert_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.user_data = "$bitstoreal"; tf_data.tfname = tf_data.user_data; tf_data.sizetf = sizetf_64; tf_data.compiletf = sys_convert_compiletf; tf_data.calltf = sys_bitstoreal_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.user_data = "$itor"; tf_data.tfname = tf_data.user_data; tf_data.sizetf = sizetf_64; tf_data.compiletf = sys_convert_compiletf; tf_data.calltf = sys_itor_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.user_data = "$realtobits"; tf_data.tfname = tf_data.user_data; tf_data.sizetf = sizetf_64; tf_data.compiletf = sys_convert_compiletf; tf_data.calltf = sys_realtobits_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.user_data = "$rtoi"; tf_data.tfname = tf_data.user_data; tf_data.sizetf = sizetf_32; tf_data.compiletf = sys_convert_compiletf; tf_data.calltf = sys_rtoi_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_countdrivers.c000066400000000000000000000146661265551621300177460ustar00rootroot00000000000000/* * Copyright (C) 2012-2014 Martin Whitaker. (icarus@martin-whitaker.me.uk) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "sys_priv.h" #include #include #include #include "ivl_alloc.h" /* * Check to see if an argument is a single bit net. */ static void check_net_arg(vpiHandle arg, vpiHandle callh, const char *name) { assert(arg); switch (vpi_get(vpiType, arg)) { case vpiPartSelect: if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) != vpiNet) break; case vpiNet: if (vpi_get(vpiSize, arg) != 1) break; return; default: break; } vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a scalar net or " "a bit-select of a vector net.\n", name); vpi_control(vpiFinish, 1); } /* * Check to see if an argument is a variable. */ static void check_var_arg(vpiHandle arg, vpiHandle callh, const char *name, const char *arg_name) { assert(arg); switch (vpi_get(vpiType, arg)) { case vpiPartSelect: if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) == vpiNet) break; case vpiMemoryWord: case vpiBitVar: case vpiReg: case vpiIntegerVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: return; default: break; } vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's %s argument must be a variable.\n", name, arg_name); vpi_control(vpiFinish, 1); } /* * Check that the given $countdrivers() call has valid arguments. */ static PLI_INT32 sys_countdrivers_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; unsigned arg_num; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least one argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a scalar net or a net bit select. */ arg = vpi_scan(argv); check_net_arg(arg, callh, name); /* The optional arguments must be variables. */ for (arg_num = 2; arg_num < 7; arg_num += 1) { const char *arg_name = NULL; switch (arg_num) { case 2: arg_name = "second"; break; case 3: arg_name = "third"; break; case 4: arg_name = "fourth"; break; case 5: arg_name = "fifth"; break; case 6: arg_name = "sixth"; break; default: assert(0); } arg = vpi_scan(argv); if (arg == 0) return 0; check_var_arg(arg, callh, name, arg_name); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "six arguments", 0); return 0; } /* * The runtime code for $countdrivers(). */ static PLI_INT32 sys_countdrivers_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; unsigned idx; unsigned counts[4]; unsigned num_drivers; s_vpi_value val; (void)name; /* Parameter is not used. */ /* All returned values are integers. */ val.format = vpiIntVal; /* Get the base net reference and bit select */ idx = 0; arg = vpi_scan(argv); assert(arg); if (vpi_get(vpiType, arg) == vpiPartSelect) { idx = vpi_get(vpiLeftRange, arg); arg = vpi_handle(vpiParent, arg); assert(arg); } /* Get the net driver counts from the runtime. */ vpip_count_drivers(arg, idx, counts); num_drivers = counts[0] + counts[1] + counts[2]; /* Handle optional net_is_forced argument. */ arg = vpi_scan(argv); if (arg == 0) goto args_done; val.value.integer = counts[3]; vpi_put_value(arg, &val, 0, vpiNoDelay); /* Handle optional number_of_01x_drivers argument. */ arg = vpi_scan(argv); if (arg == 0) goto args_done; val.value.integer = num_drivers; vpi_put_value(arg, &val, 0, vpiNoDelay); /* Handle optional number_of_0_drivers argument. */ arg = vpi_scan(argv); if (arg == 0) goto args_done; val.value.integer = counts[0]; vpi_put_value(arg, &val, 0, vpiNoDelay); /* Handle optional number_of_1_drivers argument. */ arg = vpi_scan(argv); if (arg == 0) goto args_done; val.value.integer = counts[1]; vpi_put_value(arg, &val, 0, vpiNoDelay); /* Handle optional number_of_x_drivers argument. */ arg = vpi_scan(argv); if (arg == 0) goto args_done; val.value.integer = counts[2]; vpi_put_value(arg, &val, 0, vpiNoDelay); /* Free the argument iterator. */ vpi_free_object(argv); args_done: val.value.integer = (num_drivers > 1) ? 1 : 0; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Routine to register the system tasks/functions provided in this file. */ void sys_countdrivers_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.tfname = "$countdrivers"; tf_data.calltf = sys_countdrivers_calltf; tf_data.compiletf = sys_countdrivers_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$countdrivers"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_darray.c000066400000000000000000000337671265551621300165040ustar00rootroot00000000000000/* * Copyright (c) 2012-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include # include static PLI_INT32 dobject_size_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, arg; argv = vpi_iterate(vpiArgument, callh); if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a dynamic array, queue or string " "argument.\n", name); vpi_control(vpiFinish, 1); return 0; } arg = vpi_scan(argv); /* This should never be zero. */ assert(arg); /* The argument must be a dynamic array, queue or string. */ switch (vpi_get(vpiType, arg)) { case vpiStringVar: break; case vpiArrayVar: switch(vpi_get(vpiArrayType, arg)) { case vpiDynamicArray: case vpiQueueArray: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s argument must be a dynamic array, queue or " "string.\n", name); vpi_control(vpiFinish, 1); } break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s argument must be a dynamic array, queue or string, " "given a %s.\n", name, vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); } arg = vpi_scan(argv); if (arg != 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s has too many arguments.\n", name); vpi_control(vpiFinish, 1); vpi_free_object(argv); } return 0; } static PLI_INT32 dobject_size_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg = vpi_scan(argv); (void)name; /* Parameter is not used. */ vpi_free_object(argv); s_vpi_value value; value.format = vpiIntVal; value.value.integer = vpi_get(vpiSize, arg); vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } static PLI_INT32 to_from_vec_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, arg; argv = vpi_iterate(vpiArgument, callh); if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a dynamic array. */ arg = vpi_scan(argv); /* This should never be zero. */ assert(arg); if (vpi_get(vpiType, arg) != vpiArrayVar) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s first argument must be a dynamic array, " "given a %s.\n", name, vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); } if (vpi_get(vpiArrayType, arg) != vpiDynamicArray) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s first argument must be a dynamic array.\n", name); vpi_control(vpiFinish, 1); } // HERE: Still need to verify that this is not a real or string array. // That will require adding TypeSpec support to the VPI. /* The second argument must be a net, reg or bit variable. */ arg = vpi_scan(argv); if (arg == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second argument.\n", name); vpi_control(vpiFinish, 1); } switch(vpi_get(vpiType, arg)) { case vpiNet: case vpiReg: case vpiBitVar: case vpiIntegerVar: case vpiConstant: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s second argument must be a logical net or " "variable.\n", name); vpi_control(vpiFinish, 1); } arg = vpi_scan(argv); if (arg != 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s has too many arguments.\n", name); vpi_control(vpiFinish, 1); vpi_free_object(argv); } return 0; } static const size_t BPW = 8 * sizeof(PLI_INT32); static const size_t BPWM1 = 8 * sizeof(PLI_INT32) - 1; static PLI_INT32 to_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle darr = vpi_scan(argv); vpiHandle vec = vpi_scan(argv); vpi_free_object(argv); /* Calculate and check the basic array and vector information. */ int darr_size = vpi_get(vpiSize, darr); int darr_word_size = vpi_get(vpiSize, vpi_handle_by_index(darr, 0)); assert(darr_word_size > 0); int darr_bit_size = darr_size * darr_word_size; int vec_size = vpi_get(vpiSize, vec); if (darr_size <= 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot cast an empty dynamic array.\n", name); vpi_control(vpiFinish, 1); return 0; } if (darr_bit_size != vec_size) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s dynamic array and vector size do not match " "(%d != %d).\n", name, darr_bit_size, vec_size); vpi_control(vpiFinish, 1); return 0; } /* Calculate the number of words needed to hold the dynamic array * bits and allocate enough space for them. */ size_t vec_words = (darr_bit_size + BPWM1) / BPW; s_vpi_vecval *vec_val = calloc(vec_words, sizeof(s_vpi_vecval)); s_vpi_vecval *vec_ptr = vec_val; /* The number of words in each array element. */ unsigned darr_words = (darr_word_size + BPWM1) / BPW; /* The offset in bits into the current vector value. */ unsigned offset = 0; /* We want to get each array word as a vector. */ s_vpi_value darr_val; darr_val.format = vpiVectorVal; /* We have to reverse the order of the dynamic array words. */ for (PLI_INT32 i = darr_size - 1; i >= 0; --i) { /* Get the vector value for the current array word. */ vpiHandle darr_word = vpi_handle_by_index(darr, i); vpi_get_value(darr_word, &darr_val); assert(darr_val.format == vpiVectorVal); /* The number of bits to copy for this array word. */ unsigned bits_to_copy = (unsigned)darr_word_size; /* Copy the current array bits to the vector and update the * the vector pointer accordingly. */ for (unsigned j = 0; j < darr_words; ++j) { /* Get the current array part and copy it into the * correct place. */ PLI_UINT32 aval = darr_val.value.vector->aval; PLI_UINT32 bval = darr_val.value.vector->bval; assert(offset < BPW); vec_ptr->aval |= (aval << offset); vec_ptr->bval |= (bval << offset); /* Calculate the new offset into the vector. */ offset += (bits_to_copy > BPW) ? BPW : bits_to_copy; /* If the new offset is past the end of the vector part * then the next vector part also needs to be used. */ if (offset >= BPW) { ++vec_ptr; /* Does the current array part also go into the * next vector part? */ if (offset > BPW) { /* This code has not been tested since the * current implementation only supports dynamic * array elements of size 8, 16, 32 or 64 bits * so currently this code is never run. For * now assert since it has not been checked. */ assert(0); /* Copy the rest of the array part that did not * fit in the previous vector part to the next * vector part. */ offset -= BPW; vec_ptr->aval |= (aval >> (darr_word_size - offset)); vec_ptr->bval |= (bval >> (darr_word_size - offset)); /* Start at the beginning of the next vector part. */ } else { offset = 0; } } /* Advance to the next part of the array. */ bits_to_copy -= BPW; darr_val.value.vector++; } } /* Put the result to the vector and free the allocated space. */ darr_val.format = vpiVectorVal; darr_val.value.vector = vec_val; vpi_put_value(vec, &darr_val, 0, vpiNoDelay); free(vec_val); return 0; } static PLI_INT32 from_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle darr = vpi_scan(argv); vpiHandle vec = vpi_scan(argv); vpi_free_object(argv); /* Calculate and check the basic array and vector information. */ int darr_size = vpi_get(vpiSize, darr); int darr_word_size = vpi_get(vpiSize, vpi_handle_by_index(darr, 0)); assert(darr_word_size > 0); int darr_bit_size = darr_size * darr_word_size; int vec_size = vpi_get(vpiSize, vec); if (vec_size <= 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot cast an empty vector array.\n", name); vpi_control(vpiFinish, 1); return 0; } if (darr_bit_size != vec_size) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s dynamic array and vector size do not match " "(%d != %d).\n", name, darr_bit_size, vec_size); vpi_control(vpiFinish, 1); return 0; } /* Calculate the number of words needed to hold the dynamic array * word bits and allocate enough space for them. */ size_t darr_words = (darr_word_size + BPWM1) / BPW; s_vpi_vecval *darr_val = calloc(darr_words, sizeof(s_vpi_vecval)); /* Get the vector value. */ s_vpi_value vec_val; vec_val.format = vpiVectorVal; vpi_get_value(vec, &vec_val); /* The offset in bits into the vector value. */ unsigned offset = 0; /* We have to reverse the order of the dynamic array words. */ for (int i = darr_size - 1; i >= 0; --i) { unsigned bits_to_copy = darr_word_size; s_vpi_vecval *darr_ptr = darr_val; /* Copy some of the vector bits to the current array word. */ while (bits_to_copy > 0) { unsigned copied_bits = (bits_to_copy > BPW) ? BPW : bits_to_copy; /* Start with the current vector part. */ PLI_UINT32 aval = vec_val.value.vector[offset / BPW].aval; PLI_UINT32 bval = vec_val.value.vector[offset / BPW].bval; /* If this isn't aligned then we may need to get bits * from the next part as well. */ unsigned rem_bits = offset % BPW; if (rem_bits) { aval >>= rem_bits; aval |= vec_val.value.vector[offset / BPW + 1].aval << (BPW - rem_bits); bval >>= rem_bits; bval |= vec_val.value.vector[offset / BPW + 1].bval << (BPW - rem_bits); } /* Advance to the next part of the array and vector. */ darr_ptr->aval = aval; darr_ptr->bval = bval; darr_ptr++; offset += copied_bits; bits_to_copy -= copied_bits; } /* Put part of the vector to the current dynamic array word. */ s_vpi_value result; result.format = vpiVectorVal; result.value.vector = darr_val; vpiHandle darr_word = vpi_handle_by_index(darr, i); vpi_put_value(darr_word, &result, 0, vpiNoDelay); } free(darr_val); return 0; } void sys_darray_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$size"; tf_data.calltf = dobject_size_calltf; tf_data.compiletf = dobject_size_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$size"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.sysfunctype = 0; tf_data.tfname = "$ivl_darray_method$to_vec"; tf_data.calltf = to_vec_calltf; tf_data.compiletf = to_from_vec_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ivl_darray_method$to_vec"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.sysfunctype = 0; tf_data.tfname = "$ivl_darray_method$from_vec"; tf_data.calltf = from_vec_calltf; tf_data.compiletf = to_from_vec_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ivl_darray_method$from_vec"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_deposit.c000066400000000000000000000066341265551621300166620ustar00rootroot00000000000000/* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * Copyright (c) 2000 Stephan Boettcher * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include static PLI_INT32 sys_deposit_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle target, value; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that there are at least two arguments. */ target = vpi_scan(argv); /* This should never be zero. */ value = vpi_scan(argv); if (value == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } assert(target); /* Check the targets type. It must be a net or a register. */ switch (vpi_get(vpiType, target)) { case vpiNet: case vpiReg: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid target type (%s) for %s.\n", vpi_get_str(vpiType, target), name); vpi_control(vpiFinish, 1); } /* Check that there is at most two arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } static PLI_INT32 sys_deposit_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, target, value; s_vpi_value val; (void)name; /* Parameter is not used. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); target = vpi_scan(argv); assert(target); value = vpi_scan(argv); assert(value); val.format = vpiIntVal; vpi_get_value(value, &val); vpi_put_value(target, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } void sys_deposit_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysTask; tf_data.tfname = "$deposit"; tf_data.calltf = sys_deposit_calltf; tf_data.compiletf = sys_deposit_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$deposit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_display.c000066400000000000000000002413211265551621300166520ustar00rootroot00000000000000/* * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include # include # include # include # include # include "ivl_alloc.h" #define IS_MCD(mcd) !((mcd)>>31&1) // Flag to enable better compatibility with other simulators static unsigned compatible_flag = 0; static void check_command_line_args(void) { struct t_vpi_vlog_info vlog_info; vpi_get_vlog_info(&vlog_info); for (int idx = 0 ; idx < vlog_info.argc ; idx += 1) { if (strcmp(vlog_info.argv[idx],"-compatible") == 0) { compatible_flag = 1; } } } /* Printf wrapper to handle both MCD/FD */ static PLI_INT32 my_mcd_printf(PLI_UINT32 mcd, const char *fmt, ...) { int r = 0; va_list ap; va_start(ap, fmt); if (IS_MCD(mcd)) { r = vpi_mcd_vprintf(mcd, fmt, ap); } else { FILE *fp = vpi_get_file(mcd); if (fp) r = vfprintf(fp, fmt, ap); } va_end(ap); return r; } static void my_mcd_rawwrite(PLI_UINT32 mcd, const char*buf, size_t count) { if (IS_MCD(mcd)) { vpip_mcd_rawwrite(mcd, buf, count); } else { FILE*fp = vpi_get_file(mcd); if (fp) { while (count > 0) { size_t rc = fwrite(buf, 1, count, fp); if (rc == 0) break; count -= rc; buf += rc; } } } } struct timeformat_info_s timeformat_info = { 0, 0, 0, 20 }; struct strobe_cb_info { const char*name; char*filename; int lineno; int default_format; vpiHandle scope; vpiHandle*items; unsigned nitems; unsigned fd_mcd; }; /* * The number of decimal digits needed to represent a * nr_bits binary number is floor(nr_bits*log_10(2))+1, * where log_10(2) = 0.30102999566398.... and I approximate * this transcendental number as 146/485, to avoid the vagaries * of floating-point. The smallest nr_bits for which this * approximation fails is 2621, * 2621*log_10(2)=789.9996, but (2621*146+484)/485=790 (exactly). * In cases like this, all that happens is we allocate one * unneeded char for the output. I add a "L" suffix to 146 * to make sure the computation is done as long ints, otherwise * on a 16-bit int machine (allowed by ISO C) we would mangle * this computation for bit-length of 224. I'd like to put * in a test for nr_bits < LONG_MAX/146, but don't know how * to fail, other than crashing. * * In an April 2000 thread in comp.unix.programmer, with subject * "integer -> string", I give the 28/93 * approximation, but overstate its accuracy: that version first * fails when the number of bits is 289, not 671. * * This result does not include space for a trailing '\0', if any. */ __inline__ static int calc_dec_size(int nr_bits, int is_signed) { int r; if (is_signed) --nr_bits; r = (nr_bits * 146L + 484) / 485; if (is_signed) ++r; return r; } static int vpi_get_dec_size(vpiHandle item) { return calc_dec_size( vpi_get(vpiSize, item), vpi_get(vpiSigned, item)==1 ); } static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv) { if (argv) { vpiHandle item; unsigned nitems = 1; vpiHandle*items = malloc(sizeof(vpiHandle)); items[0] = vpi_scan(argv); if (items[0] == 0) { free(items); info->nitems = 0; info->items = 0; return; } for (item = vpi_scan(argv) ; item ; item = vpi_scan(argv)) { items = realloc(items, (nitems+1)*sizeof(vpiHandle)); items[nitems] = item; nitems += 1; } info->nitems = nitems; info->items = items; } else { info->nitems = 0; info->items = 0; } } static int get_default_format(const char *name) { int default_format; switch(name[ strlen(name)-1 ]){ /* writE/strobE or monitoR or displaY/fdisplaY or sformaT */ case 'e': case 'r': case 't': case 'y': default_format = vpiDecStrVal; break; case 'h': default_format = vpiHexStrVal; break; case 'o': default_format = vpiOctStrVal; break; case 'b': default_format = vpiBinStrVal; break; default: default_format = -1; assert(0); } return default_format; } /* Build the format using the variables that control how the item will * be printed. This is used in error messages and directly by the e/f/g * format codes (minus the enclosing <>). The user needs to free the * returned string. */ static char * format_as_string(int ljust, int plus, int ld_zero, int width, int prec, char fmt) { char buf[256]; unsigned int size = 0; /* Do not remove/change the "<" without also changing the e/f/g format * code below! */ buf[size++] = '<'; buf[size++] = '%'; if (ljust == 1) buf[size++] = '-'; if (plus == 1) buf[size++] = '+'; if (ld_zero == 1) buf[size++] = '0'; if (width != -1) size += sprintf(&buf[size], "%d", width); if (prec != -1) size += sprintf(&buf[size], ".%d", prec); if (fmt) buf[size++] = fmt; /* The same goes here ">"! */ buf[size++] = '>'; buf[size] = '\0'; return strdup(buf); } static void get_time(char *rtn, const char *value, int prec, PLI_INT32 time_units) { int shift = time_units - timeformat_info.units; /* Strip any leading zeros, but leave a single zero. */ while (value[0] == '0' && value[1] != '\0') value += 1; /* We need to scale the number up. */ if (shift >= 0) { strcpy(rtn, value); /* Shift only non-zero values. */ while (shift > 0 && value[0] != '0') { strcat(rtn, "0"); shift -= 1; } if (prec > 0) strcat(rtn, "."); while(prec > 0) { strcat(rtn, "0"); prec -= 1; } /* We need to scale the number down. */ } else { int len = strlen(value); int head = len + shift; int tail; /* We have digits to the left of the decimal point. */ if (head > 0) { strncpy(rtn, value, head); *(rtn+head) = '\0'; if (prec > 0) { strcat(rtn, "."); strncat(rtn, &value[head], prec); tail = prec + shift; while (tail > 0) { strcat(rtn, "0"); tail -= 1; } } /* All digits are to the right of the decimal point. */ } else { strcpy(rtn, "0"); if (prec > 0) strcat(rtn, "."); /* Add leading zeros as needed. */ head = -head; if (head > prec) head = prec; while (head > 0) { strcat(rtn, "0"); head -= 1; } /* Add digits from the value if they fit. */ tail = prec + len + shift; if (tail > 0) { strncat(rtn, value, tail); /* Add trailing zeros to fill out the precision. */ tail = prec + shift + 1 - len; while (tail > 0) { strcat(rtn, "0"); tail -= 1; } } } } strcat(rtn, timeformat_info.suff); } static void get_time_real(char *rtn, double value, int prec, PLI_INT32 time_units) { /* Scale the value from its time units to the format time units. */ if (time_units >= timeformat_info.units) { value *= pow(10.0, time_units - timeformat_info.units); } else { value /= pow(10.0, timeformat_info.units - time_units); } sprintf(rtn, "%0.*f%s", prec, value, timeformat_info.suff); } static unsigned int get_format_char(char **rtn, int ljust, int plus, int ld_zero, int width, int prec, char fmt, const struct strobe_cb_info *info, unsigned int *idx) { s_vpi_value value; char *result, *fmtb; unsigned int size; unsigned int ini_size = 512; /* The initial size of the buffer. */ /* Make sure the width fits in the initial buffer. */ assert(width >= -1); if ((unsigned int)(width+1) > ini_size) ini_size = width + 1; /* The default return value is the full format. */ result = malloc(ini_size*sizeof(char)); fmtb = format_as_string(ljust, plus, ld_zero, width, prec, fmt); strcpy(result, fmtb); size = strlen(result) + 1; /* fallback value if errors */ switch (fmt) { case '%': case '\0': if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } strcpy(result, "%"); size = strlen(result) + 1; break; case 'b': case 'B': case 'o': case 'O': case 'h': case 'H': case 'x': case 'X': *idx += 1; if (plus != 0 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { switch (fmt) { case 'b': case 'B': value.format = vpiBinStrVal; break; case 'o': case 'O': value.format = vpiOctStrVal; break; case 'h': case 'H': case 'x': case 'X': value.format = vpiHexStrVal; break; } vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { unsigned swidth = strlen(value.value.str), free_flag = 0;; char *cp = value.value.str; if (ld_zero == 1) { /* Strip the leading zeros if a width is not given. */ if (width == -1) while (*cp == '0' && *(cp+1) != '\0') cp++; /* Pad with leading zeros. */ else if (ljust == 0 && (signed)swidth < width) { unsigned pad = (unsigned)width - swidth; cp = malloc((width+1)*sizeof(char)); memset(cp, '0', pad); strcpy(cp+pad, value.value.str); free_flag = 1; /* For a left aligned value also strip the leading zeros. */ } else if (ljust != 0) while (*cp == '0' && *(cp+1) != '\0') cp++; } /* If a width was not given, use a width of zero. */ if (width == -1) width = 0; /* If the default buffer is too small, make it big enough. */ size = strlen(cp) + 1; if ((signed)size < (width+1)) size = width+1; if (size > ini_size) result = realloc(result, size*sizeof(char)); if (ljust == 0) sprintf(result, "%*s", width, cp); else sprintf(result, "%-*s", width, cp); if (free_flag) free(cp); size = strlen(result) + 1; } } break; case 'c': case 'C': *idx += 1; if (plus != 0 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiStringVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { char ch = value.value.str[strlen(value.value.str)-1]; /* If the default buffer is too small, make it big enough. */ size = width + 1; if (size > ini_size) result = realloc(result, size*sizeof(char)); /* If the width is less than one then use a width of one. */ if (width < 1) width = 1; if (ljust == 0) { if (width > 1) { char *cp = malloc((width+1)*sizeof(char)); memset(cp, (ld_zero == 1 ? '0': ' '), width-1); cp[width-1] = ch; cp[width] = '\0'; sprintf(result, "%*s", width, cp); free(cp); } else sprintf(result, "%c", ch); } else sprintf(result, "%-*c", width, ch); size = strlen(result) + 1; } } break; case 'd': case 'D': *idx += 1; if (prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiDecStrVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { unsigned pad = 0; unsigned swidth = strlen(value.value.str) + (value.value.str[0] == '-' ? 0 : (unsigned)plus); char *tbuf, *cpb, *cp = value.value.str; /* Allocate storage and calculate the pad if needed. */ if (ljust == 0 && ld_zero == 1 && (signed)swidth < width) { tbuf = malloc((width+1)*sizeof(char)); pad = (unsigned)width - swidth; } else { tbuf = malloc((swidth+1)*sizeof(char)); } cpb = tbuf; /* Insert the sign if needed. */ if (plus == 1 && *cp != '-') { *cpb = '+'; cpb += 1; } else if (*cp == '-') { *cpb = '-'; cpb += 1; cp += 1; } /* Now add padding if it is needed and then add the value. */ memset(cpb, '0', pad); strcpy(cpb+pad, cp); /* If a width was not given, use the default, unless we have a * leading zero (width of zero). Because the width of a real in * Icarus is 1 the string length will set the width of a real * displayed using %d. */ if (width == -1) { width = (ld_zero == 1) ? 0 : vpi_get_dec_size(info->items[*idx]); } /* If the default buffer is too small make it big enough. */ size = strlen(tbuf) + 1; if ((signed)size < (width+1)) size = width+1; if (size > ini_size) result = realloc(result, size*sizeof(char)); if (ljust == 0) sprintf(result, "%*s", width, tbuf); else sprintf(result, "%-*s", width, tbuf); free(tbuf); size = strlen(result) + 1; } } break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': *idx += 1; if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiRealVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { char *cp = fmtb; if (fmt == 'F') { while (*cp != 'F') cp++; *cp = 'f'; } while (*cp != '>') cp++; *cp = '\0'; /* If the default buffer is too small make it big enough. * * This should always give enough space. The maximum double * is approximately 1.8*10^308 this means we could need 310 * characters plus the precision. We'll use 320 to give some * extra buffer space. The initial buffers size should work * for most cases, but to be safe we add the precision to * the maximum size (think %6.300f when passed 1.2*10^308). */ size = width + 1; if (size < 320) size = 320; size += prec; if (size > ini_size) result = realloc(result, size*sizeof(char)); #if !defined(__GNUC__) if (isnan(value.value.real)) sprintf(result, "%s", "nan"); else sprintf(result, fmtb+1, value.value.real); #else sprintf(result, fmtb+1, value.value.real); #endif size = strlen(result) + 1; } } break; /* This Verilog format specifier is not currently supported! * vpiCell and vpiLibrary need to be implemented first. */ case 'l': case 'L': vpi_printf("WARNING: %s:%d: %%%c currently unsupported %s%s.\n", info->filename, info->lineno, fmt, info->name, fmtb); break; case 'm': case 'M': if (plus != 0 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } /* If a width was not given, use a width of zero. */ if (width == -1) width = 0; { char *cp = vpi_get_str(vpiFullName, info->scope); /* If the default buffer is too small, make it big enough. */ size = strlen(cp) + 1; if ((signed)size < (width+1)) size = width+1; if (size > ini_size) result = realloc(result, size*sizeof(char)); if (ljust == 0) sprintf(result, "%*s", width, cp); else sprintf(result, "%-*s", width, cp); } size = strlen(result) + 1; break; case 's': case 'S': /* Strings are not numeric and are not zero filled, so %08s => %8s. */ *idx += 1; if (plus != 0 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiStringVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { if (width == -1) { /* If all we have is a leading zero then we want a zero width. */ if (ld_zero == 1) width = 0; /* Otherwise if a width was not given, use the value width. */ else width = (vpi_get(vpiSize, info->items[*idx])+7) / 8; } /* If the default buffer is too small make it big enough. */ size = strlen(value.value.str) + 1; if ((signed)size < (width+1)) size = width+1; if (size > ini_size) result = realloc(result, size*sizeof(char)); if (ljust == 0) sprintf(result, "%*s", width, value.value.str); else sprintf(result, "%-*s", width, value.value.str); size = strlen(result) + 1; } } break; case 't': case 'T': *idx += 1; if (plus != 0) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { PLI_INT32 type; /* Get the argument type and value. */ type = vpi_get(vpiType, info->items[*idx]); if (((type == vpiConstant || type == vpiParameter) && vpi_get(vpiConstType, info->items[*idx]) == vpiRealConst) || type == vpiRealVar || (type == vpiSysFuncCall && vpi_get(vpiFuncType, info->items[*idx]) == vpiRealFunc)) { value.format = vpiRealVal; } else { value.format = vpiDecStrVal; } vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { char *tbuf; PLI_INT32 time_units = vpi_get(vpiTimeUnit, info->scope); unsigned swidth, free_flag = 0; unsigned suff_len = strlen(timeformat_info.suff); char *cp; /* The 512 (513-1 for EOL) is more than enough for any double * value (309 digits plus a decimal point maximum). Because of * scaling this could be larger. For decimal values you can * have an arbitrary value so you can overflow the buffer, but * for now we will assume the user will use this as intended * (pass a time variable or the result of a time function). */ tbuf = malloc((513+suff_len)*sizeof(char)); if (prec == -1) prec = timeformat_info.prec; if (value.format == vpiRealVal) { get_time_real(tbuf, value.value.real, prec, time_units); } else { get_time(tbuf, value.value.str, prec, time_units); } cp = tbuf; swidth = strlen(tbuf); if (ld_zero == 1) { /* No leading zeros are created by this conversion so just make * the width 0 for this case. */ if (width == -1) width = 0; /* Pad with leading zeros. */ else if (ljust == 0 && (signed)swidth < width) { unsigned pad = (unsigned)width - swidth; cp = malloc((width+1)*sizeof(char)); memset(cp, '0', pad); strcpy(cp+pad, tbuf); free_flag = 1; } } if (width == -1) width = timeformat_info.width; /* If the default buffer is too small make it big enough. */ size = strlen(tbuf) + 1; if ((signed)size < (width+1)) size = width+1; if (size > ini_size) result = realloc(result, size*sizeof(char)); if (ljust == 0) sprintf(result, "%*s", width, cp); else sprintf(result, "%-*s", width, cp); if (free_flag) free(cp); free(tbuf); size = strlen(result) + 1; } } break; case 'u': case 'U': *idx += 1; if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiVectorVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { PLI_INT32 veclen, word, byte; char *cp; veclen = (vpi_get(vpiSize, info->items[*idx])+31)/32; size = veclen * 4 + 1; /* If the default buffer is too small, make it big enough. */ if (size > ini_size) result = realloc(result, size*sizeof(char)); cp = result; for (word = 0; word < veclen; word += 1) { PLI_INT32 bits = value.value.vector[word].aval & ~value.value.vector[word].bval; #ifdef WORDS_BIGENDIAN for (byte = 3; byte >= 0; byte -= 1) { #else for (byte = 0; byte <= 3; byte += 1) { #endif *cp = (bits >> byte*8) & 0xff; cp += 1; } } *cp = '\0'; } } /* size is defined above! We can't use strlen here since this can * be a binary string (can contain NULLs). */ break; case 'v': case 'V': *idx += 1; if (plus != 0 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiStrengthVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { char tbuf[4], *rbuf; PLI_INT32 nbits; int bit; /* If a width was not given use a width of zero. */ if (width == -1) width = 0; nbits = vpi_get(vpiSize, info->items[*idx]); /* This is 4 chars for all but the last bit (strength + "_") * which only needs three chars (strength), but then you need * space for the EOS '\0', so it is just number of bits * 4. */ size = nbits*4; rbuf = malloc(size*sizeof(char)); if ((signed)size < (width+1)) size = width+1; if (size > ini_size) result = realloc(result, size*sizeof(char)); strcpy(rbuf, ""); for (bit = nbits-1; bit >= 0; bit -= 1) { vpip_format_strength(tbuf, &value, bit); strcat(rbuf, tbuf); if (bit > 0) strcat(rbuf, "_"); } if (ljust == 0) sprintf(result, "%*s", width, rbuf); else sprintf(result, "%-*s", width, rbuf); free(rbuf); size = strlen(result) + 1; } } break; case 'z': case 'Z': *idx += 1; size = strlen(result) + 1; /* fallback value if errors */ if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 || prec != -1) { vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", info->filename, info->lineno, info->name, fmtb); } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { value.format = vpiVectorVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { PLI_INT32 veclen, word, elem, bits, byte; char *cp; veclen = (vpi_get(vpiSize, info->items[*idx])+31)/32; size = 2 * veclen * 4 + 1; /* If the default buffer is too small, make it big enough. */ if (size > ini_size) result = realloc(result, size*sizeof(char)); cp = result; for (word = 0; word < veclen; word += 1) { /* Write the aval followed by the bval in endian order. */ for (elem = 0; elem < 2; elem += 1) { bits = *(&value.value.vector[word].aval+elem); #ifdef WORDS_BIGENDIAN for (byte = 3; byte >= 0; byte -= 1) { #else for (byte = 0; byte <= 3; byte += 1) { #endif *cp = (bits >> byte*8) & 0xff; cp += 1; } } } *cp = '\0'; } } /* size is defined above! We can't use strlen here since this can * be a binary string (can contain NULLs). */ break; default: vpi_printf("WARNING: %s:%d: unknown format %s%s.\n", info->filename, info->lineno, info->name, fmtb); size = strlen(result) + 1; break; } free(fmtb); /* We can't use strdup here since %u and %z can insert NULL * characters into the stream. */ *rtn = malloc(size*sizeof(char)); memcpy(*rtn, result, size); free(result); return size - 1; } /* We can't use the normal str functions on the return value since * %u and %z can insert NULL characters into the stream. */ static unsigned int get_format(char **rtn, char *fmt, const struct strobe_cb_info *info, unsigned int *idx) { char *cp = fmt; unsigned int size; *rtn = strdup(""); size = 1; while (*cp) { size_t cnt = strcspn(cp, "%"); if (cnt > 0) { *rtn = realloc(*rtn, (size+cnt)*sizeof(char)); memcpy(*rtn+size-1, cp, cnt); size += cnt; cp += cnt; } else { int ljust = 0, plus = 0, ld_zero = 0, width = -1, prec = -1; char *result; cp += 1; while ((*cp == '-') || (*cp == '+')) { if (*cp == '-') ljust = 1; else plus = 1; cp += 1; } if (*cp == '0') { ld_zero = 1; cp += 1; } if (isdigit((int)*cp)) width = strtoul(cp, &cp, 10); if (*cp == '.') { cp += 1; prec = strtoul(cp, &cp, 10); } cnt = get_format_char(&result, ljust, plus, ld_zero, width, prec, *cp, info, idx); *rtn = realloc(*rtn, (size+cnt)*sizeof(char)); memcpy(*rtn+size-1, result, cnt); free(result); size += cnt; if (*cp) cp += 1; } } *(*rtn+size-1) = '\0'; return size - 1; } static unsigned int get_numeric(char **rtn, const struct strobe_cb_info *info, vpiHandle item) { int size, min; s_vpi_value val; val.format = info->default_format; vpi_get_value(item, &val); switch(info->default_format){ case vpiDecStrVal: size = vpi_get_dec_size(item); /* -1 can be represented as a one bit signed value. This returns * a size of 1 which is too small for the -1 string value so make * the string width the minimum display width. */ min = strlen(val.value.str); if (size < min) size = min; *rtn = malloc((size+1)*sizeof(char)); sprintf(*rtn, "%*s", size, val.value.str); break; default: *rtn = strdup(val.value.str); } return strlen(*rtn); } /* In many places we can't use the normal str functions since %u and %z * can insert NULL characters into the stream. */ static char *get_display(unsigned int *rtnsz, const struct strobe_cb_info *info) { char *result, *fmt, *rtn, *func_name; const char *cresult; s_vpi_value value; unsigned int idx, size, width; char buf[256]; rtn = strdup(""); size = 1; for (idx = 0; idx < info->nitems; idx += 1) { vpiHandle item = info->items[idx]; switch (vpi_get(vpiType, item)) { case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, item) == vpiStringConst) { value.format = vpiStringVal; vpi_get_value(item, &value); fmt = strdup(value.value.str); width = get_format(&result, fmt, info, &idx); free(fmt); } else if (vpi_get(vpiConstType, item) == vpiRealConst) { value.format = vpiRealVal; vpi_get_value(item, &value); #if !defined(__GNUC__) if(compatible_flag) sprintf(buf, "%g", value.value.real); else { if(value.value.real == 0.0 || value.value.real == -0.0) sprintf(buf, "%.05f", value.value.real); else sprintf(buf, "%#g", value.value.real); } #else sprintf(buf, compatible_flag ? "%g" : "%#g", value.value.real); #endif result = strdup(buf); width = strlen(result); } else { width = get_numeric(&result, info, item); } rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, result, width); free(result); break; case vpiNet: case vpiReg: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiIntegerVar: case vpiMemoryWord: case vpiPartSelect: width = get_numeric(&result, info, item); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, result, width); free(result); break; /* It appears that this is not currently used! A time variable is passed as an integer and processed above. Hence this code has only been visually checked. */ case vpiTimeVar: value.format = vpiDecStrVal; vpi_get_value(item, &value); get_time(buf, value.value.str, timeformat_info.prec, vpi_get(vpiTimeUnit, info->scope)); width = strlen(buf); if (width < timeformat_info.width) width = timeformat_info.width; rtn = realloc(rtn, (size+width)*sizeof(char)); sprintf(rtn+size-1, "%*s", width, buf); break; /* Realtime variables are also processed here. */ case vpiRealVar: value.format = vpiRealVal; vpi_get_value(item, &value); #if !defined(__GNUC__) if (compatible_flag) sprintf(buf, "%g", value.value.real); else { if (value.value.real == 0.0 || value.value.real == -0.0) sprintf(buf, "%.05f", value.value.real); else sprintf(buf, "%#g", value.value.real); } #else sprintf(buf, compatible_flag ? "%g" : "%#g", value.value.real); #endif width = strlen(buf); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, buf, width); break; /* Process string variables like string constants: interpret the contained strings like format strings. */ case vpiStringVar: value.format = vpiStringVal; vpi_get_value(item, &value); fmt = strdup(value.value.str); width = get_format(&result, fmt, info, &idx); free(fmt); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, result, width); free(result); break; case vpiSysFuncCall: func_name = vpi_get_str(vpiName, item); if (strcmp(func_name, "$time") == 0) { value.format = vpiDecStrVal; vpi_get_value(item, &value); width = strlen(value.value.str); if (width < 20) width = 20; rtn = realloc(rtn, (size+width)*sizeof(char)); sprintf(rtn+size-1, "%*s", width, value.value.str); } else if (strcmp(func_name, "$stime") == 0) { value.format = vpiDecStrVal; vpi_get_value(item, &value); width = strlen(value.value.str); if (width < 10) width = 10; rtn = realloc(rtn, (size+width)*sizeof(char)); sprintf(rtn+size-1, "%*s", width, value.value.str); } else if (strcmp(func_name, "$simtime") == 0) { value.format = vpiDecStrVal; vpi_get_value(item, &value); width = strlen(value.value.str); if (width < 20) width = 20; rtn = realloc(rtn, (size+width)*sizeof(char)); sprintf(rtn+size-1, "%*s", width, value.value.str); } else if (strcmp(func_name, "$realtime") == 0) { /* Use the local scope precision. */ int use_prec = vpi_get(vpiTimeUnit, info->scope) - vpi_get(vpiTimePrecision, info->scope); assert(use_prec >= 0); value.format = vpiRealVal; vpi_get_value(item, &value); sprintf(buf, "%.*f", use_prec, value.value.real); width = strlen(buf); rtn = realloc(rtn, (size+width)*sizeof(char)); sprintf(rtn+size-1, "%*s", width, buf); } else { vpi_printf("WARNING: %s:%d: %s does not support %s as an argument!\n", info->filename, info->lineno, info->name, func_name); strcpy(buf, ""); width = strlen(buf); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, buf, width); } break; default: vpi_printf("WARNING: %s:%d: unknown argument type (%s) given to %s!\n", info->filename, info->lineno, vpi_get_str(vpiType, item), info->name); cresult = ""; width = strlen(cresult); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, cresult, width); break; } size += width; } rtn[size-1] = '\0'; *rtnsz = size - 1; return rtn; } #ifdef BR916_STOPGAP_FIX static char br916_hint_issued = 0; #endif static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, int no_auto, int is_monitor) { vpiHandle arg; int ret = 0; /* If there are no arguments, just return. */ if (argv == 0) return ret; for (arg = vpi_scan(argv); arg; arg = vpi_scan(argv)) { if (no_auto && vpi_get(vpiAutomatic, arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s argument \"%s\" is an automatic variable.\n", name, vpi_get_str(vpiName, arg)); ret = 1; } switch (vpi_get(vpiType, arg)) { case vpiMemoryWord: case vpiPartSelect: if (is_monitor && vpi_get(vpiConstantSelect, arg) == 0) { vpi_printf("SORRY: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s must have a constant %s select.\n", name, vpi_get_str(vpiType, arg)); ret = 1; } case vpiConstant: case vpiParameter: case vpiNet: case vpiReg: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: case vpiRealVar: case vpiStringVar: #ifdef BR916_STOPGAP_FIX // no_auto implies either $strobe or $monitor if (no_auto) { switch (vpi_get(_vpiFromThr, arg)) { case _vpiVThr: case _vpiWord: case _vpiString: vpi_printf("SORRY: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("currently only simple signals or constant " "expressions may be passed to %s.\n", name); if (!br916_hint_issued) { vpi_printf("NOTE: You can work around this by " "assigning the desired expression " "to an\n" " intermediate net (using a " "continuous assignment) and passing " "that net\n" " to %s.\n", name); br916_hint_issued = 1; } ret = 1; default: break; } } #endif case vpiSysFuncCall: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s does not support argument type (%s).\n", name, vpi_get_str(vpiType, arg)); ret = 1; break; } } return ret; } /* Common compiletf routine. */ static PLI_INT32 sys_common_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name, int no_auto, int is_monitor) { vpiHandle callh, argv; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); if(name[1] == 'f') { /* Check that there is a fd/mcd and that it is numeric. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least a file descriptor/MCD.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's file descriptor/MCD must be numeric.\n", name); vpi_control(vpiFinish, 1); } } if (sys_check_args(callh, argv, name, no_auto, is_monitor)) { vpi_control(vpiFinish, 1); } return 0; } /* Check the $display, $write, $fdisplay and $fwrite based tasks. */ static PLI_INT32 sys_display_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { /* These tasks can have automatic variables and are not monitor. */ return sys_common_compiletf(name, 0, 0); } /* This implements the $display/$fdisplay and the $write/$fwrite based tasks. */ static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, scope; struct strobe_cb_info info; char* result; unsigned int size; PLI_UINT32 fd_mcd; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); /* Get the file/MC descriptor and verify it is valid. */ if(name[1] == 'f') { errno = 0; vpiHandle arg = vpi_scan(argv); s_vpi_value val; val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* If the MCD is zero we have nothing to do so just return. */ if (fd_mcd == 0) { vpi_free_object(argv); return 0; } if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || ( IS_MCD(fd_mcd) && my_mcd_printf(fd_mcd, "") == EOF)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor/MCD (0x%x) given " "to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; vpi_free_object(argv); return 0; } } else { fd_mcd = 1; } scope = vpi_handle(vpiScope, callh); assert(scope); /* We could use vpi_get_str(vpiName, callh) to get the task name, * but name is already defined. */ info.name = name; info.filename = strdup(vpi_get_str(vpiFile, callh)); info.lineno = (int)vpi_get(vpiLineNo, callh); info.default_format = get_default_format(name); info.scope = scope; array_from_iterator(&info, argv); /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, &info); my_mcd_rawwrite(fd_mcd, result, size); if ((strncmp(name,"$display",8) == 0) || (strncmp(name,"$fdisplay",9) == 0)) my_mcd_rawwrite(fd_mcd, "\n", 1); free(info.filename); free(info.items); free(result); return 0; } /* * The strobe implementation takes the parameter handles that are * passed to the calltf and puts them in to an array for safe * keeping. That array (and other bookkeeping) is passed, via the * struct_cb_info object, to the REadOnlySych function strobe_cb, * where it is used to perform the actual formatting and printing. */ static PLI_INT32 strobe_cb(p_cb_data cb) { struct strobe_cb_info*info = (struct strobe_cb_info*)cb->user_data; /* We really need to cancel any $fstrobe() calls for a file when it * is closed, but for now we will just skip processing the result. * Which has the same basic effect. */ if ((! IS_MCD(info->fd_mcd) && vpi_get_file(info->fd_mcd) != NULL) || ( IS_MCD(info->fd_mcd) && my_mcd_printf(info->fd_mcd, "") != EOF)) { char* result; unsigned int size; /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, info); my_mcd_rawwrite(info->fd_mcd, result, size); my_mcd_rawwrite(info->fd_mcd, "\n", 1); free(result); } free(info->filename); free(info->items); free(info); return 0; } /* Check both the $strobe and $fstrobe based tasks. */ static PLI_INT32 sys_strobe_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { /* These tasks can not have automatic variables and are not monitor. */ return sys_common_compiletf(name, 1, 0); } /* This implements both the $strobe and $fstrobe based tasks. */ static PLI_INT32 sys_strobe_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh, argv, scope; struct t_cb_data cb; struct t_vpi_time timerec; struct strobe_cb_info*info; PLI_UINT32 fd_mcd; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); /* Get the file/MC descriptor and verify it is valid. */ if(name[1] == 'f') { errno = 0; vpiHandle arg = vpi_scan(argv); s_vpi_value val; val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* If the MCD is zero we have nothing to do so just return. */ if (fd_mcd == 0) { vpi_free_object(argv); return 0; } if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || ( IS_MCD(fd_mcd) && my_mcd_printf(fd_mcd, "") == EOF)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor/MCD (0x%x) given " "to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; vpi_free_object(argv); return 0; } } else { fd_mcd = 1; } scope = vpi_handle(vpiScope, callh); assert(scope); info = calloc(1, sizeof(struct strobe_cb_info)); info->fd_mcd = fd_mcd; /* We could use vpi_get_str(vpiName, callh) to get the task name, * but name is already defined. */ info->name = name; info->filename = strdup(vpi_get_str(vpiFile, callh)); info->lineno = (int)vpi_get(vpiLineNo, callh); info->default_format = get_default_format(name); info->scope= scope; array_from_iterator(info, argv); timerec.type = vpiSimTime; timerec.low = 0; timerec.high = 0; cb.reason = cbReadOnlySynch; cb.cb_rtn = strobe_cb; cb.time = &timerec; cb.obj = 0; cb.value = 0; cb.user_data = (char*)info; vpi_register_cb(&cb); return 0; } /* * The $monitor system task works by managing these static variables, * and the cbValueChange callbacks associated with registers and * nets. Note that it is proper to keep the state in static variables * because there can only be one monitor at a time pending (even * though that monitor may be watching many variables). */ static struct strobe_cb_info monitor_info = { 0, 0, 0, 0, 0, 0, 0, 0 }; static vpiHandle *monitor_callbacks = 0; static int monitor_scheduled = 0; static int monitor_enabled = 1; static PLI_INT32 monitor_cb_2(p_cb_data cb) { char* result; unsigned int size; (void)cb; /* Parameter is not used. */ /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, &monitor_info); my_mcd_rawwrite(monitor_info.fd_mcd, result, size); my_mcd_rawwrite(monitor_info.fd_mcd, "\n", 1); monitor_scheduled = 0; free(result); return 0; } /* * The monitor_cb_1 callback is called when an event occurs somewhere * in the simulation. All this function does is schedule the actual * display to occur in a ReadOnlySync callback. The monitor_scheduled * flag is used to allow only one monitor strobe to be scheduled. */ static PLI_INT32 monitor_cb_1(p_cb_data cause) { struct t_cb_data cb; struct t_vpi_time timerec; (void)cause; /* Parameter is not used. */ if (monitor_enabled == 0) return 0; if (monitor_scheduled) return 0; /* This this action caused the first trigger, then schedule the monitor to happen at the end of the time slice and mark it as scheduled. */ monitor_scheduled += 1; timerec.type = vpiSimTime; timerec.low = 0; timerec.high = 0; cb.reason = cbReadOnlySynch; cb.cb_rtn = monitor_cb_2; cb.time = &timerec; cb.obj = 0; cb.value = 0; vpi_register_cb(&cb); return 0; } static PLI_INT32 sys_monitor_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); if (sys_check_args(callh, argv, name, 1, 1)) vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 sys_monitor_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh, argv, scope; unsigned idx; struct t_cb_data cb; struct t_vpi_time timerec; (void)name; /* Parameter is not used. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); /* If there was a previous $monitor, then remove the callbacks related to it. */ if (monitor_callbacks) { for (idx = 0 ; idx < monitor_info.nitems ; idx += 1) if (monitor_callbacks[idx]) vpi_remove_cb(monitor_callbacks[idx]); free(monitor_callbacks); monitor_callbacks = 0; free(monitor_info.filename); free(monitor_info.items); monitor_info.items = 0; monitor_info.nitems = 0; monitor_info.name = 0; } scope = vpi_handle(vpiScope, callh); assert(scope); /* Make an array of handles from the argument list. */ array_from_iterator(&monitor_info, argv); monitor_info.name = name; monitor_info.filename = strdup(vpi_get_str(vpiFile, callh)); monitor_info.lineno = (int)vpi_get(vpiLineNo, callh); monitor_info.default_format = get_default_format(name); monitor_info.scope = scope; monitor_info.fd_mcd = 1; /* Attach callbacks to all the parameters that might change. */ monitor_callbacks = calloc(monitor_info.nitems, sizeof(vpiHandle)); timerec.type = vpiSuppressTime; cb.reason = cbValueChange; cb.cb_rtn = monitor_cb_1; cb.time = &timerec; cb.value = NULL; for (idx = 0 ; idx < monitor_info.nitems ; idx += 1) { switch (vpi_get(vpiType, monitor_info.items[idx])) { case vpiMemoryWord: /* * We only support constant selections. Make this * better when we add a real compiletf routine. */ assert(vpi_get(vpiConstantSelect, monitor_info.items[idx])); case vpiNet: case vpiReg: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiPartSelect: /* Monitoring reg and net values involves setting a callback for value changes. Pass the storage pointer for the callback itself as user_data so that the callback can refresh itself. */ cb.user_data = (char*)(monitor_callbacks+idx); cb.obj = monitor_info.items[idx]; monitor_callbacks[idx] = vpi_register_cb(&cb); break; } } /* When the $monitor is called, it schedules a first display for the end of the current time, like a $strobe. */ monitor_cb_1(0); return 0; } static PLI_INT32 sys_monitoron_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ monitor_enabled = 1; monitor_cb_1(0); return 0; } static PLI_INT32 sys_monitoroff_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ monitor_enabled = 0; return 0; } static PLI_INT32 sys_swrite_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; PLI_INT32 type; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least one argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a register or a SV string. */ arg = vpi_scan(argv); /* This should never be zero. */ type = vpi_get(vpiType, arg); if (type != vpiReg && type != vpiStringVar) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a register or SV string.\n", name); vpi_control(vpiFinish, 1); return 0; } if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 sys_swrite_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, reg, scope; struct strobe_cb_info info; s_vpi_value val; unsigned int size; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); reg = vpi_scan(argv); scope = vpi_handle(vpiScope, callh); assert(scope); /* We could use vpi_get_str(vpiName, callh) to get the task name, but * name is already defined. */ info.name = name; info.filename = strdup(vpi_get_str(vpiFile, callh)); info.lineno = (int)vpi_get(vpiLineNo, callh); info.default_format = get_default_format(name); info.scope = scope; array_from_iterator(&info, argv); /* Because %u and %z may put embedded NULL characters into the returned * string strlen() may not match the real size! */ val.value.str = get_display(&size, &info); val.format = vpiStringVal; vpi_put_value(reg, &val, 0, vpiNoDelay); if (size != strlen(val.value.str)) { vpi_printf("WARNING: %s:%d: %s returned a value with an embedded NULL " "(see %%u/%%z).\n", info.filename, info.lineno, name); } free(val.value.str); free(info.filename); free(info.items); return 0; } static PLI_INT32 sys_sformat_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; PLI_INT32 type; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least two argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a register or a SV string. */ arg = vpi_scan(argv); /* This should never be zero. */ type = vpi_get(vpiType, arg); if (type != vpiReg && type != vpiStringVar) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a register or SV string.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The second argument must be a string, a register or a SV string. */ arg = vpi_scan(argv); if (arg == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least two argument.\n", name); vpi_control(vpiFinish, 1); return 0; } type = vpi_get(vpiType, arg); if (((type != vpiConstant && type != vpiParameter) || vpi_get(vpiConstType, arg) != vpiStringConst) && type != vpiReg && type != vpiStringVar) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be a string or a register.\n", name); vpi_control(vpiFinish, 1); return 0; } if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 sys_sformat_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, reg, scope; struct strobe_cb_info info; s_vpi_value val; char *result, *fmt; unsigned int idx, size; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); reg = vpi_scan(argv); val.format = vpiStringVal; vpi_get_value(vpi_scan(argv), &val); fmt = strdup(val.value.str); scope = vpi_handle(vpiScope, callh); assert(scope); /* We could use vpi_get_str(vpiName, callh) to get the task name, but * name is already defined. */ info.name = name; info.filename = strdup(vpi_get_str(vpiFile, callh)); info.lineno = (int)vpi_get(vpiLineNo, callh); info.default_format = get_default_format(name); info.scope = scope; array_from_iterator(&info, argv); idx = -1; size = get_format(&result, fmt, &info, &idx); free(fmt); if (idx+1< info.nitems) { vpi_printf("WARNING: %s:%d: %s has %d extra argument(s).\n", info.filename, info.lineno, name, info.nitems-idx-1); } val.value.str = result; val.format = vpiStringVal; vpi_put_value(reg, &val, 0, vpiNoDelay); if (size != strlen(val.value.str)) { vpi_printf("WARNING: %s:%d: %s returned a value with an embedded NULL " "(see %%u/%%z).\n", info.filename, info.lineno, name); } free(val.value.str); free(info.filename); free(info.items); return 0; } static PLI_INT32 sys_end_of_compile(p_cb_data cb_data) { (void)cb_data; /* Parameter is not used. */ /* The default timeformat prints times in unit of simulation precision. */ free(timeformat_info.suff); timeformat_info.suff = strdup(""); timeformat_info.units = vpi_get(vpiTimePrecision, 0); timeformat_info.prec = 0; timeformat_info.width = 20; return 0; } static PLI_INT32 sys_timeformat_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); if (argv) { vpiHandle arg; /* Check that the unit argument is numeric. */ if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's units argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Check that the precision argument is given and is numeric. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires zero or four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's precision argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Check that the suffix argument is given and is a string. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires zero or four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's suffix argument must be a string.\n", name); vpi_control(vpiFinish, 1); } /* Check that the min. width argument is given and is numeric. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires zero or four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's minimum width argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "four arguments", 0); } return 0; } static PLI_INT32 sys_timeformat_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_value value; vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); (void)name; /* Parameter is not used. */ if (argv) { vpiHandle units = vpi_scan(argv); vpiHandle prec = vpi_scan(argv); vpiHandle suff = vpi_scan(argv); vpiHandle wid = vpi_scan(argv); vpi_free_object(argv); value.format = vpiIntVal; vpi_get_value(units, &value); timeformat_info.units = value.value.integer; value.format = vpiIntVal; vpi_get_value(prec, &value); timeformat_info.prec = value.value.integer; value.format = vpiStringVal; vpi_get_value(suff, &value); free(timeformat_info.suff); timeformat_info.suff = strdup(value.value.str); value.format = vpiIntVal; vpi_get_value(wid, &value); timeformat_info.width = value.value.integer; } else { /* If no arguments are given then use the default values. */ sys_end_of_compile(NULL); } return 0; } static const char *pts_convert(int value) { const char *string; switch (value) { case 0: string = "1s"; break; case -1: string = "100ms"; break; case -2: string = "10ms"; break; case -3: string = "1ms"; break; case -4: string = "100us"; break; case -5: string = "10us"; break; case -6: string = "1us"; break; case -7: string = "100ns"; break; case -8: string = "10ns"; break; case -9: string = "1ns"; break; case -10: string = "100ps"; break; case -11: string = "10ps"; break; case -12: string = "1ps"; break; case -13: string = "100fs"; break; case -14: string = "10fs"; break; case -15: string = "1fs"; break; default: string = "invalid"; assert(0); } return string; } static PLI_INT32 sys_printtimescale_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); if (argv) { vpiHandle arg = vpi_scan(argv); switch (vpi_get(vpiType, arg)) { case vpiFunction: case vpiGenScope: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiMemory: case vpiMemoryWord: case vpiModule: case vpiNamedBegin: case vpiNamedEvent: case vpiNamedFork: case vpiNet: case vpiNetArray: // case vpiNetBit: // Unused and unavailable in Icarus case vpiParameter: case vpiPartSelect: case vpiRealVar: case vpiReg: // case vpiRegBit: // Unused and unavailable in Icarus case vpiTask: case vpiTimeVar: // Unused in Icarus break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's argument must have a module, given a %s.\n", name, vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "one argument", 1); } return 0; } static PLI_INT32 sys_printtimescale_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle item, scope; (void)name; /* Parameter is not used. */ if (!argv) { item = sys_func_module(callh); } else { item = vpi_scan(argv); vpi_free_object(argv); } if (vpi_get(vpiType, item) != vpiModule) { scope = vpi_handle(vpiModule, item); } else { scope = item; } vpi_printf("Time scale of (%s) is ", vpi_get_str(vpiFullName, item)); vpi_printf("%s / ", pts_convert(vpi_get(vpiTimeUnit, scope))); vpi_printf("%s\n", pts_convert(vpi_get(vpiTimePrecision, scope))); return 0; } static PLI_INT32 sys_fatal_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); if (argv) { vpiHandle arg = vpi_scan(argv); /* Check that finish_number (1st argument) is numeric */ if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's finish number must be numeric.\n", name); vpi_control(vpiFinish, 1); } if (sys_check_args(callh, argv, name, 0, 0)) { vpi_control(vpiFinish, 1); } } return 0; } static PLI_INT32 sys_severity_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle scope; struct strobe_cb_info info; struct t_vpi_time now; PLI_UINT64 now64; char *sstr, *t, *dstr; unsigned int size, location=0; s_vpi_value finish_number; /* Set the default finish number for $fatal. */ finish_number.value.integer = 1; /* Check that the finish number is in range. */ if (strncmp(name,"$fatal", 6) == 0 && argv) { vpiHandle arg = vpi_scan(argv); finish_number.format = vpiIntVal; vpi_get_value(arg, &finish_number); if ((finish_number.value.integer < 0) || (finish_number.value.integer > 2)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("$fatal called with finish_number of %d, " "but it must be 0, 1, or 2.\n", (int)finish_number.value.integer); finish_number.value.integer = 1; } } /* convert name to upper and drop $ to get severity string */ sstr = strdup(name) + 1; for (t=sstr; *t; t+=1) *t = toupper((int)*t); scope = vpi_handle(vpiScope, callh); assert(scope); info.name = name; info.filename = strdup(vpi_get_str(vpiFile, callh)); info.lineno = (int)vpi_get(vpiLineNo, callh); info.default_format = vpiDecStrVal; info.scope = scope; array_from_iterator(&info, argv); vpi_printf("%s: %s:%d: ", sstr, info.filename, info.lineno); dstr = get_display(&size, &info); while (location < size) { if (dstr[location] == '\0') { my_mcd_printf(1, "%c", '\0'); location += 1; } else { my_mcd_printf(1, "%s", &dstr[location]); location += strlen(&dstr[location]); } } now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); vpi_printf("\n%*s Time: %" PLI_UINT64_FMT " Scope: %s\n", (int)strlen(sstr), " ", now64, vpi_get_str(vpiFullName, scope)); free(--sstr); /* Get the $ back. */ free(info.filename); free(info.items); free(dstr); if (strncmp(name,"$fatal",6) == 0) { vpi_control(vpiFinish, finish_number.value.integer); } return 0; } static PLI_INT32 sys_end_of_simulation(p_cb_data cb_data) { (void)cb_data; /* Parameter is not used. */ free(monitor_callbacks); monitor_callbacks = 0; free(monitor_info.filename); free(monitor_info.items); monitor_info.items = 0; monitor_info.nitems = 0; monitor_info.name = 0; free(timeformat_info.suff); timeformat_info.suff = 0; return 0; } void sys_display_register(void) { s_cb_data cb_data; s_vpi_systf_data tf_data; vpiHandle res; check_command_line_args(); /*============================== display */ tf_data.type = vpiSysTask; tf_data.tfname = "$display"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$display"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$displayh"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$displayh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$displayo"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$displayo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$displayb"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$displayb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== write */ tf_data.type = vpiSysTask; tf_data.tfname = "$write"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$write"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$writeh"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writeh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$writeo"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writeo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$writeb"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writeb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== strobe */ tf_data.type = vpiSysTask; tf_data.tfname = "$strobe"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$strobe"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$strobeh"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$strobeh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$strobeo"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$strobeo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$strobeb"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$strobeb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fstrobe */ tf_data.type = vpiSysTask; tf_data.tfname = "$fstrobe"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fstrobe"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fstrobeh"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fstrobeh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fstrobeo"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fstrobeo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fstrobeb"; tf_data.calltf = sys_strobe_calltf; tf_data.compiletf = sys_strobe_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fstrobeb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== monitor */ tf_data.type = vpiSysTask; tf_data.tfname = "$monitor"; tf_data.calltf = sys_monitor_calltf; tf_data.compiletf = sys_monitor_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$monitor"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$monitorh"; tf_data.calltf = sys_monitor_calltf; tf_data.compiletf = sys_monitor_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$monitorh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$monitoro"; tf_data.calltf = sys_monitor_calltf; tf_data.compiletf = sys_monitor_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$monitoro"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$monitorb"; tf_data.calltf = sys_monitor_calltf; tf_data.compiletf = sys_monitor_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$monitorb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$monitoron"; tf_data.calltf = sys_monitoron_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$monitoron"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$monitoroff"; tf_data.calltf = sys_monitoroff_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$monitoroff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fdisplay */ tf_data.type = vpiSysTask; tf_data.tfname = "$fdisplay"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fdisplay"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fdisplayh"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fdisplayh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fdisplayo"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fdisplayo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fdisplayb"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fdisplayb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fwrite */ tf_data.type = vpiSysTask; tf_data.tfname = "$fwrite"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fwrite"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fwriteh"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fwriteh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fwriteo"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fwriteo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$fwriteb"; tf_data.calltf = sys_display_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fwriteb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== swrite */ tf_data.type = vpiSysTask; tf_data.tfname = "$swrite"; tf_data.calltf = sys_swrite_calltf; tf_data.compiletf = sys_swrite_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$swrite"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$swriteh"; tf_data.calltf = sys_swrite_calltf; tf_data.compiletf = sys_swrite_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$swriteh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$swriteo"; tf_data.calltf = sys_swrite_calltf; tf_data.compiletf = sys_swrite_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$swriteo"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$swriteb"; tf_data.calltf = sys_swrite_calltf; tf_data.compiletf = sys_swrite_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$swriteb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$sformat"; tf_data.calltf = sys_sformat_calltf; tf_data.compiletf = sys_sformat_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$sformat"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================ timeformat */ tf_data.type = vpiSysTask; tf_data.tfname = "$timeformat"; tf_data.calltf = sys_timeformat_calltf; tf_data.compiletf = sys_timeformat_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$timeformat"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$printtimescale"; tf_data.calltf = sys_printtimescale_calltf; tf_data.compiletf = sys_printtimescale_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$printtimescale"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================ severity tasks */ tf_data.type = vpiSysTask; tf_data.tfname = "$fatal"; tf_data.calltf = sys_severity_calltf; tf_data.compiletf = sys_fatal_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fatal"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$error"; tf_data.calltf = sys_severity_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$error"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$warning"; tf_data.calltf = sys_severity_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$warning"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$info"; tf_data.calltf = sys_severity_calltf; tf_data.compiletf = sys_display_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$info"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); cb_data.reason = cbEndOfCompile; cb_data.time = 0; cb_data.cb_rtn = sys_end_of_compile; cb_data.user_data = "system"; vpi_register_cb(&cb_data); cb_data.reason = cbEndOfSimulation; cb_data.cb_rtn = sys_end_of_simulation; cb_data.user_data = "system"; vpi_register_cb(&cb_data); } iverilog-10_1/vpi/sys_fileio.c000066400000000000000000001131311265551621300164510ustar00rootroot00000000000000/* * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include # include # include # include # include "ivl_alloc.h" #define IS_MCD(mcd) !((mcd)>>31&1) /* * Implement the $fopen system function. */ static PLI_INT32 sys_fopen_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); /* Check that there is a file name argument and that it is a string. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a string file name argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_string_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's file name argument must be a string.\n", name); vpi_control(vpiFinish, 1); } /* The type argument is optional. */ arg = vpi_scan(argv); if (arg == 0) return 0; /* When provided, the type argument must be a string. */ if (! is_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's type argument must be a string.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two string arguments", 1); return 0; } static PLI_INT32 sys_fopen_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; int fail = 0; char *mode_string = 0; vpiHandle fileh = vpi_scan(argv); char *fname; vpiHandle mode = vpi_scan(argv); errno = 0; /* Get the mode handle if it exists. */ if (mode) { char *esc_md; val.format = vpiStringVal; vpi_get_value(mode, &val); /* Verify that we have a string and that it is not NULL. */ if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's mode argument is not a valid string.\n", name); fail = 1; } /* Make sure the mode string is correct. */ if (strlen(val.value.str) > 3) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); esc_md = as_escaped(val.value.str); vpi_printf("%s's mode argument (%s) is too long.\n", name, esc_md); free(esc_md); fail = 1; } else { unsigned bin = 0, plus = 0, idx; switch (val.value.str[0]) { case 'r': case 'w': case 'a': for (idx = 1; idx < 3 ; idx++) { if (val.value.str[idx] == '\0') break; switch (val.value.str[idx]) { case 'b': if (bin) fail = 1; bin = 1; break; case '+': if (plus) fail = 1; plus = 1; break; default: fail = 1; break; } } if (! fail) break; default: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); esc_md = as_escaped(val.value.str); vpi_printf("%s's mode argument (%s) is invalid.\n", name, esc_md); free(esc_md); fail = 1; break; } } mode_string = strdup(val.value.str); vpi_free_object(argv); } fname = get_filename(callh, name, fileh); /* If either the mode or file name are not valid just return. */ if (fail || fname == 0) { free(fname); if (mode) free(mode_string); return 0; } val.format = vpiIntVal; if (mode) { val.value.integer = vpi_fopen(fname, mode_string); free(mode_string); } else val.value.integer = vpi_mcd_open(fname); vpi_put_value(callh, &val, 0, vpiNoDelay); free(fname); return 0; } /* * Implement the $fopenr(), $fopenw() and $fopena() system functions * from Chris Spear's File I/O for Verilog. */ static PLI_INT32 sys_fopenrwa_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; char *fname; const char *mode; errno = 0; /* Get the mode. */ mode = name + strlen(name) - 1; /* Get the file name. */ fname = get_filename(callh, name, vpi_scan(argv)); vpi_free_object(argv); if (fname == 0) return 0; /* Open the file and return the result. */ val.format = vpiIntVal; val.value.integer = vpi_fopen(fname, mode); vpi_put_value(callh, &val, 0, vpiNoDelay); free(fname); return 0; } /* * Implement $fclose system function */ static PLI_INT32 sys_fclose_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle fd = vpi_scan(argv); s_vpi_value val; PLI_UINT32 fd_mcd; errno = 0; vpi_free_object(argv); /* Get the file/MC descriptor and verify that it is valid. */ val.format = vpiIntVal; vpi_get_value(fd, &val); fd_mcd = val.value.integer; if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || ( IS_MCD(fd_mcd) && vpi_mcd_printf(fd_mcd, "%s", "") == EOF) || (! fd_mcd)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor/MCD (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; return 0; } /* We need to cancel any active $fstrobe()'s for this FD/MCD. * For now we check in the strobe callback and skip the output * generation when needed. */ vpi_mcd_close(fd_mcd); return 0; } /* * Implement $fflush system function */ static PLI_INT32 sys_fflush_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; PLI_UINT32 fd_mcd; errno = 0; /* If we have no argument then flush all the streams. */ if (argv == 0) { fflush(NULL); return 0; } /* Get the file/MC descriptor and verify that it is valid. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* If the MCD is zero we have nothing to do so just return. */ if (fd_mcd == 0) return 0; if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || ( IS_MCD(fd_mcd) && vpi_mcd_printf(fd_mcd, "%s", "") == EOF) || (! fd_mcd)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor/MCD (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; return 0; } if (IS_MCD(fd_mcd)) { vpi_mcd_flush(fd_mcd); } else { /* If we have a valid file descriptor flush the file. */ FILE *fp = vpi_get_file(fd_mcd); if (fp) fflush(fp); } return 0; } static PLI_INT32 sys_fputc_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; PLI_UINT32 fd_mcd; FILE *fp; unsigned char chr; errno = 0; /* Get the character. */ arg = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); chr = val.value.integer; /* Get the file/MC descriptor. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* Put the character and return the result. */ fp = vpi_get_file(fd_mcd); val.format = vpiIntVal; if (!fp) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; val.value.integer = EOF; } else { val.value.integer = fputc(chr, fp); if (val.value.integer != EOF) val.value.integer = 0; } vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_fgets_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* * Check that there are two arguments and that the first is a * register and that the second is numeric. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (vpi_get(vpiType, vpi_scan(argv)) != vpiReg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a reg.\n", name); vpi_control(vpiFinish, 1); } arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (numeric) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } static PLI_INT32 sys_fgets_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle regh; vpiHandle arg; s_vpi_value val; PLI_UINT32 fd_mcd; FILE *fp; PLI_INT32 reg_size; char*text; errno = 0; /* Get the register handle. */ regh = vpi_scan(argv); /* Get the file/MCD descriptor. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* Return zero if this is not a valid fd. */ fp = vpi_get_file(fd_mcd); if (!fp) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; val.format = vpiIntVal; val.value.integer = 0; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* Get the register size in bytes and allocate the buffer. */ reg_size = vpi_get(vpiSize, regh) / 8; text = malloc(reg_size + 1); /* Read in the bytes. Return 0 if there was an error. */ if (fgets(text, reg_size+1, fp) == 0) { val.format = vpiIntVal; val.value.integer = 0; vpi_put_value(callh, &val, 0, vpiNoDelay); free(text); return 0; } /* Return the number of character read. */ val.format = vpiIntVal; val.value.integer = strlen(text); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the characters to the register. */ val.format = vpiStringVal; val.value.str = text; vpi_put_value(regh, &val, 0, vpiNoDelay); free(text); return 0; } static PLI_INT32 sys_fread_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; PLI_INT32 type; /* We must have at least two arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the first required argument is a register or memory. */ type = vpi_get(vpiType, vpi_scan(argv)); if (type != vpiReg && type != vpiMemory) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a reg or memory.\n", name); vpi_control(vpiFinish, 1); } /* Check that the second required argument is numeric (a fd). */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (file descriptor) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* * If given check that the third argument is numeric (start). * * Technically you can give the fourth argument (count) with * out a third argument (start), but Icarus does not currently * support missing function arguments! */ arg = vpi_scan(argv); if (arg) { if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's third argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* If given check that the fourth argument is numeric (count). */ arg = vpi_scan(argv); if (arg) { if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's fourth argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "four arguments", 1); } } return 0; } /* * The pattern here is get the current vector, load the new bits on * top of the old ones and then put the modified vector. We need the * "get" first so that if we run out of bits in the file we keep the * original ones. */ static unsigned fread_word(FILE *fp, vpiHandle word, unsigned words, unsigned bpe, s_vpi_vecval *vector) { int bidx; s_vpi_value val; struct t_vpi_vecval *cur = &vector[words-1]; unsigned rtn = 0; /* Get the current bits from the register and copy them to * my local vector. */ val.format = vpiVectorVal; vpi_get_value(word, &val); for (bidx = 0; (unsigned)bidx < words; bidx += 1) { vector[bidx].aval = val.value.vector[bidx].aval; vector[bidx].bval = val.value.vector[bidx].bval; } /* Copy the bytes to the local vector MSByte first. */ for (bidx = bpe-1; bidx >= 0; bidx -= 1) { unsigned clr_mask, bnum; int byte = fgetc(fp); if (byte == EOF) break; /* Clear the current byte and load the new value. */ bnum = bidx % 4; clr_mask = ~(0xff << bnum*8); cur->aval &= clr_mask; cur->bval &= clr_mask; cur->aval |= byte << bnum*8; rtn += 1; if (bnum == 0) cur -= 1; } /* Put the updated bits into the register. */ val.value.vector = vector; vpi_put_value(word, &val, 0, vpiNoDelay); return rtn; } static PLI_INT32 sys_fread_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg, mem_reg; s_vpi_value val; PLI_UINT32 fd_mcd; PLI_INT32 start, count, width, rtn; unsigned is_mem, bpe, words; FILE *fp; s_vpi_vecval *vector; errno = 0; /* Get the register/memory. */ mem_reg = vpi_scan(argv); /* Get the file descriptor. */ arg = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* Return 0 if this is not a valid fd. */ fp = vpi_get_file(fd_mcd); if (!fp) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; val.format = vpiIntVal; val.value.integer = 0; vpi_put_value(callh, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } /* Are we reading into a memory? */ if (vpi_get(vpiType, mem_reg) == vpiReg) is_mem = 0; else is_mem = 1; /* We only need to get these for memories. */ if (is_mem) { PLI_INT32 left, right, max, min; /* Get the left and right memory address. */ val.format = vpiIntVal; vpi_get_value(vpi_handle(vpiLeftRange, mem_reg), &val); left = val.value.integer; val.format = vpiIntVal; vpi_get_value(vpi_handle(vpiRightRange, mem_reg), &val); right = val.value.integer; max = (left > right) ? left : right; min = (left < right) ? left : right; /* Get the starting address (optional). */ arg = vpi_scan(argv); if (arg) { val.format = vpiIntVal; vpi_get_value(arg, &val); start = val.value.integer; if (start < min || start > max) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's start argument (%d) is outside " "memory range [%d:%d].\n", name, (int)start, (int)left, (int)right); val.format = vpiIntVal; val.value.integer = 0; vpi_put_value(callh, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } /* Get the count (optional). */ arg = vpi_scan(argv); if (arg) { val.format = vpiIntVal; vpi_get_value(arg, &val); count = val.value.integer; if (count > max-start) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's count argument (%d) is too " "large for start (%d) and memory " "range [%d:%d].\n", name, (int)count, (int)start, (int)left, (int)right); count = max - start + 1; } vpi_free_object(argv); } else { count = max - start + 1; } } else { start = min; count = max - min + 1; } width = vpi_get(vpiSize, vpi_handle_by_index(mem_reg, start)); } else { start = 0; count = 1; width = vpi_get(vpiSize, mem_reg); vpi_free_object(argv); } assert(width > 0); words = (width - 1)/32 + 1; vector = calloc(words, sizeof(s_vpi_vecval)); bpe = (width+7)/8; assert(count >= 0); if (is_mem) { unsigned idx; rtn = 0; for (idx = 0; idx < (unsigned)count; idx += 1) { vpiHandle word; word = vpi_handle_by_index(mem_reg, start+(signed)idx); rtn += fread_word(fp, word, words, bpe, vector); if (feof(fp)) break; } } else { rtn = fread_word(fp, mem_reg, words, bpe, vector); } free(vector); /* Return the number of bytes read. */ val.format = vpiIntVal; val.value.integer = rtn; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_ungetc_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; PLI_UINT32 fd_mcd; FILE *fp; int chr; errno = 0; /* Get the character. */ arg = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); chr = val.value.integer; /* Get the file/MC descriptor. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* Return EOF if this is not a valid fd. */ fp = vpi_get_file(fd_mcd); if (!fp) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; val.format = vpiIntVal; val.value.integer = EOF; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* ungetc the character and return the result. */ val.format = vpiIntVal; val.value.integer = ungetc(chr, fp); if (val.value.integer != EOF) val.value.integer = 0; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_fseek_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are three numeric arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires three arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the first argument is numeric. */ if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Check that the second argument exists and is numeric. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (numeric) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Check that the third argument exists and is numeric. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a third (numeric) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's third argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "three arguments", 0); return 0; } static PLI_INT32 sys_fseek_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; PLI_UINT32 fd_mcd; PLI_INT32 offset, oper; FILE *fp; errno = 0; /* Get the file pointer. */ arg = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* Get the offset. */ arg = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); offset = val.value.integer; /* Get the operation. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); oper = val.value.integer; /* Check that the operation is in the valid range. */ switch (oper) { case 0: oper = SEEK_SET; break; case 1: oper = SEEK_CUR; break; case 2: oper = SEEK_END; break; default: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's operation must be 0, 1 or 2 given %d.\n", name, (int)oper); oper = -1; /* An invalid argument value. */ } /* Return EOF if this is not a valid fd. */ fp = vpi_get_file(fd_mcd); if (!fp) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; val.format = vpiIntVal; val.value.integer = EOF; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } val.format = vpiIntVal; #if defined(__GNUC__) val.value.integer = fseek(fp, offset, oper); #else if(oper < 0) { val.value.integer = EOF; errno = EINVAL; } else val.value.integer = fseek(fp, offset, oper); #endif vpi_put_value(callh, &val, 0 , vpiNoDelay); return 0; } static PLI_INT32 sys_common_fd_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; PLI_UINT32 fd_mcd; FILE *fp; errno = 0; /* Get the file pointer. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; /* Return EOF if this is not a valid fd. */ fp = vpi_get_file(fd_mcd); if (!fp) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; val.format = vpiIntVal; val.value.integer = EOF; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } val.format = vpiIntVal; switch (name[4]) { case 'l': /* $ftell() */ val.value.integer = ftell(fp); break; case 'f': /* $feof() is from 1264-2005*/ val.value.integer = feof(fp); break; case 'i': /* $rewind() */ val.value.integer = fseek(fp, 0L, SEEK_SET); break; case 't': /* $fgetc() */ val.value.integer = fgetc(fp); break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot be processed with this routine.\n", name); assert(0); break; } vpi_put_value(callh, &val, 0 , vpiNoDelay); return 0; } /* * Implement the $ferror system function. */ static PLI_INT32 sys_ferror_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; argv = vpi_iterate(vpiArgument, callh); /* * Check that there are two arguments and that the first is * numeric and that the second is a 640 bit or larger register. * * The parser requires that a function have at least one argument, * so argv should always be defined with one argument. */ assert(argv); if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's fd (first) argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Check that the second argument is given and that it is a 640 bit * or larger register. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (register) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (vpi_get(vpiType, arg) != vpiReg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be a reg (>=640 bits).\n", name); vpi_control(vpiFinish, 1); } else if (vpi_get(vpiSize, arg) < 640) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must have 640 bit or more.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } static PLI_INT32 sys_ferror_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle reg; s_vpi_value val; char *msg; PLI_INT32 size; unsigned chars; PLI_UINT32 fd_mcd; /* Get the file pointer. */ val.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &val); fd_mcd = val.value.integer; /* Get the register to put the string result and figure out how many * characters it will hold. */ reg = vpi_scan(argv); size = vpi_get(vpiSize, reg); chars = size / 8; vpi_free_object(argv); /* If we do not already have an error check that the fd is valid. * The assumption is that the other routines have set errno to * EBADF when they encounter a bad file descriptor, so we do not * need to check here. We also need to special case this since * $fopen() will return 0 (a bad file descriptor) when it has a * problem (sets errno). */ if (!errno && !vpi_get_file(fd_mcd) ) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (unsigned int)fd_mcd, name); errno = EBADF; } /* Return the error code. */ val.format = vpiIntVal; val.value.integer = errno; vpi_put_value(callh, &val, 0, vpiNoDelay); /* Only return the number of characters that will fit in the reg. */ msg = (char *) malloc(chars); if (errno != 0) strncpy(msg, strerror(errno), chars-1); else strncpy(msg, "", chars-1); msg[chars-1] = '\0'; val.format = vpiStringVal; val.value.str = msg; vpi_put_value(reg, &val, 0, vpiNoDelay); free(msg); return 0; } void sys_fileio_register(void) { s_vpi_systf_data tf_data; vpiHandle res; /*============================== fopen */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fopen"; tf_data.calltf = sys_fopen_calltf; tf_data.compiletf = sys_fopen_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fopen"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fopenr */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fopenr"; tf_data.calltf = sys_fopenrwa_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fopenr"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fopenw */ tf_data.tfname = "$fopenw"; tf_data.user_data = "$fopenw"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fopena */ tf_data.tfname = "$fopena"; tf_data.user_data = "$fopena"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fclose */ tf_data.type = vpiSysTask; tf_data.tfname = "$fclose"; tf_data.calltf = sys_fclose_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fclose"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fflush */ tf_data.type = vpiSysTask; tf_data.tfname = "$fflush"; tf_data.calltf = sys_fflush_calltf; tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fgetc */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fgetc"; tf_data.calltf = sys_common_fd_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fgetc"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fgets */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fgets"; tf_data.calltf = sys_fgets_calltf; tf_data.compiletf = sys_fgets_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fgets"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fread */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fread"; tf_data.calltf = sys_fread_calltf; tf_data.compiletf = sys_fread_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fread"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== ungetc */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$ungetc"; tf_data.calltf = sys_ungetc_calltf; tf_data.compiletf = sys_two_numeric_args_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ungetc"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== ftell */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$ftell"; tf_data.calltf = sys_common_fd_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ftell"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== fseek */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fseek"; tf_data.calltf = sys_fseek_calltf; tf_data.compiletf = sys_fseek_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fseek"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); vpip_make_systf_system_defined(res); /*============================== rewind */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$rewind"; tf_data.calltf = sys_common_fd_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$rewind"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== ferror */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$ferror"; tf_data.calltf = sys_ferror_calltf; tf_data.compiletf = sys_ferror_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ferror"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* $feof() is from 1364-2005. */ /*============================== feof */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$feof"; tf_data.calltf = sys_common_fd_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$feof"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* Icarus specific. */ /*============================== fputc */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fputc"; tf_data.calltf = sys_fputc_calltf; tf_data.compiletf = sys_two_numeric_args_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fputc"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_finish.c000066400000000000000000000044661265551621300164740ustar00rootroot00000000000000/* * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sys_priv.h" #include static PLI_INT32 sys_finish_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv; s_vpi_value val; long diag_msg = 1; /* Get the argument list and look for the diagnostic message level. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); if (argv) { vpiHandle arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); diag_msg = val.value.integer; } if (strcmp((const char*)name, "$stop") == 0) { vpi_control(vpiStop, diag_msg); return 0; } vpi_control(vpiFinish, diag_msg); return 0; } void sys_finish_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysTask; tf_data.tfname = "$finish"; tf_data.calltf = sys_finish_calltf; tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$finish"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$stop"; tf_data.calltf = sys_finish_calltf; tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$stop"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_fst.c000066400000000000000000000672101265551621300160040ustar00rootroot00000000000000/* * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include "vcd_priv.h" # include "fstapi.h" /* * This file contains the implementations of the FST related functions. */ # include # include # include # include # include # include "ivl_alloc.h" static char *dump_path = NULL; static struct fstContext *dump_file = NULL; struct vcd_info { vpiHandle item; vpiHandle cb; struct t_vpi_time time; struct vcd_info *next; struct vcd_info *dmp_next; fstHandle handle; int scheduled; }; static struct vcd_info *vcd_list = NULL; static struct vcd_info *vcd_dmp_list = NULL; static PLI_UINT64 vcd_cur_time = 0; static int dump_is_off = 0; static long dump_limit = 0; static int dump_is_full = 0; static int finish_status = 0; static enum lxm_optimum_mode_e { LXM_NONE = 0, LXM_SPACE = 1, LXM_SPEED = 2, LXM_BOTH = 3 } lxm_optimum_mode = LXM_NONE; static const char*units_names[] = { "s", "ms", "us", "ns", "ps", "fs" }; static void show_this_item(struct vcd_info*info) { s_vpi_value value; PLI_INT32 type = vpi_get(vpiType, info->item); if (type == vpiRealVar) { value.format = vpiRealVal; vpi_get_value(info->item, &value); fstWriterEmitValueChange(dump_file, info->handle, &value.value.real); } else { value.format = vpiBinStrVal; vpi_get_value(info->item, &value); fstWriterEmitValueChange(dump_file, info->handle, (type != vpiNamedEvent) ? value.value.str : "1"); } } /* Dump values for a $dumpoff. */ static void show_this_item_x(struct vcd_info*info) { PLI_INT32 type = vpi_get(vpiType, info->item); if (type == vpiRealVar) { /* Some tools dump nothing here...? */ double mynan = strtod("NaN", NULL); fstWriterEmitValueChange(dump_file, info->handle, &mynan); } else if (type == vpiNamedEvent) { /* Do nothing for named events. */ } else { int siz = vpi_get(vpiSize, info->item); char *xmem = malloc(siz); memset(xmem, 'x', siz); fstWriterEmitValueChange(dump_file, info->handle, xmem); free(xmem); } } /* * managed qsorted list of scope names/variables for duplicates bsearching */ struct vcd_names_list_s fst_tab = { 0, 0, 0, 0 }; struct vcd_names_list_s fst_var = { 0, 0, 0, 0 }; static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */ static PLI_UINT64 dumpvars_time; __inline__ static int dump_header_pending(void) { return dumpvars_status != 2; } /* * This function writes out all the traced variables, whether they * changed or not. */ static void vcd_checkpoint(void) { struct vcd_info*cur; for (cur = vcd_list ; cur ; cur = cur->next) show_this_item(cur); } static void vcd_checkpoint_x(void) { struct vcd_info*cur; for (cur = vcd_list ; cur ; cur = cur->next) show_this_item_x(cur); } static PLI_INT32 variable_cb_2(p_cb_data cause) { struct vcd_info* info = vcd_dmp_list; PLI_UINT64 now = timerec_to_time64(cause->time); if (now != vcd_cur_time) { fstWriterEmitTimeChange(dump_file, now); vcd_cur_time = now; } do { show_this_item(info); info->scheduled = 0; } while ((info = info->dmp_next) != 0); vcd_dmp_list = 0; return 0; } static PLI_INT32 variable_cb_1(p_cb_data cause) { struct t_cb_data cb; struct vcd_info*info = (struct vcd_info*)cause->user_data; if (dump_is_full) return 0; if (dump_is_off) return 0; if (dump_header_pending()) return 0; if (info->scheduled) return 0; if ((dump_limit > 0) && fstWriterGetDumpSizeLimitReached(dump_file)) { dump_is_full = 1; vpi_printf("WARNING: Dump file limit (%ld bytes) " "exceeded.\n", dump_limit); return 0; } if (!vcd_dmp_list) { cb = *cause; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); } info->scheduled = 1; info->dmp_next = vcd_dmp_list; vcd_dmp_list = info; return 0; } static PLI_INT32 dumpvars_cb(p_cb_data cause) { if (dumpvars_status != 1) return 0; dumpvars_status = 2; dumpvars_time = timerec_to_time64(cause->time); vcd_cur_time = dumpvars_time; /* nothing to do for $enddefinitions $end */ if (!dump_is_off) { fstWriterEmitTimeChange(dump_file, dumpvars_time); /* nothing to do for $dumpvars... */ vcd_checkpoint(); /* ...nothing to do for $end */ } return 0; } static PLI_INT32 finish_cb(p_cb_data cause) { struct vcd_info *cur, *next; if (finish_status != 0) return 0; finish_status = 1; dumpvars_time = timerec_to_time64(cause->time); if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) { fstWriterEmitTimeChange(dump_file, dumpvars_time); } fstWriterClose(dump_file); for (cur = vcd_list ; cur ; cur = next) { next = cur->next; free(cur); } vcd_list = 0; vcd_names_delete(&fst_tab); vcd_names_delete(&fst_var); nexus_ident_delete(); free(dump_path); dump_path = 0; return 0; } __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; static struct t_vpi_time now; if (dumpvars_status == 1) return 0; if (dumpvars_status == 2) { vpi_printf("FST warning: $dumpvars ignored, previously" " called at simtime %" PLI_UINT64_FMT "\n", dumpvars_time); return 1; } now.type = vpiSimTime; cb.time = &now; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; cb.obj = 0x0; vpi_register_cb(&cb); cb.reason = cbEndOfSimulation; cb.cb_rtn = finish_cb; vpi_register_cb(&cb); dumpvars_status = 1; return 0; } static PLI_INT32 sys_dumpoff_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; dump_is_off = 1; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { fstWriterEmitTimeChange(dump_file, now64); vcd_cur_time = now64; } fstWriterEmitDumpActive(dump_file, 0); /* $dumpoff */ vcd_checkpoint_x(); return 0; } static PLI_INT32 sys_dumpon_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (!dump_is_off) return 0; dump_is_off = 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { fstWriterEmitTimeChange(dump_file, now64); vcd_cur_time = now64; } fstWriterEmitDumpActive(dump_file, 1); /* $dumpon */ vcd_checkpoint(); return 0; } static PLI_INT32 sys_dumpall_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { fstWriterEmitTimeChange(dump_file, now64); vcd_cur_time = now64; } /* nothing to do for $dumpall... */ vcd_checkpoint(); return 0; } static void open_dumpfile(vpiHandle callh) { if (dump_path == 0) dump_path = strdup("dump.fst"); dump_file = fstWriterCreate(dump_path, 1); if (dump_file == 0) { vpi_printf("FST Error: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); free(dump_path); dump_path = 0; return; } else { int prec = vpi_get(vpiTimePrecision, 0); unsigned scale = 1; unsigned udx = 0; time_t walltime; char scale_buf[65]; vpi_printf("FST info: dumpfile %s opened for output.\n", dump_path); time(&walltime); assert(prec >= -15); while (prec < 0) { udx += 1; prec += 3; } while (prec > 0) { scale *= 10; prec -= 1; } fstWriterSetDate(dump_file, asctime(localtime(&walltime))); fstWriterSetVersion(dump_file, "Icarus Verilog"); sprintf(scale_buf, "\t%u%s\n", scale, units_names[udx]); fstWriterSetTimescaleFromString(dump_file, scale_buf); /* Set the faster dump type when requested. */ if ((lxm_optimum_mode == LXM_SPEED) || (lxm_optimum_mode == LXM_BOTH)) { fstWriterSetPackType(dump_file, 1); } /* Set the most effective compression when requested. */ if ((lxm_optimum_mode == LXM_SPACE) || (lxm_optimum_mode == LXM_BOTH)) { fstWriterSetRepackOnClose(dump_file, 1); } } } static PLI_INT32 sys_dumpfile_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); char *path; /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { char msg[64]; snprintf(msg, sizeof(msg), "FST warning: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s called after $dumpvars started,\n", msg, name); vpi_printf("%*s using existing file (%s).\n", (int) strlen(msg), " ", dump_path); vpi_free_object(argv); return 0; } path = get_filename(callh, name, vpi_scan(argv)); vpi_free_object(argv); if (! path) return 0; if (dump_path) { vpi_printf("FST warning: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Overriding dump file %s with %s.\n", dump_path, path); free(dump_path); } dump_path = path; return 0; } static PLI_INT32 sys_dumpflush_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ if (dump_file) fstWriterFlushContext(dump_file); return 0; } static PLI_INT32 sys_dumplimit_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; (void)name; /* Parameter is not used. */ /* Get the value and set the dump limit. */ val.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &val); dump_limit = val.value.integer; fstWriterSetDumpSizeLimit(dump_file, dump_limit); vpi_free_object(argv); return 0; } static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; enum fstVarType type = FST_VT_MAX; enum fstScopeType stype = FST_ST_MAX; enum fstVarDir dir; const char *name; const char *fullname; char *escname; const char *ident; fstHandle new_ident; int nexus_id; unsigned size; PLI_INT32 item_type; /* Get the displayed type for the various $var and $scope types. */ /* Not all of these are supported now, but they should be in a * future development version. */ item_type = vpi_get(vpiType, item); switch (item_type) { case vpiNamedEvent: type = FST_VT_VCD_EVENT; break; case vpiIntVar: case vpiIntegerVar: type = FST_VT_VCD_INTEGER; break; case vpiParameter: type = FST_VT_VCD_PARAMETER; break; /* Icarus converts realtime to real. */ case vpiRealVar: type = FST_VT_VCD_REAL; break; case vpiMemoryWord: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiLongIntVar: case vpiReg: type = FST_VT_VCD_REG; break; /* Icarus converts a time to a plain register. */ case vpiTimeVar: type = FST_VT_VCD_TIME; break; case vpiNet: switch (vpi_get(vpiNetType, item)) { case vpiWand: type = FST_VT_VCD_WAND; break; case vpiWor: type = FST_VT_VCD_WOR; break; case vpiTri: type = FST_VT_VCD_TRI; break; case vpiTri0: type = FST_VT_VCD_TRI0; break; case vpiTri1: type = FST_VT_VCD_TRI1; break; case vpiTriReg: type = FST_VT_VCD_TRIREG; break; case vpiTriAnd: type = FST_VT_VCD_TRIAND; break; case vpiTriOr: type = FST_VT_VCD_TRIOR; break; case vpiSupply1: type = FST_VT_VCD_SUPPLY1; break; case vpiSupply0: type = FST_VT_VCD_SUPPLY0; break; default: type = FST_VT_VCD_WIRE; break; } break; case vpiNamedBegin: stype = FST_ST_VCD_BEGIN; break; case vpiNamedFork: stype = FST_ST_VCD_FORK; break; case vpiFunction: stype = FST_ST_VCD_FUNCTION; break; case vpiGenScope: stype = FST_ST_VCD_GENERATE; break; case vpiModule: stype = FST_ST_VCD_MODULE; break; case vpiTask: stype = FST_ST_VCD_TASK; break; default: vpi_printf("FST warning: $dumpvars: Unsupported argument " "type (%s)\n", vpi_get_str(vpiType, item)); return; } /* Do some special processing/checking on array words. Dumping * array words is an Icarus extension. */ if (item_type == vpiMemoryWord) { /* Turn a non-constant array word select into a constant * word select. */ if (vpi_get(vpiConstantSelect, item) == 0) { vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ /* This does not work as expected since we always find at * least the array word. We likely need a custom routine. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("FST warning: array word %s will conflict " "with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } } fullname = vpi_get_str(vpiFullName, item); /* Generate the $var or $scope commands. */ switch (item_type) { case vpiParameter: vpi_printf("FST sorry: $dumpvars: can not dump parameters.\n"); break; case vpiNamedEvent: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiMemoryWord: case vpiReg: case vpiTimeVar: case vpiNet: /* If we are skipping all signal or this is in an automatic * scope then just return. */ if (skip || vpi_get(vpiAutomatic, item)) return; /* Skip this signal if it has already been included. * This can only happen for implicitly given signals. */ if (vcd_names_search(&fst_var, fullname)) return; /* Declare the variable in the FST file. */ name = vpi_get_str(vpiName, item); if (is_escaped_id(name)) { escname = malloc(strlen(name) + 2); sprintf(escname, "\\%s", name); } else escname = strdup(name); /* Some signals can have an alias so handle that. */ nexus_id = vpi_get(_vpiNexusId, item); ident = 0; if (nexus_id) ident = find_nexus_ident(nexus_id); /* Named events do not have a size, but other tools use * a size of 1 and some viewers do not accept a width of * zero so we will also use a width of one for events. */ if (item_type == vpiNamedEvent) size = 1; else size = vpi_get(vpiSize, item); /* The FST format supports a port direction so if the net * is a port set the direction to one of the following: * FST_VD_INPUT, FST_VD_OUTPUT or FST_VD_INOUT */ dir = FST_VD_IMPLICIT; if (size > 1 || vpi_get(vpiLeftRange, item) != 0) { char *buf = malloc(strlen(escname) + 65); sprintf(buf, "%s [%i:%i]", escname, (int)vpi_get(vpiLeftRange, item), (int)vpi_get(vpiRightRange, item)); new_ident = fstWriterCreateVar(dump_file, type, dir, size, buf, (fstHandle)(intptr_t)ident); free(buf); } else { new_ident = fstWriterCreateVar(dump_file, type, dir, size, escname, (fstHandle)(intptr_t)ident); } free(escname); if (!ident) { if (nexus_id) set_nexus_ident(nexus_id, (const char *)(intptr_t)new_ident); /* Add a callback for the signal. */ info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->handle = new_ident; info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->dmp_next = 0; info->next = vcd_list; vcd_list = info; info->cb = vpi_register_cb(&cb); } break; case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: if (depth > 0) { char *instname; char *defname = NULL; /* list of types to iterate upon */ static int types[] = { /* Value */ vpiNamedEvent, vpiNet, /* vpiParameter, */ vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiGenScope, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; int i; int nskip = (vcd_names_search(&fst_tab, fullname) != 0); /* We have to always scan the scope because the * depth could be different for this call. */ if (nskip) { vpi_printf("FST warning: ignoring signals in " "previously scanned scope %s.\n", fullname); } else { vcd_names_add(&fst_tab, fullname); } /* Set the file and line information for this scope. * Everything has instance information. Only a module * has separate definition information. */ instname = vpi_get_str(vpiFile, item); fstWriterSetSourceInstantiationStem(dump_file, instname, (int)vpi_get(vpiLineNo, item), 0); if (item_type == vpiModule) { fstWriterSetSourceStem(dump_file, vpi_get_str(vpiDefFile, item), (int)vpi_get(vpiDefLineNo, item), 0); } else { fstWriterSetSourceStem(dump_file, instname, (int)vpi_get(vpiLineNo, item), 0); } /* This must be done before the other name is fetched * and the string must always be freed */ if (item_type == vpiModule) { defname = strdup(vpi_get_str(vpiDefName, item)); } name = vpi_get_str(vpiName, item); /* If the two names match only use the vpiName. */ if (defname && (strcmp(defname, name) == 0)) { free(defname); defname = NULL; } fstWriterSetScope(dump_file, stype, name, defname); free(defname); for (i=0; types[i]>0; i++) { vpiHandle hand; vpiHandle argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } /* Sort any signals that we added above. */ fstWriterSetUpscope(dump_file); } break; } } static int draw_scope(vpiHandle item, vpiHandle callh) { int depth; const char *name; char *defname = NULL; PLI_INT32 scope_type, type; vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) return 0; depth = 1 + draw_scope(scope, callh); scope_type = vpi_get(vpiType, scope); /* This must be done before the other name is fetched * and the string must always be freed */ if (scope_type == vpiModule) { defname = strdup(vpi_get_str(vpiDefName, scope)); } name = vpi_get_str(vpiName, scope); /* If the two names match only use the vpiName. */ if (defname && (strcmp(defname, name) == 0)) { free(defname); defname = NULL; } switch (scope_type) { case vpiNamedBegin: type = FST_ST_VCD_BEGIN; break; case vpiFunction: type = FST_ST_VCD_FUNCTION; break; case vpiNamedFork: type = FST_ST_VCD_FORK; break; case vpiGenScope: type = FST_ST_VCD_GENERATE; break; case vpiModule: type = FST_ST_VCD_MODULE; break; case vpiTask: type = FST_ST_VCD_TASK; break; default: type = FST_ST_VCD_MODULE; vpi_printf("FST Error: %s:%d: $dumpvars: Unsupported scope " "type (%d)\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), (int)vpi_get(vpiType, item)); assert(0); } fstWriterSetScope(dump_file, type, name, defname); free(defname); return depth; } static PLI_INT32 sys_dumpvars_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle item; s_vpi_value value; unsigned depth = 0; (void)name; /* Parameter is not used. */ if (dump_file == 0) { open_dumpfile(callh); if (dump_file == 0) { if (argv) vpi_free_object(argv); return 0; } } if (install_dumpvars_callback()) { if (argv) vpi_free_object(argv); return 0; } /* Get the depth if it exists. */ if (argv) { value.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &value); depth = value.value.integer; } if (!depth) depth = 10000; /* This dumps all the modules in the design if none are given. */ if (!argv || !(item = vpi_scan(argv))) { argv = vpi_iterate(vpiModule, 0x0); assert(argv); /* There must be at least one top level module. */ item = vpi_scan(argv); } for ( ; item; item = vpi_scan(argv)) { char *scname; const char *fullname; int add_var = 0; int dep; PLI_INT32 item_type = vpi_get(vpiType, item); /* If this is a signal make sure it has not already * been included. */ switch (item_type) { case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiMemoryWord: case vpiNamedEvent: case vpiNet: case vpiParameter: case vpiRealVar: case vpiReg: case vpiTimeVar: /* Warn if the variables scope (which includes the * variable) or the variable itself was already * included. A scope does not automatically include * memory words so do not check the scope for them. */ scname = strdup(vpi_get_str(vpiFullName, vpi_handle(vpiScope, item))); fullname = vpi_get_str(vpiFullName, item); if (((item_type != vpiMemoryWord) && vcd_names_search(&fst_tab, scname)) || vcd_names_search(&fst_var, fullname)) { vpi_printf("FST warning: skipping signal %s, " "it was previously included.\n", fullname); free(scname); continue; } else { add_var = 1; } free(scname); } dep = draw_scope(item, callh); scan_item(depth, item, 0); /* The scope list must be sorted after we scan an item. */ vcd_names_sort(&fst_tab); while (dep--) fstWriterSetUpscope(dump_file); /* Add this signal to the variable list so we can verify it * is not included twice. This must be done after it has * been added */ if (add_var) { vcd_names_add(&fst_var, vpi_get_str(vpiFullName, item)); vcd_names_sort(&fst_var); } } return 0; } void sys_fst_register(void) { int idx; struct t_vpi_vlog_info vlog_info; s_vpi_systf_data tf_data; vpiHandle res; /* Scan the extended arguments, looking for fst optimization flags. */ vpi_get_vlog_info(&vlog_info); /* The "speed" option is not used in this dumper. */ for (idx = 0 ; idx < vlog_info.argc ; idx += 1) { if (strcmp(vlog_info.argv[idx],"-fst-space") == 0) { lxm_optimum_mode = LXM_SPACE; } else if (strcmp(vlog_info.argv[idx],"-fst-speed") == 0) { lxm_optimum_mode = LXM_SPEED; } else if (strcmp(vlog_info.argv[idx],"-fst-space-speed") == 0) { lxm_optimum_mode = LXM_BOTH; } else if (strcmp(vlog_info.argv[idx],"-fst-speed-space") == 0) { lxm_optimum_mode = LXM_BOTH; } } /* All the compiletf routines are located in vcd_priv.c. */ tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpvars"; tf_data.calltf = sys_dumpvars_calltf; tf_data.compiletf = sys_dumpvars_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpvars"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_icarus.c000066400000000000000000000245711265551621300165010ustar00rootroot00000000000000/* * Copyright (C) 2008-2012 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "sys_priv.h" #include static PLI_INT32 finish_and_return_calltf(ICARUS_VPI_CONST PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; (void) name; /* Not used! */ /* Get the return value. */ arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; vpi_get_value(arg, &val); /* Set the return value. */ vpip_set_return_value(val.value.integer); /* Now finish. */ vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 task_not_implemented_compiletf(ICARUS_VPI_CONST PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpi_printf("SORRY: %s:%d: task %s() is not currently implemented.\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), name); vpi_control(vpiFinish, 1); return 0; } /* * This is used to warn the user that the specified optional system * task/function is not available (from Annex C 1364-2005). */ static PLI_INT32 missing_optional_compiletf(ICARUS_VPI_CONST PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpi_printf("SORRY: %s:%d: %s() is not available in Icarus verilog.\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), name); vpi_control(vpiFinish, 1); return 0; } /* * Register the function with Verilog. */ void sys_special_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysTask; tf_data.calltf = finish_and_return_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$finish_and_return"; tf_data.user_data = "$finish_and_return"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* These tasks are not currently implemented. */ tf_data.type = vpiSysTask; tf_data.calltf = 0; tf_data.sizetf = 0; tf_data.compiletf = task_not_implemented_compiletf; tf_data.tfname = "$fmonitor"; tf_data.user_data = "$fmonitor"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$fmonitorb"; tf_data.user_data = "$fmonitorb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$fmonitoro"; tf_data.user_data = "$fmonitoro"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$fmonitorh"; tf_data.user_data = "$fmonitorh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$and$array"; tf_data.user_data = "$async$and$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$nand$array"; tf_data.user_data = "$async$nand$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$or$array"; tf_data.user_data = "$async$or$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$nor$array"; tf_data.user_data = "$async$nor$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$and$plane"; tf_data.user_data = "$async$and$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$nand$plane"; tf_data.user_data = "$async$nand$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$or$plane"; tf_data.user_data = "$async$or$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$async$nor$plane"; tf_data.user_data = "$async$nor$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$and$array"; tf_data.user_data = "$sync$and$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$nand$array"; tf_data.user_data = "$sync$nand$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$or$array"; tf_data.user_data = "$sync$or$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$nor$array"; tf_data.user_data = "$sync$nor$array"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$and$plane"; tf_data.user_data = "$sync$and$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$nand$plane"; tf_data.user_data = "$sync$nand$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$or$plane"; tf_data.user_data = "$sync$or$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sync$nor$plane"; tf_data.user_data = "$sync$nor$plane"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$dumpports"; tf_data.user_data = "$dumpports"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$dumpportsoff"; tf_data.user_data = "$dumpportsoff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$dumpportson"; tf_data.user_data = "$dumpportson"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$dumpportsall"; tf_data.user_data = "$dumpportsall"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$dumpportslimit"; tf_data.user_data = "$dumpportslimit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$dumpportsflush"; tf_data.user_data = "$dumpportsflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* The following optional system tasks/functions are not implemented * in Icarus Verilog (from Annex C 1364-2005). */ tf_data.type = vpiSysTask; tf_data.calltf = 0; tf_data.sizetf = 0; tf_data.compiletf = missing_optional_compiletf; tf_data.tfname = "$input"; tf_data.user_data = "$input"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$key"; tf_data.user_data = "$key"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$nokey"; tf_data.user_data = "$nokey"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$list"; tf_data.user_data = "$list"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$log"; tf_data.user_data = "$log"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$nolog"; tf_data.user_data = "$nolog"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$save"; tf_data.user_data = "$save"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$restart"; tf_data.user_data = "$restart"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$incsave"; tf_data.user_data = "$incsave"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$scope"; tf_data.user_data = "$scope"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$showscopes"; tf_data.user_data = "$showscopes"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$showvars"; tf_data.user_data = "$showvars"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sreadmemb"; tf_data.user_data = "$sreadmemb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$sreadmemh"; tf_data.user_data = "$sreadmemh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* Optional functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$getpattern"; tf_data.user_data = "$getpattern"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$scale"; tf_data.user_data = "$scale"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_lxt.c000066400000000000000000000527141265551621300160220ustar00rootroot00000000000000/* * Copyright (c) 2002-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* The sys_priv.h include must be before the lxt_write.h include! */ # include "sys_priv.h" # include "lxt_write.h" # include "vcd_priv.h" /* * This file contains the implementations of the LXT related functions. */ # include # include # include # include # include # include "stringheap.h" # include "ivl_alloc.h" static char *dump_path = NULL; static struct lt_trace *dump_file = NULL; struct vcd_info { vpiHandle item; vpiHandle cb; struct t_vpi_time time; struct lt_symbol *sym; struct vcd_info *next; struct vcd_info *dmp_next; int scheduled; }; static struct vcd_info *vcd_list = NULL; static struct vcd_info *vcd_dmp_list = NULL; static PLI_UINT64 vcd_cur_time = 0; static int dump_is_off = 0; static long dump_limit = 0; static int dump_is_full = 0; static int finish_status = 0; static enum lxm_optimum_mode_e { LXM_NONE = 0, LXM_SPACE = 1, LXM_SPEED = 2 } lxm_optimum_mode = LXM_SPEED; /* * The lxt_scope head and current pointers are used to keep a scope * stack that can be accessed from the bottom. The lxt_scope_head * points to the first (bottom) item in the stack and * lxt_scope_current points to the last (top) item in the stack. The * push_scope and pop_scope methods manipulate the stack. */ struct lxt_scope { struct lxt_scope *next, *prev; char *name; int len; }; static struct lxt_scope *lxt_scope_head=NULL, *lxt_scope_current=NULL; static void push_scope(const char *name) { struct lxt_scope *t = (struct lxt_scope *) calloc(1, sizeof(struct lxt_scope)); t->name = strdup(name); t->len = strlen(name); if(!lxt_scope_head) { lxt_scope_head = lxt_scope_current = t; } else { lxt_scope_current->next = t; t->prev = lxt_scope_current; lxt_scope_current = t; } } static void pop_scope(void) { struct lxt_scope *t; assert(lxt_scope_current); t=lxt_scope_current->prev; free(lxt_scope_current->name); free(lxt_scope_current); lxt_scope_current = t; if (lxt_scope_current) { lxt_scope_current->next = 0; } else { lxt_scope_head = 0; } } /* * This function uses the scope stack to generate a hierarchical * name. Scan the scope stack from the bottom up to construct the * name. */ static char *create_full_name(const char *name) { char *n, *n2; int len = 0; int is_esc_id = is_escaped_id(name); struct lxt_scope *t = lxt_scope_head; /* Figure out how long the combined string will be. */ while(t) { len+=t->len+1; t=t->next; } len += strlen(name) + 1; if (is_esc_id) len += 1; /* Allocate a string buffer. */ n = n2 = malloc(len); t = lxt_scope_head; while(t) { strcpy(n2, t->name); n2 += t->len; *n2 = '.'; n2++; t=t->next; } if (is_esc_id) { *n2 = '\\'; n2++; } strcpy(n2, name); n2 += strlen(n2); assert( (n2 - n + 1) == len ); return n; } static void show_this_item(struct vcd_info*info) { s_vpi_value value; if (vpi_get(vpiType, info->item) == vpiRealVar) { value.format = vpiRealVal; vpi_get_value(info->item, &value); lt_emit_value_double(dump_file, info->sym, 0, value.value.real); } else { value.format = vpiBinStrVal; vpi_get_value(info->item, &value); lt_emit_value_bit_string(dump_file, info->sym, 0 /* array row */, value.value.str); } } static void show_this_item_x(struct vcd_info*info) { if (vpi_get(vpiType,info->item) == vpiRealVar) { /* Should write a NaN here? */ } else { lt_emit_value_bit_string(dump_file, info->sym, 0, "x"); } } /* * managed qsorted list of scope names for duplicates bsearching */ struct vcd_names_list_s lxt_tab; static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */ static PLI_UINT64 dumpvars_time; __inline__ static int dump_header_pending(void) { return dumpvars_status != 2; } /* * This function writes out all the traced variables, whether they * changed or not. */ static void vcd_checkpoint(void) { struct vcd_info*cur; for (cur = vcd_list ; cur ; cur = cur->next) show_this_item(cur); } static void vcd_checkpoint_x(void) { struct vcd_info*cur; for (cur = vcd_list ; cur ; cur = cur->next) show_this_item_x(cur); } static PLI_INT32 variable_cb_2(p_cb_data cause) { struct vcd_info* info = vcd_dmp_list; PLI_UINT64 now = timerec_to_time64(cause->time); if (now != vcd_cur_time) { lt_set_time64(dump_file, now); vcd_cur_time = now; } do { show_this_item(info); info->scheduled = 0; } while ((info = info->dmp_next) != 0); vcd_dmp_list = 0; return 0; } static PLI_INT32 variable_cb_1(p_cb_data cause) { struct t_cb_data cb; struct vcd_info*info = (struct vcd_info*)cause->user_data; if (dump_is_full) return 0; if (dump_is_off) return 0; if (dump_header_pending()) return 0; if (info->scheduled) return 0; if ((dump_limit > 0) && (ftell(dump_file->handle) > dump_limit)) { dump_is_full = 1; vpi_printf("WARNING: Dump file limit (%ld bytes) " "exceeded.\n", dump_limit); return 0; } if (!vcd_dmp_list) { cb = *cause; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); } info->scheduled = 1; info->dmp_next = vcd_dmp_list; vcd_dmp_list = info; return 0; } static PLI_INT32 dumpvars_cb(p_cb_data cause) { if (dumpvars_status != 1) return 0; dumpvars_status = 2; dumpvars_time = timerec_to_time64(cause->time); vcd_cur_time = dumpvars_time; if (!dump_is_off) { lt_set_time64(dump_file, dumpvars_time); vcd_checkpoint(); } return 0; } static PLI_INT32 finish_cb(p_cb_data cause) { struct vcd_info *cur, *next; if (finish_status != 0) return 0; finish_status = 1; dumpvars_time = timerec_to_time64(cause->time); if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) { lt_set_time64(dump_file, dumpvars_time); } for (cur = vcd_list ; cur ; cur = next) { next = cur->next; free(cur); } vcd_list = 0; vcd_names_delete(&lxt_tab); nexus_ident_delete(); free(dump_path); dump_path = 0; return 0; } __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; static struct t_vpi_time now; if (dumpvars_status == 1) return 0; if (dumpvars_status == 2) { vpi_printf("LXT warning: $dumpvars ignored, previously" " called at simtime %" PLI_UINT64_FMT "\n", dumpvars_time); return 1; } now.type = vpiSimTime; cb.time = &now; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; cb.obj = 0x0; vpi_register_cb(&cb); cb.reason = cbEndOfSimulation; cb.cb_rtn = finish_cb; vpi_register_cb(&cb); dumpvars_status = 1; return 0; } static PLI_INT32 sys_dumpoff_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; dump_is_off = 1; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { lt_set_time64(dump_file, now64); vcd_cur_time = now64; } lt_set_dumpoff(dump_file); vcd_checkpoint_x(); return 0; } static PLI_INT32 sys_dumpon_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (!dump_is_off) return 0; dump_is_off = 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { lt_set_time64(dump_file, now64); vcd_cur_time = now64; } lt_set_dumpon(dump_file); vcd_checkpoint(); return 0; } static PLI_INT32 sys_dumpall_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { lt_set_time64(dump_file, now64); vcd_cur_time = now64; } vcd_checkpoint(); return 0; } static void *close_dumpfile(void) { lt_close(dump_file); dump_file = NULL; return NULL; } static void open_dumpfile(vpiHandle callh) { if (dump_path == 0) dump_path = strdup("dump.lxt"); dump_file = lt_init(dump_path); if (dump_file == 0) { vpi_printf("LXT Error: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); free(dump_path); dump_path = 0; return; } else { int prec = vpi_get(vpiTimePrecision, 0); vpi_printf("LXT info: dumpfile %s opened for output.\n", dump_path); assert(prec >= -15); lt_set_timescale(dump_file, prec); lt_set_initial_value(dump_file, 'x'); lt_set_clock_compress(dump_file); atexit((void(*)(void))close_dumpfile); } } static PLI_INT32 sys_dumpfile_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); char *path; /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { char msg[64]; snprintf(msg, sizeof(msg), "LXT warning: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s called after $dumpvars started,\n", msg, name); vpi_printf("%*s using existing file (%s).\n", (int) strlen(msg), " ", dump_path); vpi_free_object(argv); return 0; } path = get_filename(callh, name, vpi_scan(argv)); vpi_free_object(argv); if (! path) return 0; if (dump_path) { vpi_printf("LXT warning: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Overriding dump file %s with %s.\n", dump_path, path); free(dump_path); } dump_path = path; return 0; } /* * The LXT1 format has no concept of file flushing. */ static PLI_INT32 sys_dumpflush_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 0; } static PLI_INT32 sys_dumplimit_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; (void)name; /* Parameter is not used. */ /* Get the value and set the dump limit. */ val.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &val); dump_limit = val.value.integer; vpi_free_object(argv); return 0; } static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; const char* name; const char* ident; int nexus_id; switch (vpi_get(vpiType, item)) { case vpiMemoryWord: if (vpi_get(vpiConstantSelect, item) == 0) { /* Turn a non-constant array word select into a * constant word select. */ vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: case vpiReg: case vpiNet: /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("LXT warning: dumping array word %s will " "conflict with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } if (skip || vpi_get(vpiAutomatic, item)) break; name = vpi_get_str(vpiName, item); nexus_id = vpi_get(_vpiNexusId, item); if (nexus_id) { ident = find_nexus_ident(nexus_id); } else { ident = 0; } if (!ident) { char*tmp = create_full_name(name); ident = strdup_sh(&name_heap, tmp); free(tmp); if (nexus_id) set_nexus_ident(nexus_id, ident); info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiLeftRange, item), vpi_get(vpiRightRange, item), LT_SYM_F_BITS); info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->next = vcd_list; vcd_list = info; info->cb = vpi_register_cb(&cb); } else { char *n = create_full_name(name); lt_symbol_alias(dump_file, ident, n, vpi_get(vpiSize, item)-1, 0); free(n); } break; case vpiRealVar: if (skip || vpi_get(vpiAutomatic, item)) break; name = vpi_get_str(vpiName, item); { char*tmp = create_full_name(name); ident = strdup_sh(&name_heap, tmp); free(tmp); } info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiSize, item)-1, 0, LT_SYM_F_DOUBLE); info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->next = vcd_list; vcd_list = info; info->cb = vpi_register_cb(&cb); break; case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: if (depth > 0) { const char* fullname = vpi_get_str(vpiFullName, item); /* list of types to iterate upon */ static int types[] = { /* Value */ /* vpiNamedEvent, */ vpiNet, /* vpiParameter, */ vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiGenScope, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; int i; int nskip = (vcd_names_search(&lxt_tab, fullname) != 0); #if 0 vpi_printf("LXT info: scanning scope %s, %u levels\n", fullname, depth); #endif if (nskip) { vpi_printf("LXT warning: ignoring signals in " "previously scanned scope %s\n", fullname); } else { vcd_names_add(&lxt_tab, fullname); } name = vpi_get_str(vpiName, item); push_scope(name); for (i=0; types[i]>0; i++) { vpiHandle hand; vpiHandle argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } pop_scope(); } break; default: vpi_printf("LXT warning: $dumpvars: Unsupported parameter " "type (%s)\n", vpi_get_str(vpiType, item)); } } static int draw_scope(vpiHandle item) { int depth; const char *name; vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) return 0; depth = 1 + draw_scope(scope); name = vpi_get_str(vpiName, scope); push_scope(name); return depth; } static PLI_INT32 sys_dumpvars_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle item; s_vpi_value value; unsigned depth = 0; (void)name; /* Parameter is not used. */ if (dump_file == 0) { open_dumpfile(callh); if (dump_file == 0) { if (argv) vpi_free_object(argv); return 0; } } if (install_dumpvars_callback()) { if (argv) vpi_free_object(argv); return 0; } /* Get the depth if it exists. */ if (argv) { value.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &value); depth = value.value.integer; } if (!depth) depth = 10000; /* This dumps all the modules in the design if none are given. */ if (!argv || !(item = vpi_scan(argv))) { argv = vpi_iterate(vpiModule, 0x0); assert(argv); /* There must be at least one top level module. */ item = vpi_scan(argv); } for ( ; item; item = vpi_scan(argv)) { int dep = draw_scope(item); scan_item(depth, item, 0); /* The scope list must be sorted after we scan an item. */ vcd_names_sort(&lxt_tab); while (dep--) pop_scope(); } /* Most effective compression. */ if (lxm_optimum_mode == LXM_SPACE) { lt_set_no_interlace(dump_file); } return 0; } void sys_lxt_register(void) { int idx; struct t_vpi_vlog_info vlog_info; s_vpi_systf_data tf_data; vpiHandle res; /* Scan the extended arguments, looking for lxt optimization flags. */ vpi_get_vlog_info(&vlog_info); /* The "speed" option is not used in this dumper. */ for (idx = 0 ; idx < vlog_info.argc ; idx += 1) { if (strcmp(vlog_info.argv[idx],"-lxt-space") == 0) { lxm_optimum_mode = LXM_SPACE; } else if (strcmp(vlog_info.argv[idx],"-lxt-speed") == 0) { lxm_optimum_mode = LXM_SPEED; } } /* All the compiletf routines are located in vcd_priv.c. */ tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpvars"; tf_data.calltf = sys_dumpvars_calltf; tf_data.compiletf = sys_dumpvars_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpvars"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_lxt2.c000066400000000000000000000613471265551621300161060ustar00rootroot00000000000000/* * Copyright (c) 2003-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* The sys_priv.h include must be before the lxt2_write.h include! */ # include "sys_priv.h" # include "lxt2_write.h" # include "vcd_priv.h" /* * This file contains the implementations of the LXT2 related functions. */ # include # include # include # include # include "stringheap.h" # include # include "ivl_alloc.h" static char *dump_path = NULL; static struct lxt2_wr_trace *dump_file = NULL; static void* lxt2_thread(void*arg); /* * Manage a table of all the dump-enabled vcd item. The cells of this * table are allocated incrementally, but are released all at once, so * we can allocate the items in chunks. */ struct vcd_info { vpiHandle item; vpiHandle cb; struct lxt2_wr_symbol *sym; struct vcd_info *dmp_next; }; struct vcd_info_chunk { struct vcd_info_chunk*chunk_next; uint16_t chunk_fill; struct vcd_info data[0x10000]; }; struct vcd_info_chunk*info_chunk_list = 0; struct vcd_info*new_vcd_info(void) { struct vcd_info_chunk*cur_chunk = info_chunk_list; if (cur_chunk == 0 || cur_chunk->chunk_fill == 0xffff) { struct vcd_info_chunk*tmp = calloc(1, sizeof(struct vcd_info_chunk)); tmp->chunk_fill = 0; tmp->chunk_next = cur_chunk; info_chunk_list = cur_chunk = tmp; return info_chunk_list->data + 0; } cur_chunk->chunk_fill += 1; struct vcd_info*ptr = cur_chunk->data + cur_chunk->chunk_fill; return ptr; } void functor_all_vcd_info( void (*fun) (struct vcd_info*info) ) { struct vcd_info_chunk*cur; for (cur = info_chunk_list ; cur ; cur = cur->chunk_next) { int idx; struct vcd_info*ptr; for (idx=0, ptr=cur->data ; idx <= cur->chunk_fill ; idx += 1, ptr += 1) fun (ptr); } } void delete_all_vcd_info(void) { while (info_chunk_list) { struct vcd_info_chunk*tmp = info_chunk_list->chunk_next; free(info_chunk_list); info_chunk_list = tmp; } } /* * Use this pointer to keep a list of vcd_info items that are * scheduled to be dumped. Use the VCD_INFO_ENDP value as a trailer * pointer instead of 0 so that individual vcd_info objects can tell * if they are in the list already, even if they are at the end of the * list. */ # define VCD_INFO_ENDP ((struct vcd_info*)1) static struct vcd_info *vcd_dmp_list = VCD_INFO_ENDP; static struct t_vpi_time vcd_dmp_time; static PLI_UINT64 vcd_cur_time = 0; static int dump_is_off = 0; static long dump_limit = 0; static int dump_is_full = 0; static int finish_status = 0; static enum lxm_optimum_mode_e { LXM_NONE = 0, LXM_SPACE = 1, LXM_SPEED = 2 } lxm_optimum_mode = LXM_SPEED; static off_t lxt2_file_size_limit = 0x40000000UL; /* * The lxt_scope head and current pointers are used to keep a scope * stack that can be accessed from the bottom. The lxt_scope_head * points to the first (bottom) item in the stack and * lxt_scope_current points to the last (top) item in the stack. The * push_scope and pop_scope methods manipulate the stack. */ struct lxt_scope { struct lxt_scope *next, *prev; char *name; int len; }; static struct lxt_scope *lxt_scope_head=NULL, *lxt_scope_current=NULL; static void push_scope(const char *name) { struct lxt_scope *t = (struct lxt_scope *) calloc(1, sizeof(struct lxt_scope)); t->name = strdup(name); t->len = strlen(name); if(!lxt_scope_head) { lxt_scope_head = lxt_scope_current = t; } else { lxt_scope_current->next = t; t->prev = lxt_scope_current; lxt_scope_current = t; } } static void pop_scope(void) { struct lxt_scope *t; assert(lxt_scope_current); t=lxt_scope_current->prev; free(lxt_scope_current->name); free(lxt_scope_current); lxt_scope_current = t; if (lxt_scope_current) { lxt_scope_current->next = 0; } else { lxt_scope_head = 0; } } /* * This function uses the scope stack to generate a hierarchical * name. Scan the scope stack from the bottom up to construct the * name. */ static char *create_full_name(const char *name) { char *n, *n2; int len = 0; int is_esc_id = is_escaped_id(name); struct lxt_scope *t = lxt_scope_head; /* Figure out how long the combined string will be. */ while(t) { len+=t->len+1; t=t->next; } len += strlen(name) + 1; if (is_esc_id) len += 1; /* Allocate a string buffer. */ n = n2 = malloc(len); t = lxt_scope_head; while(t) { strcpy(n2, t->name); n2 += t->len; *n2 = '.'; n2++; t=t->next; } if (is_esc_id) { *n2 = '\\'; n2++; } strcpy(n2, name); n2 += strlen(n2); assert( (n2 - n + 1) == len ); return n; } static void show_this_item(struct vcd_info*info) { s_vpi_value value; if (vpi_get(vpiType, info->item) == vpiRealVar) { value.format = vpiRealVal; vpi_get_value(info->item, &value); vcd_work_emit_double(info->sym, value.value.real); } else { value.format = vpiBinStrVal; vpi_get_value(info->item, &value); vcd_work_emit_bits(info->sym, value.value.str); } } static void show_this_item_x(struct vcd_info*info) { if (vpi_get(vpiType,info->item) == vpiRealVar) { /* Should write a NaN here? */ } else { vcd_work_emit_bits(info->sym, "x"); } } static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */ static PLI_UINT64 dumpvars_time; __inline__ static int dump_header_pending(void) { return dumpvars_status != 2; } /* * This function writes out all the traced variables, whether they * changed or not. */ static void vcd_checkpoint(void) { functor_all_vcd_info( show_this_item ); } static void vcd_checkpoint_x(void) { functor_all_vcd_info( show_this_item_x ); } static PLI_INT32 variable_cb_2(p_cb_data cause) { assert(cause->time->type == vpiSimTime); PLI_UINT64 now = timerec_to_time64(cause->time); if (now != vcd_cur_time) { vcd_work_set_time(now); vcd_cur_time = now; } while (vcd_dmp_list != VCD_INFO_ENDP) { struct vcd_info* info = vcd_dmp_list; show_this_item(info); vcd_dmp_list = info->dmp_next; info->dmp_next = 0; } return 0; } static PLI_INT32 variable_cb_1(p_cb_data cause) { struct t_cb_data cb; struct vcd_info*info = (struct vcd_info*)cause->user_data; if (dump_is_full) return 0; if (dump_is_off) return 0; if (dump_header_pending()) return 0; if (info->dmp_next) return 0; if ((dump_limit > 0) && (ftell(dump_file->handle) > dump_limit)) { dump_is_full = 1; vpi_printf("WARNING: Dump file limit (%ld bytes) " "exceeded.\n", dump_limit); return 0; } if (vcd_dmp_list == VCD_INFO_ENDP) { vcd_dmp_time.type = vpiSuppressTime; cb = *cause; cb.time = &vcd_dmp_time; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); } info->dmp_next = vcd_dmp_list; vcd_dmp_list = info; return 0; } static PLI_INT32 dumpvars_cb(p_cb_data cause) { if (dumpvars_status != 1) return 0; dumpvars_status = 2; dumpvars_time = timerec_to_time64(cause->time); vcd_cur_time = dumpvars_time; if (!dump_is_off) { vcd_work_set_time(dumpvars_time); vcd_checkpoint(); } return 0; } static PLI_INT32 finish_cb(p_cb_data cause) { if (finish_status != 0) return 0; finish_status = 1; dumpvars_time = timerec_to_time64(cause->time); if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) { vcd_work_set_time(dumpvars_time); } vcd_work_terminate(); delete_all_vcd_info(); vcd_scope_names_delete(); nexus_ident_delete(); free(dump_path); dump_path = 0; return 0; } __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; static struct t_vpi_time now; if (dumpvars_status == 1) return 0; if (dumpvars_status == 2) { vpi_printf("LXT2 warning: $dumpvars ignored, previously" " called at simtime %" PLI_UINT64_FMT "\n", dumpvars_time); return 1; } now.type = vpiSimTime; cb.time = &now; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; cb.obj = 0x0; vpi_register_cb(&cb); cb.reason = cbEndOfSimulation; cb.cb_rtn = finish_cb; vpi_register_cb(&cb); dumpvars_status = 1; return 0; } static PLI_INT32 sys_dumpoff_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; dump_is_off = 1; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { vcd_work_set_time(now64); vcd_cur_time = now64; } vcd_work_dumpoff(); vcd_checkpoint_x(); return 0; } static PLI_INT32 sys_dumpon_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (!dump_is_off) return 0; dump_is_off = 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { vcd_work_set_time(now64); vcd_cur_time = now64; } vcd_work_dumpon(); vcd_checkpoint(); return 0; } static PLI_INT32 sys_dumpall_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { vcd_work_set_time(now64); vcd_cur_time = now64; } vcd_checkpoint(); return 0; } static void *close_dumpfile(void) { vcd_work_terminate(); lxt2_wr_close(dump_file); dump_file = NULL; return NULL; } static void open_dumpfile(vpiHandle callh) { off_t use_file_size_limit = lxt2_file_size_limit; if (dump_path == 0) dump_path = strdup("dump.lx2"); dump_file = lxt2_wr_init(dump_path); if (getenv("LXT_FILE_SIZE_LIMIT")) { const char*limit_string = getenv("LXT_FILE_SIZE_LIMIT"); char*ep; use_file_size_limit = strtoul(limit_string,&ep,0); if (use_file_size_limit == 0 || ep[0] != 0) { vpi_printf("LXT2 Warning: %s:%d: LXT_FILE_SIZE_LIMIT is invalid: %s\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), limit_string); use_file_size_limit = lxt2_file_size_limit; } } if (dump_file == 0) { vpi_printf("LXT2 Error: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); free(dump_path); dump_path = 0; return; } else { int prec = vpi_get(vpiTimePrecision, 0); vpi_printf("LXT2 info: dumpfile %s opened for output.\n", dump_path); assert(prec >= -15); lxt2_wr_set_timescale(dump_file, prec); lxt2_wr_set_initial_value(dump_file, 'x'); lxt2_wr_set_compression_depth(dump_file, 4); lxt2_wr_set_partial_on(dump_file, 1); lxt2_wr_set_break_size(dump_file, use_file_size_limit); vcd_work_start(lxt2_thread, 0); atexit((void(*)(void))close_dumpfile); } } static PLI_INT32 sys_dumpfile_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); char *path; /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { char msg[64]; snprintf(msg, sizeof(msg), "LXT2 warning: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s called after $dumpvars started,\n", msg, name); vpi_printf("%*s using existing file (%s).\n", (int) strlen(msg), " ", dump_path); vpi_free_object(argv); return 0; } path = get_filename(callh, name, vpi_scan(argv)); vpi_free_object(argv); if (! path) return 0; if (dump_path) { vpi_printf("LXT2 warning: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Overriding dump file %s with %s.\n", dump_path, path); free(dump_path); } dump_path = path; return 0; } /* * The LXT2 format is a binary format, but a $dumpflush causes what is * in the binary file at the moment to be consistent with itself so * that the waveforms can be read by another process. LXT2 normally * writes checkpoints out, but this makes it happen at a specific * time. */ static PLI_INT32 sys_dumpflush_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ if (dump_file) vcd_work_flush(); return 0; } static PLI_INT32 sys_dumplimit_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; (void)name; /* Parameter is not used. */ /* Get the value and set the dump limit. */ assert(argv); val.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &val); dump_limit = val.value.integer; vpi_free_object(argv); return 0; } static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; const char* name; const char* ident; int nexus_id; switch (vpi_get(vpiType, item)) { case vpiMemoryWord: if (vpi_get(vpiConstantSelect, item) == 0) { /* Turn a non-constant array word select into a * constant word select. */ vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: case vpiReg: case vpiNet: /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("LXT2 warning: dumping array word %s will " "conflict with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } if (skip || vpi_get(vpiAutomatic, item)) break; name = vpi_get_str(vpiName, item); nexus_id = vpi_get(_vpiNexusId, item); if (nexus_id) { ident = find_nexus_ident(nexus_id); } else { ident = 0; } if (!ident) { char*tmp = create_full_name(name); ident = strdup_sh(&name_heap, tmp); free(tmp); if (nexus_id) set_nexus_ident(nexus_id, ident); info = new_vcd_info(); info->item = item; info->sym = lxt2_wr_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiLeftRange, item), vpi_get(vpiRightRange, item), LXT2_WR_SYM_F_BITS); info->dmp_next = 0; cb.time = 0; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->cb = vpi_register_cb(&cb); } else { char *n = create_full_name(name); lxt2_wr_symbol_alias(dump_file, ident, n, vpi_get(vpiSize, item)-1, 0); free(n); } break; case vpiRealVar: if (skip || vpi_get(vpiAutomatic, item)) break; name = vpi_get_str(vpiName, item); { char*tmp = create_full_name(name); ident = strdup_sh(&name_heap, tmp); free(tmp); } info = new_vcd_info(); info->item = item; info->sym = lxt2_wr_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiSize, item)-1, 0, LXT2_WR_SYM_F_DOUBLE); info->dmp_next = 0; cb.time = 0; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->cb = vpi_register_cb(&cb); break; case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: if (depth > 0) { const char* fullname = vpi_get_str(vpiFullName, item); /* list of types to iterate upon */ static int types[] = { /* Value */ /* vpiNamedEvent, */ vpiNet, /* vpiParameter, */ vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiGenScope, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; int i; int nskip = vcd_scope_names_test(fullname); #if 0 vpi_printf("LXT2 info: scanning scope %s, %u levels\n", fullname, depth); #endif if (nskip) { vpi_printf("LXT2 warning: ignoring signals in " "previously scanned scope %s\n", fullname); } else { vcd_scope_names_add(fullname); } name = vpi_get_str(vpiName, item); push_scope(name); for (i=0; types[i]>0; i++) { vpiHandle hand; vpiHandle argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } pop_scope(); } break; default: vpi_printf("LXT2 warning: $dumpvars: Unsupported parameter " "type (%s)\n", vpi_get_str(vpiType, item)); } } static int draw_scope(vpiHandle item) { int depth; const char *name; vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) return 0; depth = 1 + draw_scope(scope); name = vpi_get_str(vpiName, scope); push_scope(name); return depth; } static PLI_INT32 sys_dumpvars_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle item; s_vpi_value value; unsigned depth = 0; (void)name; /* Parameter is not used. */ if (dump_file == 0) { open_dumpfile(callh); if (dump_file == 0) { if (argv) vpi_free_object(argv); return 0; } } if (install_dumpvars_callback()) { if (argv) vpi_free_object(argv); return 0; } /* Get the depth if it exists. */ if (argv) { value.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &value); depth = value.value.integer; } if (!depth) depth = 10000; /* This dumps all the modules in the design if none are given. */ if (!argv || !(item = vpi_scan(argv))) { argv = vpi_iterate(vpiModule, 0x0); assert(argv); /* There must be at least one top level module. */ item = vpi_scan(argv); } for ( ; item; item = vpi_scan(argv)) { int dep = draw_scope(item); scan_item(depth, item, 0); while (dep--) pop_scope(); } /* Most effective compression. */ if (lxm_optimum_mode == LXM_SPACE) { lxt2_wr_set_compression_depth(dump_file, 9); lxt2_wr_set_partial_off(dump_file); } return 0; } static void* lxt2_thread(void*arg) { /* Keep track of the current time, and only call the set_time function when the time changes. */ uint64_t cur_time = 0; int run_flag = 1; (void)arg; /* Parameter is not used. */ while (run_flag) { struct vcd_work_item_s*cell = vcd_work_thread_peek(); if (cell->time != cur_time) { cur_time = cell->time; lxt2_wr_set_time64(dump_file, cur_time); } switch (cell->type) { case WT_NONE: break; case WT_FLUSH: lxt2_wr_flush(dump_file); break; case WT_DUMPON: lxt2_wr_set_dumpon(dump_file); break; case WT_DUMPOFF: lxt2_wr_set_dumpoff(dump_file); break; case WT_EMIT_DOUBLE: lxt2_wr_emit_value_double(dump_file, cell->sym_.lxt2, 0, cell->op_.val_double); break; case WT_EMIT_BITS: lxt2_wr_emit_value_bit_string(dump_file, cell->sym_.lxt2, 0, cell->op_.val_char); break; case WT_TERMINATE: run_flag = 0; break; } vcd_work_thread_pop(); } return 0; } void sys_lxt2_register(void) { int idx; struct t_vpi_vlog_info vlog_info; s_vpi_systf_data tf_data; vpiHandle res; /* Scan the extended arguments, looking for lxt2 optimization flags. */ vpi_get_vlog_info(&vlog_info); /* The "speed" option is not used in this dumper. */ for (idx = 0 ; idx < vlog_info.argc ; idx += 1) { if (strcmp(vlog_info.argv[idx],"-lxt2-space") == 0) { lxm_optimum_mode = LXM_SPACE; } else if (strcmp(vlog_info.argv[idx],"-lxt2-speed") == 0) { lxm_optimum_mode = LXM_SPEED; } else if (strcmp(vlog_info.argv[idx],"-lx2-space") == 0) { lxm_optimum_mode = LXM_SPACE; } else if (strcmp(vlog_info.argv[idx],"-lx2-speed") == 0) { lxm_optimum_mode = LXM_SPEED; } } /* All the compiletf routines are located in vcd_priv.c. */ tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpvars"; tf_data.calltf = sys_dumpvars_calltf; tf_data.compiletf = sys_dumpvars_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpvars"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_plusargs.c000066400000000000000000000300321265551621300170400ustar00rootroot00000000000000/* * Copyright (c) 2002-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include /* * Compare the +arguments passed to the simulator with the argument * passed to the $test$plusargs. If there is a simulator argument that * is like this argument, then return true. Otherwise return false. */ static PLI_INT32 sys_test_plusargs_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_value val; s_vpi_vlog_info info; int idx; int flag = 0; size_t slen, len; (void)name; /* Parameter is not used. */ vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); val.format = vpiStringVal; vpi_get_value(vpi_scan(argv), &val); slen = strlen(val.value.str); vpi_get_vlog_info(&info); /* Look for a +arg that matches the prefix supplied. */ for (idx = 0 ; idx < info.argc ; idx += 1) { /* Skip arguments that are not +args. */ if (info.argv[idx][0] != '+') continue; len = strlen(info.argv[idx]+1); if (len < slen) continue; if (strncmp(val.value.str, info.argv[idx]+1, slen) != 0) continue; flag = 1; break; } val.format = vpiIntVal; val.value.integer = flag; vpi_put_value(callh, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_value_plusargs_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the first argument is a string. */ arg = vpi_scan(argv); assert(arg != 0); if ( ! is_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a string.\n", name); vpi_control(vpiFinish, 1); return 0; } arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's requires a second variable argument.\n", name); vpi_control(vpiFinish, 1); return 0; } switch (vpi_get(vpiType, arg)) { case vpiReg: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiTimeVar: case vpiStringVar: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be a variable, found a %s.\n", name, vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); return 0; } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } static PLI_INT32 sys_value_plusargs_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_vlog_info info; s_vpi_value fmt; s_vpi_value res; char msg[64]; char*cp; int idx; int flag = 0; size_t slen, len; vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); fmt.format = vpiStringVal; vpi_get_value(vpi_scan(argv), &fmt); /* Check for the start of a format string. */ cp = strchr(fmt.value.str, '%'); if (cp == 0) { snprintf(msg, sizeof(msg), "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s is missing a format code.\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", fmt.value.str); vpi_control(vpiFinish, 1); return 0; } /* This is the length of string we will look for. */ slen = cp - fmt.value.str; /* Skip a zero. */ cp += 1; if (*cp == '0') cp += 1; /* Check the format code. */ switch (*cp) { case 'd': case 'D': case 'o': case 'O': case 'h': case 'H': case 'x': case 'X': case 'b': case 'B': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 's': case 'S': break; default: snprintf(msg, sizeof(msg), "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s has an invalid format string:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", fmt.value.str); vpi_control(vpiFinish, 1); return 0; } /* Warn if there is any trailing garbage. */ if (*(cp+1) != '\0') { snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Skipping trailing garbage in %s's format string:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", fmt.value.str); *(cp+1) = '\0'; } vpi_get_vlog_info(&info); /* Look for a +arg that matches the prefix supplied. */ for (idx = 0 ; idx < info.argc ; idx += 1) { char*sp, *tp, *end; size_t sp_len; /* Skip arguments that are not +args. */ if (info.argv[idx][0] != '+') continue; len = strlen(info.argv[idx]+1); if (len < slen) continue; if (strncmp(fmt.value.str, info.argv[idx]+1, slen) != 0) continue; sp = info.argv[idx]+1+slen; sp_len = strlen(sp); switch (*cp) { case 'd': case 'D': res.format = vpiDecStrVal; /* A decimal string can set the value to "x" or "z". */ if (sp_len == strspn(sp, "xX_") || sp_len == strspn(sp, "zZ_")) { res.value.str = sp; /* A decimal string must contain only these characters. * A decimal string can not start with an "_" character. * A "-" can only be at the start of the string. */ } else if (sp_len != strspn(sp, "-0123456789_") || *sp == '_' || ((tp = strrchr(sp, '-')) && tp != sp)) { res.value.str = "x"; snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Invalid decimal value passed to %s:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", sp); } else { res.value.str = sp; } break; case 'o': case 'O': res.format = vpiOctStrVal; /* An octal string must contain only these characters. * An octal string can not start with an "_" character. * A "-" can only be at the start of the string. */ if (sp_len != strspn(sp, "-01234567_xXzZ") || *sp == '_' || ((tp = strrchr(sp, '-')) && tp != sp)) { res.value.str = "x"; snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Invalid octal value passed to %s:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", sp); } else { res.value.str = sp; } break; case 'h': case 'H': case 'x': case 'X': res.format = vpiHexStrVal; /* A hex. string must contain only these characters. * A hex. string can not start with an "_" character. * A "-" can only be at the start of the string. */ if (sp_len != strspn(sp, "-0123456789aAbBcCdDeEfF_xXzZ") || *sp == '_' || ((tp = strrchr(sp, '-')) && tp != sp)) { res.value.str = "x"; snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Invalid hex value passed to %s:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", sp); } else { res.value.str = sp; } break; case 'b': case 'B': res.format = vpiBinStrVal; /* A binary string must contain only these characters. * A binary string can not start with an "_" character. * A "-" can only be at the start of the string. */ if (sp_len != strspn(sp, "-01_xXzZ") || *sp == '_' || ((tp = strrchr(sp, '-')) && tp != sp)) { res.value.str = "x"; snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Invalid binary value passed to %s:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", sp); } else { res.value.str = sp; } break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': res.format = vpiRealVal; res.value.real = strtod(sp, &end); /* If we didn't get a full conversion print a warning. */ if (*end) { /* We had an invalid value passed. */ if (end == sp) { snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Invalid real value passed to " "%s:\n", msg, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", sp); /* We have extra garbage at the end. */ } else { snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s Extra character(s) \"%s\" found " "in %s's real string:\n", msg, end, name); vpi_printf("%*s \"%s\".\n", (int)strlen(msg), " ", sp); } } break; case 's': case 'S': res.format = vpiStringVal; res.value.str = sp; break; default: assert(0); } vpi_put_value(vpi_scan(argv), &res, 0, vpiNoDelay); flag = 1; break; } res.format = vpiIntVal; res.value.integer = flag; vpi_put_value(callh, &res, 0, vpiNoDelay); vpi_free_object(argv); return 0; } void sys_plusargs_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$test$plusargs"; tf_data.calltf = sys_test_plusargs_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$test$plusargs"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$value$plusargs"; tf_data.calltf = sys_value_plusargs_calltf; tf_data.compiletf = sys_value_plusargs_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$value$plusargs"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_priv.c000066400000000000000000000266371265551621300162000ustar00rootroot00000000000000/* * Copyright (c) 2003-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sys_priv.h" #include #include #include #include #include "ivl_alloc.h" PLI_UINT64 timerec_to_time64(const struct t_vpi_time*timerec) { PLI_UINT64 tmp; tmp = timerec->high; tmp <<= 32; tmp |= (PLI_UINT64) timerec->low; return tmp; } char *as_escaped(char *arg) { unsigned idx, cur, cnt, len = strlen(arg); char *res = (char *) malloc(sizeof(char *) * len); cur = 0; cnt = len; for (idx = 0; idx < cnt; idx++) { if (isprint((int)arg[idx])) { res[cur] = arg[idx]; cur += 1; } else { len += 3; res = (char *) realloc(res, sizeof(char *) * len); sprintf(&(res[cur]), "\\%03o", arg[idx]); cur += 4; } } res[cur] = 0; return res; } /* * Generic routine to get the filename from the given handle. * The result is duplicated so call free when the name is no * longer needed. Returns 0 (NULL) for an error. */ char *get_filename(vpiHandle callh, const char *name, vpiHandle file) { s_vpi_value val; unsigned len, idx; /* Get the filename. */ val.format = vpiStringVal; vpi_get_value(file, &val); /* Verify that we have a string and that it is not NULL. */ if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's file name argument (%s) is not a valid string.\n", name, vpi_get_str(vpiType, file)); return 0; } /* * Verify that the file name is composed of only printable * characters. */ len = strlen(val.value.str); for (idx = 0; idx < len; idx++) { if (! isprint((int)val.value.str[idx])) { char msg[64]; char *esc_fname = as_escaped(val.value.str); snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s's file name argument contains non-" "printable characters.\n", msg, name); vpi_printf("%*s \"%s\"\n", (int) strlen(msg), " ", esc_fname); free(esc_fname); return 0; } } return strdup(val.value.str); } void check_for_extra_args(vpiHandle argv, vpiHandle callh, const char *name, const char *arg_str, unsigned opt) { /* Check that there are no extra arguments. */ if (vpi_scan(argv) != 0) { char msg[64]; unsigned argc; snprintf(msg, sizeof(msg), "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; argc = 1; while (vpi_scan(argv)) argc += 1; vpi_printf("%s %s takes %s%s.\n", msg, name, opt ? "at most ": "", arg_str); vpi_printf("%*s Found %u extra argument%s.\n", (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); vpi_control(vpiFinish, 1); } } /* * This routine returns 1 if the argument is a constant value, * otherwise it returns 0. * * This routine was also copied to sys_clog2.c since it is not * part of the standard system functions. */ unsigned is_constant_obj(vpiHandle obj) { unsigned rtn = 0; assert(obj); switch(vpi_get(vpiType, obj)) { case vpiConstant: case vpiParameter: rtn = 1; break; } return rtn; } /* * This routine returns 1 if the argument supports has a numeric value, * otherwise it returns 0. */ unsigned is_numeric_obj(vpiHandle obj) { unsigned rtn = 0; assert(obj); switch(vpi_get(vpiType, obj)) { case vpiConstant: case vpiParameter: /* These cannot be a string constant. */ if (vpi_get(vpiConstType, obj) != vpiStringConst) rtn = 1; break; /* These can have a valid numeric value. */ case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiMemoryWord: case vpiNet: case vpiPartSelect: case vpiRealVar: case vpiReg: case vpiTimeVar: rtn = 1; break; } return rtn; } /* * This routine returns 1 if the argument supports a valid string value, * otherwise it returns 0. */ unsigned is_string_obj(vpiHandle obj) { unsigned rtn = 0; assert(obj); switch(vpi_get(vpiType, obj)) { case vpiConstant: case vpiParameter: { /* These must be a string or binary constant. */ PLI_INT32 ctype = vpi_get(vpiConstType, obj); if (ctype == vpiStringConst || ctype == vpiBinaryConst) rtn = 1; break; } /* These can have a valid string value. */ case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiMemoryWord: case vpiNet: case vpiPartSelect: case vpiReg: case vpiTimeVar: case vpiStringVar: rtn = 1; break; } return rtn; } /* * Find the enclosing module. */ vpiHandle sys_func_module(vpiHandle obj) { assert(obj); while (vpi_get(vpiType, obj) != vpiModule) { obj = vpi_handle(vpiScope, obj); assert(obj); } return obj; } /* * Standard compiletf routines. */ /* For system tasks/functions that do not take an argument. */ PLI_INT32 sys_no_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); /* Make sure there are no arguments. */ if (argv != 0) { char msg[64]; unsigned argc; snprintf(msg, sizeof(msg), "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; argc = 0; while (vpi_scan(argv)) argc += 1; vpi_printf("%s %s does not take an argument.\n", msg, name); vpi_printf("%*s Found %u extra argument%s.\n", (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); vpi_control(vpiFinish, 1); } return 0; } /* For system tasks/functions that take a single numeric argument. */ PLI_INT32 sys_one_numeric_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); /* Check that there is an argument and that it is numeric. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a single numeric argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "a single numeric argument", 0); return 0; } /* For system tasks/functions that take a single optional numeric argument. */ PLI_INT32 sys_one_opt_numeric_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); /* The argument is optional so just return if none are found. */ if (argv == 0) return 0; if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "one numeric argument", 1); return 0; } /* For system tasks/functions that take two numeric arguments. */ PLI_INT32 sys_two_numeric_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are two argument and that they are numeric. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two numeric arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (numeric) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two numeric arguments", 0); return 0; } /* For system tasks/functions that take a single string argument. */ PLI_INT32 sys_one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); /* Check that there is an argument and that it is a string. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a single string argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_string_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's argument must be a string.\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "a single string argument", 0); return 0; } iverilog-10_1/vpi/sys_priv.h000066400000000000000000000046141265551621300161740ustar00rootroot00000000000000#ifndef IVL_sys_priv_H #define IVL_sys_priv_H /* * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vpi_config.h" #include "sv_vpi_user.h" /* * Context structure for PRNG in mt19937int.c */ struct context_s { int mti; /* the array for the state vector */ unsigned long mt[1023]; /* mti==N+1 means mt[N] is not init */ }; extern void sgenrand(struct context_s *context, unsigned long seed); extern unsigned long genrand(struct context_s *context); extern PLI_UINT64 timerec_to_time64(const struct t_vpi_time*timerec); extern char *as_escaped(char *arg); extern char *get_filename(vpiHandle callh, const char *name, vpiHandle file); extern void check_for_extra_args(vpiHandle argv, vpiHandle callh, const char *name, const char *arg_str, unsigned opt); struct timeformat_info_s { int units; unsigned prec; char*suff; unsigned width; }; extern struct timeformat_info_s timeformat_info; extern unsigned is_constant_obj(vpiHandle obj); extern unsigned is_numeric_obj(vpiHandle obj); extern unsigned is_string_obj(vpiHandle obj); extern vpiHandle sys_func_module(vpiHandle obj); /* * The standard compiletf routines. */ extern PLI_INT32 sys_no_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_one_numeric_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_one_opt_numeric_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_two_numeric_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); #endif /* IVL_sys_priv_H */ iverilog-10_1/vpi/sys_queue.c000066400000000000000000001256731265551621300163440ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "sys_priv.h" #include #include #include #include "ivl_alloc.h" /* * The two queue types. */ #define IVL_QUEUE_FIFO 1 #define IVL_QUEUE_LIFO 2 /* * The statistical codes that can be passed to $q_exam(). */ #define IVL_QUEUE_LENGTH 1 #define IVL_QUEUE_MEAN 2 #define IVL_QUEUE_MAX_LENGTH 3 #define IVL_QUEUE_SHORTEST 4 #define IVL_QUEUE_LONGEST 5 #define IVL_QUEUE_AVERAGE 6 /* * All the values that can be returned by the queue tasks/function. */ #define IVL_QUEUE_OK 0 #define IVL_QUEUE_FULL 1 #define IVL_QUEUE_UNDEFINED_ID 2 #define IVL_QUEUE_EMPTY 3 #define IVL_QUEUE_UNSUPPORTED_TYPE 4 #define IVL_QUEUE_INVALID_LENGTH 5 #define IVL_QUEUE_DUPLICATE_ID 6 #define IVL_QUEUE_OUT_OF_MEMORY 7 /* Icarus specific status codes. */ #define IVL_QUEUE_UNDEFINED_STAT_CODE 8 #define IVL_QUEUE_VALUE_OVERFLOWED 9 #define IVL_QUEUE_NO_STATISTICS 10 /* * Routine to add the given time to the the total time (high/low). */ static void add_to_wait_time(uint64_t *high, uint64_t *low, uint64_t c_time) { uint64_t carry = 0U; if ((UINT64_MAX - *low) < c_time) carry = 1U; *low += c_time; assert((carry == 0U) || (*high < UINT64_MAX)); *high += carry; } /* * Routine to divide the given total time (high/low) by the number of * items to get the average. */ static uint64_t calc_average_wait_time(uint64_t high, uint64_t low, uint64_t total) { int bit = 64; uint64_t result = 0U; assert(total != 0U); if (high == 0U) return (low/total); /* This is true by design, but since we can only return 64 bits * make sure nothing went wrong. */ assert(high < total); /* It's a big value so calculate the average the long way. */ do { unsigned carry = 0U; /* Copy bits from low to high until we have a bit to place * in the result or there are no bits left. */ while ((bit >= 0) && (high < total) && !carry) { /* If the MSB is set then we will have a carry. */ if (high > (UINT64_MAX >> 1)) carry = 1U; high <<= 1; high |= (low & 0x8000000000000000) != 0; low <<= 1; bit -= 1; } /* If this is a valid bit, set the appropriate bit in the * result and subtract the total from the current value. */ if (bit >= 0) { result |= UINT64_C(1) << bit; high = high - total; } /* Loop until there are no bits left. */ } while (bit > 0); return result; } /* * The data structure used for an individual queue element. It hold four * state result for the jobs and inform fields along with the time that * the element was added in base time units. */ typedef struct t_ivl_queue_elem { uint64_t time; s_vpi_vecval job; s_vpi_vecval inform; } s_ivl_queue_elem, *p_ivl_queue_elem; /* * This structure is used to represent a specific queue. The time * information is in base simulation units. */ typedef struct t_ivl_queue_base { uint64_t shortest_wait_time; uint64_t first_add_time; uint64_t latest_add_time; uint64_t wait_time_high; uint64_t wait_time_low; uint64_t number_of_adds; p_ivl_queue_elem queue; PLI_INT32 id; PLI_INT32 length; PLI_INT32 type; PLI_INT32 head; PLI_INT32 elems; PLI_INT32 max_len; PLI_INT32 have_shortest_statistic; } s_ivl_queue_base, *p_ivl_queue_base; /* * For now we keep the queues in a vector since there are likely not too many * of them. We may need something more efficient later. */ static p_ivl_queue_base base = NULL; static int64_t base_len = 0; /* * This routine is called at the end of simulation to free the queue memory. */ static PLI_INT32 cleanup_queue(p_cb_data cause) { PLI_INT32 idx; (void) cause; /* Unused argument. */ for (idx = 0; idx < base_len; idx += 1) free(base[idx].queue); free(base); base = NULL; base_len = 0; return 0; } /* * Add a new queue to the list, return 1 if there is not enough memory, * otherwise return 0. */ static unsigned create_queue(PLI_INT32 id, PLI_INT32 type, PLI_INT32 length) { p_ivl_queue_base new_base; p_ivl_queue_elem queue; /* Allocate space for the new queue base. */ base_len += 1; new_base = (p_ivl_queue_base) realloc(base, base_len*sizeof(s_ivl_queue_base)); /* If we ran out of memory then fix the length and return a fail. */ if (new_base == NULL) { base_len -= 1; return 1; } base = new_base; /* Allocate space for the queue elements. */ queue = (p_ivl_queue_elem) malloc(length*sizeof(s_ivl_queue_elem)); /* If we ran out of memory then fix the length and return a fail. */ if (queue == NULL) { base_len -= 1; return 1; } /* The memory was allocated so configure it. */ base[base_len-1].queue = queue; base[base_len-1].id = id; base[base_len-1].length = length; base[base_len-1].type = type; base[base_len-1].head = 0; base[base_len-1].elems = 0; base[base_len-1].max_len = 0; base[base_len-1].shortest_wait_time = UINT64_MAX; base[base_len-1].first_add_time = 0U; base[base_len-1].latest_add_time = 0U; base[base_len-1].wait_time_high = 0U; base[base_len-1].wait_time_low = 0U; base[base_len-1].number_of_adds = 0U; base[base_len-1].have_shortest_statistic = 0; return 0; } /* * Check to see if the given queue is full. */ static unsigned is_queue_full(int64_t idx) { if (base[idx].elems >= base[idx].length) return 1; return 0; } /* * Add the job and inform to the queue. Return 1 if the queue is full, * otherwise return 0. */ static unsigned add_to_queue(int64_t idx, p_vpi_vecval job, p_vpi_vecval inform) { PLI_INT32 length = base[idx].length; PLI_INT32 type = base[idx].type; PLI_INT32 head = base[idx].head; PLI_INT32 elems = base[idx].elems; PLI_INT32 loc; s_vpi_time cur_time; uint64_t c_time; assert(elems <= length); /* If the queue is full we can't add anything. */ if (elems == length) return 1; /* Increment the number of element since one will be added.*/ base[idx].elems += 1; /* Save the job and inform to the queue. */ if (type == IVL_QUEUE_LIFO) { assert(head == 0); /* For a LIFO head must always be zero. */ loc = elems; } else { assert(type == IVL_QUEUE_FIFO); loc = head + elems; if (loc >= length) loc -= length; } base[idx].queue[loc].job.aval = job->aval; base[idx].queue[loc].job.bval = job->bval; base[idx].queue[loc].inform.aval = inform->aval; base[idx].queue[loc].inform.bval = inform->bval; /* Save the current time with this entry for the statistics. */ cur_time.type = vpiSimTime; vpi_get_time(NULL, &cur_time); c_time = cur_time.high; c_time <<= 32; c_time |= cur_time.low; base[idx].queue[loc].time = c_time; /* Increment the maximum length if needed. */ if (base[idx].max_len == elems) base[idx].max_len += 1; /* Update the inter-arrival statistics. */ assert(base[idx].number_of_adds < UINT64_MAX); base[idx].number_of_adds += 1; if (base[idx].number_of_adds == 1) base[idx].first_add_time = c_time; base[idx].latest_add_time = c_time; return 0; } /* * Get the job and inform values from the queue. Return 1 if the queue is * empty, otherwise return 0. */ static unsigned remove_from_queue(int64_t idx, p_vpi_vecval job, p_vpi_vecval inform) { PLI_INT32 type = base[idx].type; PLI_INT32 head = base[idx].head; PLI_INT32 elems = base[idx].elems - 1; PLI_INT32 loc; s_vpi_time cur_time; uint64_t c_time; assert(elems >= -1); /* If the queue is empty we can't remove anything. */ if (elems < 0) return 1; /* Decrement the number of element in the queue structure since one * will be removed.*/ base[idx].elems -= 1; /* Remove the job and inform from the queue. */ if (type == IVL_QUEUE_LIFO) { assert(head == 0); /* For a LIFO head must always be zero. */ loc = elems; } else { assert(type == IVL_QUEUE_FIFO); loc = head; if (head + 1 == base[idx].length) base[idx].head = 0; else base[idx].head += 1; } job->aval = base[idx].queue[loc].job.aval; job->bval = base[idx].queue[loc].job.bval; inform->aval = base[idx].queue[loc].inform.aval; inform->bval = base[idx].queue[loc].inform.bval; /* Get the current simulation time. */ cur_time.type = vpiSimTime; vpi_get_time(NULL, &cur_time); c_time = cur_time.high; c_time <<= 32; c_time |= cur_time.low; /* Set the shortest wait time if needed. */ assert(c_time >= base[idx].queue[loc].time); c_time -= base[idx].queue[loc].time; if (c_time < base[idx].shortest_wait_time) { base[idx].shortest_wait_time = c_time; } base[idx].have_shortest_statistic = 1; /* Add the current element wait time to the total wait time. */ add_to_wait_time(&(base[idx].wait_time_high), &(base[idx].wait_time_low), c_time); return 0; } /* * Return the current queue length. */ static PLI_INT32 get_current_queue_length(int64_t idx) { return base[idx].elems; } /* * Return the maximum queue length. */ static PLI_INT32 get_maximum_queue_length(int64_t idx) { return base[idx].max_len; } /* * Return the longest wait time in the queue in base simulation units. * Make sure to check that there are elements in the queue before calling * this routine. The caller will need to scale the time as appropriate. */ static uint64_t get_longest_queue_time(int64_t idx) { s_vpi_time cur_time; uint64_t c_time; /* Get the current simulation time. */ cur_time.type = vpiSimTime; vpi_get_time(NULL, &cur_time); c_time = cur_time.high; c_time <<= 32; c_time |= cur_time.low; /* Subtract the element with the longest time (the head) from the * current time. */ assert(c_time >= base[idx].queue[base[idx].head].time); c_time -= base[idx].queue[base[idx].head].time; return c_time; } /* * Check to see if there are inter-arrival time statistics. */ static unsigned have_interarrival_statistic(int64_t idx) { return (base[idx].number_of_adds >= 2U); } /* * Return the mean inter-arrival time for the queue. This is just the * latest add time minus the first add time divided be the number of time * deltas (the number of adds - 1). */ static uint64_t get_mean_interarrival_time(int64_t idx) { return ((base[idx].latest_add_time - base[idx].first_add_time) / (base[idx].number_of_adds - 1U)); } /* * Check to see if there are shortest wait time statistics. */ static unsigned have_shortest_wait_statistic(int64_t idx) { return (base[idx].have_shortest_statistic != 0); } /* * Return the shortest amount of time an element has waited in the queue. */ static uint64_t get_shortest_wait_time(int64_t idx) { return base[idx].shortest_wait_time; } /* * Check to see if we have an average wait time statistics. */ static unsigned have_average_wait_statistic(int64_t idx) { return (base[idx].number_of_adds >= 1U); } /* * Return the average wait time in the queue. */ static uint64_t get_average_wait_time(int64_t idx) { PLI_INT32 length = base[idx].length; PLI_INT32 loc = base[idx].head; PLI_INT32 elems = base[idx].elems; PLI_INT32 count; /* Initialize the high and low time with the current total time. */ uint64_t high = base[idx].wait_time_high; uint64_t low = base[idx].wait_time_low; s_vpi_time cur_time; uint64_t c_time; /* Get the current simulation time. */ cur_time.type = vpiSimTime; vpi_get_time(NULL, &cur_time); c_time = cur_time.high; c_time <<= 32; c_time |= cur_time.low; /* For each element still in the queue, add its wait time to the * total wait time. */ for (count = 0; count < elems; count += 1) { uint64_t add_time = base[idx].queue[loc].time; assert(c_time >= add_time); add_to_wait_time(&high, &low, c_time-add_time); /* Move to the next element. */ loc += 1; if (loc == length) loc = 0; } /* Return the average wait time. */ return calc_average_wait_time(high, low, base[idx].number_of_adds); } /* * Check to see if the given id already exists. Return the index for the * queue if it exists, otherwise return -1. */ static int64_t get_id_index(PLI_INT32 id) { int64_t idx; for (idx = 0; idx < base_len; idx += 1) { if (id == base[idx].id) return idx; } return -1; } /* * Check to see if the given value is bit based and has 32 or fewer bits. */ static unsigned is_32_or_smaller_obj(vpiHandle obj) { PLI_INT32 const_type; unsigned rtn = 0; assert(obj); switch(vpi_get(vpiType, obj)) { case vpiConstant: case vpiParameter: const_type = vpi_get(vpiConstType, obj); if ((const_type != vpiRealConst) && (const_type != vpiStringConst)) rtn = 1; break; /* These can have valid 32 bit or smaller numeric values. */ case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiMemoryWord: case vpiNet: case vpiPartSelect: case vpiReg: rtn = 1; break; } /* The object must be 32 bits or smaller. */ if (vpi_get(vpiSize, obj) > 32) rtn = 0; return rtn; } /* * Check to see if the argument is a variable that is exactly 32 bits in size. */ static void check_var_arg_32(vpiHandle arg, vpiHandle callh, const char *name, const char *desc) { assert(arg); switch (vpi_get(vpiType, arg)) { case vpiMemoryWord: case vpiPartSelect: case vpiBitVar: case vpiReg: // Check that we have exactly 32 bits. if (vpi_get(vpiSize, arg) != 32) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's %s (variable) argument must be 32 bits.\n", name, desc); vpi_control(vpiFinish, 1); } case vpiIntegerVar: case vpiIntVar: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's %s argument must be a 32 bit variable.\n", name, desc); vpi_control(vpiFinish, 1); } } /* * Check to see if the argument is a variable of at least 32 bits. */ static void check_var_arg_large(vpiHandle arg, vpiHandle callh, const char *name, const char *desc) { assert(arg); switch (vpi_get(vpiType, arg)) { case vpiMemoryWord: case vpiPartSelect: case vpiBitVar: case vpiReg: // Check that we have at least 32 bits. if (vpi_get(vpiSize, arg) < 32) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's %s (variable) argument must have at least " "32 bits.\n", name, desc); vpi_control(vpiFinish, 1); } case vpiIntegerVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's %s argument must be a variable.\n", name, desc); vpi_control(vpiFinish, 1); } } /* * Check that the given number of arguments are numeric. */ static unsigned check_numeric_args(vpiHandle argv, unsigned count, vpiHandle callh, const char *name) { unsigned idx; /* Check that the first count arguments are numeric. Currently * only three are needed/supported. */ for (idx = 0; idx < count; idx += 1) { const char *loc = NULL; vpiHandle arg = vpi_scan(argv); /* Get the name for this argument. */ switch (idx) { case 0: loc = "first"; break; case 1: loc = "second"; break; case 2: loc = "third"; break; default: assert(0); } /* Check that there actually is an argument. */ if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a %s (<= 32 bit numeric) argument.\n", name, loc); vpi_control(vpiFinish, 1); return 1; } /* Check that it is no more than 32 bits. */ if (! is_32_or_smaller_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's %s argument must be numeric (<= 32 bits).\n", name, loc); vpi_control(vpiFinish, 1); } } return 0; } /* * Check to see if the given argument is valid (does not have any X/Z bits). * Return zero if it is valid and a positive value if it is invalid. */ static unsigned get_valid_32(vpiHandle arg, PLI_INT32 *value) { PLI_INT32 size, mask; s_vpi_value val; size = vpi_get(vpiSize, arg); /* The compiletf routine should have already verified that this is * <= 32 bits. */ assert((size <= 32) && (size > 0)); /* Create a mask so that we only check the appropriate bits. */ mask = UINT32_MAX >> (32 - size); /* Get the value and return the possible integer value in the value * variable. Return the b-value bits to indicate if the value is * undefined (has X/Z bit). */ val.format = vpiVectorVal; vpi_get_value(arg, &val); *value = val.value.vector->aval & mask; /* If the argument is signed and less than 32 bit we need to sign * extend the value. */ if (vpi_get(vpiSigned, arg) && (size < 32)) { if ((*value) & (1 << (size - 1))) *value |= ~mask; } return (val.value.vector->bval & mask); } static void get_four_state(vpiHandle arg, p_vpi_vecval vec) { PLI_INT32 size, mask; s_vpi_value val; size = vpi_get(vpiSize, arg); /* The compiletf routine should have already verified that this is * <= 32 bits. */ assert((size <= 32) && (size > 0)); /* Create a mask so that we only use the appropriate bits. */ mask = UINT32_MAX >> (32 - size); /* Get the bits for the argument and save them in the return value. */ val.format = vpiVectorVal; vpi_get_value(arg, &val); vec->aval = val.value.vector->aval & mask; vec->bval = val.value.vector->bval & mask; /* If the argument is signed and less than 32 bit we need to sign * extend the value. */ if (vpi_get(vpiSigned, arg) && (size < 32)) { if (vec->aval & (1 << (size - 1))) vec->aval |= ~mask; if (vec->bval & (1 << (size - 1))) vec->bval |= ~mask; } } /* * Fill the passed variable with x. */ static void fill_variable_with_x(vpiHandle var) { s_vpi_value val; PLI_INT32 words = ((vpi_get(vpiSize, var) - 1) / 32) + 1; PLI_INT32 idx; p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); assert(val_ptr); /* Fill the vector with X. */ for (idx = 0; idx < words; idx += 1) { val_ptr[idx].aval = 0xffffffff; val_ptr[idx].bval = 0xffffffff; } /* Put the vector to the variable. */ val.format = vpiVectorVal; val.value.vector = val_ptr; vpi_put_value(var, &val, 0, vpiNoDelay); free(val_ptr); } /* * Fill the passed variable with the passed value if it fits. If it doesn't * fit then set all bits to one and return that the value is too big instead * of the normal OK. The value is a time and needs to be scaled to the * calling module's timescale. */ static PLI_INT32 fill_variable_with_scaled_time(vpiHandle var, uint64_t c_time) { s_vpi_value val; PLI_INT32 size = vpi_get(vpiSize, var); PLI_INT32 is_signed = vpi_get(vpiSigned, var); PLI_INT32 words = ((size - 1) / 32) + 1; uint64_t max_val = 0; uint64_t scale = 1; uint64_t frac; PLI_INT32 rtn, units, prec; p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); assert(val_ptr); assert(size >= 32); assert(words > 0); /* Scale the variable to match the calling module's timescale. */ prec = vpi_get(vpiTimePrecision, 0); units = vpi_get(vpiTimeUnit, vpi_handle(vpiModule, var)); assert(units >= prec); while (units > prec) { scale *= 10; units -= 1; } frac = c_time % scale; c_time /= scale; if ((scale > 1) && (frac >= scale/2)) c_time += 1; /* Find the maximum value + 1 that can be put into the variable. */ if (size < 64) { max_val = 1; max_val <<= (size - is_signed); } /* If the time is too big to fit then return the maximum positive * value and that the value overflowed. Otherwise, return the time * and OK. */ if (max_val && (c_time >= max_val)) { /* For a single word only the MSB is cleared if signed. */ if (words == 1) { if (is_signed) { val_ptr[0].aval = 0x7fffffff; } else { val_ptr[0].aval = 0xffffffff; } val_ptr[0].bval = 0x00000000; /* For two words the lower word is filled with 1 and the top * word has a size dependent fill if signed. */ } else { assert(words == 2); val_ptr[0].aval = 0xffffffff; val_ptr[0].bval = 0x00000000; if (is_signed) { val_ptr[1].aval = ~(UINT32_MAX >> (size - 32)); } else { val_ptr[1].aval = 0xffffffff; } val_ptr[1].bval = 0x00000000; } rtn = IVL_QUEUE_VALUE_OVERFLOWED; } else { /* Fill the vector with 0. */ for (PLI_INT32 idx = 0; idx < words; idx += 1) { val_ptr[idx].aval = 0x00000000; val_ptr[idx].bval = 0x00000000; } /* Add the time to the vector. */ switch (words) { default: val_ptr[1].aval = (c_time >> 32) & 0xffffffff; case 1: val_ptr[0].aval = c_time & 0xffffffff; } rtn = IVL_QUEUE_OK; } /* Put the vector to the variable. */ val.format = vpiVectorVal; val.value.vector = val_ptr; vpi_put_value(var, &val, 0, vpiNoDelay); free(val_ptr); return rtn; } /* * Check that the given $q_initialize() call has valid arguments. */ static PLI_INT32 sys_q_initialize_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the first three arguments (the id, type and maximum * length) are numeric. */ if (check_numeric_args(argv, 3, callh, name)) return 0; /* The fourth argument (the status) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a fourth (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the status argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "fourth"); /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "four arguments", 0); return 0; } /* * The runtime code for $q_initialize(). */ static PLI_INT32 sys_q_initialize_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle status; PLI_INT32 id, type, length; s_vpi_value val; unsigned invalid_id, invalid_type, invalid_length; (void)name; /* Parameter is not used. */ /* Get the id. */ invalid_id = get_valid_32(vpi_scan(argv), &id); /* Get the queue type. */ invalid_type = get_valid_32(vpi_scan(argv), &type); /* Get the queue maximum length. */ invalid_length = get_valid_32(vpi_scan(argv), &length); /* Get the status variable. */ status = vpi_scan(argv); /* We are done with the argument iterator so free it. */ vpi_free_object(argv); /* If the id is invalid then return. */ if (invalid_id) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNDEFINED_ID; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Verify that the type is valid. */ if (invalid_type || ((type != IVL_QUEUE_FIFO) && (type != IVL_QUEUE_LIFO))) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNSUPPORTED_TYPE; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Verify that the queue length is greater than zero. */ if (invalid_length || (length <= 0)) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_INVALID_LENGTH; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Check that this is not a duplicate queue id. */ if (get_id_index(id) >= 0) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_DUPLICATE_ID; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Create the queue and fail if we do not have enough memory. */ if (create_queue(id, type, length)) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_OUT_OF_MEMORY; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* The queue was initialized correctly so return OK. */ val.format = vpiIntVal; val.value.integer = IVL_QUEUE_OK; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* * Check that the given $q_add() call has valid arguments. */ static PLI_INT32 sys_q_add_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the first three arguments (the id, job and information) * are numeric. */ if (check_numeric_args(argv, 3, callh, name)) return 0; /* The fourth argument (the status) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a fourth (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the status argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "fourth"); /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "four arguments", 0); return 0; } /* * The runtime code for $q_add(). */ static PLI_INT32 sys_q_add_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle status; PLI_INT32 id; int64_t idx; s_vpi_vecval job, inform; s_vpi_value val; unsigned invalid_id; (void)name; /* Parameter is not used. */ /* Get the id. */ invalid_id = get_valid_32(vpi_scan(argv), &id); /* Get the job. */ get_four_state(vpi_scan(argv), &job); /* Get the value. */ get_four_state(vpi_scan(argv), &inform); /* Get the status variable. */ status = vpi_scan(argv); /* We are done with the argument iterator so free it. */ vpi_free_object(argv); /* Verify that the id is valid. */ idx = get_id_index(id); if (invalid_id || (idx < 0)) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNDEFINED_ID; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Add the data to the queue if it is not already full. */ if (add_to_queue(idx, &job, &inform)) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_FULL; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* The data was added to the queue so return OK. */ val.format = vpiIntVal; val.value.integer = IVL_QUEUE_OK; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* * Check that the given $q_remove() call has valid arguments. */ static PLI_INT32 sys_q_remove_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument (the id) must be numeric. */ if (! is_32_or_smaller_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", name); vpi_control(vpiFinish, 1); } /* The second argument (the job id) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the job id argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "second"); /* The third argument (the information id) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a third (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the information id argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "third"); /* The fourth argument (the status) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a fourth (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the status argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "fourth"); /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "four arguments", 0); return 0; } /* * The runtime code for $q_remove(). */ static PLI_INT32 sys_q_remove_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle job, inform, status; PLI_INT32 id, idx; s_vpi_vecval job_val, inform_val; s_vpi_value val; unsigned invalid_id; (void)name; /* Parameter is not used. */ /* Get the id. */ invalid_id = get_valid_32(vpi_scan(argv), &id); /* Get the job variable. */ job = vpi_scan(argv); /* Get the inform variable. */ inform = vpi_scan(argv); /* Get the status variable. */ status = vpi_scan(argv); /* We are done with the argument iterator so free it. */ vpi_free_object(argv); /* Verify that the id is valid. */ idx = get_id_index(id); if (invalid_id || (idx < 0)) { fill_variable_with_x(job); fill_variable_with_x(inform); val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNDEFINED_ID; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Remove the data from the queue if it is not already empty. */ if (remove_from_queue(idx, &job_val, &inform_val)) { fill_variable_with_x(job); fill_variable_with_x(inform); val.format = vpiIntVal; val.value.integer = IVL_QUEUE_EMPTY; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } val.format = vpiVectorVal; val.value.vector = &job_val; vpi_put_value(job, &val, 0, vpiNoDelay); val.format = vpiVectorVal; val.value.vector = &inform_val; vpi_put_value(inform, &val, 0, vpiNoDelay); /* The data was added to the queue so return OK. */ val.format = vpiIntVal; val.value.integer = IVL_QUEUE_OK; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* * Check that the given $q_full() call has valid arguments. */ static PLI_INT32 sys_q_full_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument (the id) must be numeric. */ if (! is_32_or_smaller_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", name); vpi_control(vpiFinish, 1); } /* The second argument (the status) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the status argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "second"); /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } /* * The runtime code for $q_full(). */ static PLI_INT32 sys_q_full_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle status; PLI_INT32 id, idx; s_vpi_value val; unsigned invalid_id; (void)name; /* Parameter is not used. */ /* Get the id. */ invalid_id = get_valid_32(vpi_scan(argv), &id); /* Get the status variable. */ status = vpi_scan(argv); /* We are done with the argument iterator so free it. */ vpi_free_object(argv); /* Verify that the id is valid. */ idx = get_id_index(id); if (invalid_id || (idx < 0)) { val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNDEFINED_ID; vpi_put_value(status, &val, 0, vpiNoDelay); fill_variable_with_x(callh); return 0; } /* Get the queue state and return it. */ val.format = vpiIntVal; if (is_queue_full(idx)) val.value.integer = 1; else val.value.integer = 0; vpi_put_value(callh, &val, 0, vpiNoDelay); /* The queue state was passed back so return OK. */ val.format = vpiIntVal; val.value.integer = IVL_QUEUE_OK; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* * Check that the given $q_exam() call has valid arguments. */ static PLI_INT32 sys_q_exam_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires four arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the first two arguments (the id and code) are numeric. */ if (check_numeric_args(argv, 2, callh, name)) return 0; /* The third argument (the value) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a third (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the value argument is a variable with at least * 32 bits. */ check_var_arg_large(arg, callh, name, "third"); /* The fourth argument (the status) must be a variable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a fourth (variable) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that the status argument is a 32 bit variable. */ check_var_arg_32(arg, callh, name, "fourth"); /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } /* * The runtime code for $q_exam(). */ static PLI_INT32 sys_q_exam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle value, status; PLI_INT32 id, code, idx, rtn; s_vpi_value val; unsigned invalid_id, invalid_code; (void)name; /* Parameter is not used. */ /* Get the id. */ invalid_id = get_valid_32(vpi_scan(argv), &id); /* Get the code. */ invalid_code = get_valid_32(vpi_scan(argv), &code); /* Get the value variable. */ value = vpi_scan(argv); /* Get the status variable. */ status = vpi_scan(argv); /* We are done with the argument iterator so free it. */ vpi_free_object(argv); /* Verify that the id is valid. */ idx = get_id_index(id); if (invalid_id || (idx < 0)) { fill_variable_with_x(value); val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNDEFINED_ID; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* Verify that the code is valid. */ if (invalid_code || (code <= 0) || (code > 6)) { fill_variable_with_x(value); val.format = vpiIntVal; val.value.integer = IVL_QUEUE_UNDEFINED_STAT_CODE; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } rtn = IVL_QUEUE_OK; /* Calculate the requested queue information. */ switch (code) { /* The current queue length. */ case IVL_QUEUE_LENGTH: val.format = vpiIntVal; val.value.integer = get_current_queue_length(idx); vpi_put_value(value, &val, 0, vpiNoDelay); break; /* The mean inter-arrival time. */ case IVL_QUEUE_MEAN: if (have_interarrival_statistic(idx) == 0) { fill_variable_with_x(value); rtn = IVL_QUEUE_NO_STATISTICS; } else { uint64_t ia_time = get_mean_interarrival_time(idx); rtn = fill_variable_with_scaled_time(value, ia_time); } break; /* The maximum queue length. */ case IVL_QUEUE_MAX_LENGTH: val.format = vpiIntVal; val.value.integer = get_maximum_queue_length(idx); vpi_put_value(value, &val, 0, vpiNoDelay); break; /* The shortest queue wait time ever. */ case IVL_QUEUE_SHORTEST: if (have_shortest_wait_statistic(idx) == 0) { fill_variable_with_x(value); rtn = IVL_QUEUE_NO_STATISTICS; } else { uint64_t sw_time = get_shortest_wait_time(idx); rtn = fill_variable_with_scaled_time(value, sw_time); } break; /* The longest wait time for elements still in the queue. */ case IVL_QUEUE_LONGEST: if (get_current_queue_length(idx) == 0) { fill_variable_with_x(value); rtn = IVL_QUEUE_NO_STATISTICS; } else { uint64_t lq_time = get_longest_queue_time(idx); rtn = fill_variable_with_scaled_time(value, lq_time); } break; /* The average queue wait time. */ case IVL_QUEUE_AVERAGE: if (have_average_wait_statistic(idx) == 0) { fill_variable_with_x(value); rtn = IVL_QUEUE_NO_STATISTICS; } else { uint64_t aw_time = get_average_wait_time(idx); rtn = fill_variable_with_scaled_time(value, aw_time); } break; default: assert(0); } /* The queue information was passed back so now return the status. */ val.format = vpiIntVal; val.value.integer = rtn; vpi_put_value(status, &val, 0, vpiNoDelay); return 0; } /* * Routine to register the system tasks/functions provided in this file. */ void sys_queue_register(void) { s_vpi_systf_data tf_data; s_cb_data cb; vpiHandle res; tf_data.type = vpiSysTask; tf_data.tfname = "$q_initialize"; tf_data.calltf = sys_q_initialize_calltf; tf_data.compiletf = sys_q_initialize_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$q_initialize"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$q_add"; tf_data.calltf = sys_q_add_calltf; tf_data.compiletf = sys_q_add_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$q_add"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$q_remove"; tf_data.calltf = sys_q_remove_calltf; tf_data.compiletf = sys_q_remove_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$q_remove"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$q_full"; tf_data.calltf = sys_q_full_calltf; tf_data.compiletf = sys_q_full_compiletf; tf_data.sizetf = 0; /* Not needed for a vpiSysFuncInt. */ tf_data.user_data = "$q_full"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$q_exam"; tf_data.calltf = sys_q_exam_calltf; tf_data.compiletf = sys_q_exam_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$q_exam"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* Create a callback to clear all the queue memory when the * simulator finishes. */ cb.time = NULL; cb.reason = cbEndOfSimulation; cb.cb_rtn = cleanup_queue; cb.user_data = 0x0; cb.obj = 0x0; vpi_register_cb(&cb); } iverilog-10_1/vpi/sys_random.c000066400000000000000000000717061265551621300164750ustar00rootroot00000000000000/* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include "sys_random.h" # include # include # include # include #if ULONG_MAX > 4294967295UL # define UNIFORM_MAX INT_MAX # define UNIFORM_MIN INT_MIN #else # define UNIFORM_MAX LONG_MAX # define UNIFORM_MIN LONG_MIN #endif static double uniform(long *seed, long start, long end); static double normal(long *seed, long mean, long deviation); static double exponential(long *seed, long mean); static long poisson(long *seed, long mean); static double chi_square(long *seed, long deg_of_free); static double t(long *seed, long deg_of_free); static double erlangian(long *seed, long k, long mean); static long rtl_dist_chi_square(long *seed, long df) { double r; long i; if (df > 0) { r = chi_square(seed, df); if (r >= 0) { i = (long) (r + 0.5); } else { r = -r; i = (long) (r + 0.5); i = -i; } } else { vpi_printf("WARNING: Chi_square distribution must have " "a positive degree of freedom\n"); i = 0; } return i; } static long rtl_dist_erlang(long *seed, long k, long mean) { double r; long i; if (k > 0) { r = erlangian(seed, k, mean); if (r >= 0) { i = (long) (r + 0.5); } else { r = -r; i = (long) (r + 0.5); i = -i; } } else { vpi_printf("WARNING: K-stage erlangian distribution must have " "a positive k\n"); i = 0; } return i; } static long rtl_dist_exponential(long *seed, long mean) { double r; long i; if (mean > 0) { r = exponential(seed, mean); if (r >= 0) { i = (long) (r + 0.5); } else { r = -r; i = (long) (r + 0.5); i = -i; } } else { vpi_printf("WARNING: Exponential distribution must have " "a positive mean\n"); i = 0; } return i; } static long rtl_dist_normal(long *seed, long mean, long sd) { double r; long i; r = normal(seed, mean, sd); if (r >= 0) { i = (long) (r + 0.5); } else { r = -r; i = (long) (r + 0.5); i = -i; } return i; } static long rtl_dist_poisson(long *seed, long mean) { long i; if (mean > 0) { i = poisson(seed, mean); } else { vpi_printf("WARNING: Poisson distribution must have " "a positive mean\n"); i = 0; } return i; } static long rtl_dist_t(long *seed, long df) { double r; long i; if (df > 0) { r = t(seed, df); if (r >= 0) { i = (long) (r + 0.5); } else { r = -r; i = (long) (r + 0.5); i = -i; } } else { vpi_printf("WARNING: t distribution must have " "a positive degree of freedom\n"); i = 0; } return i; } /* copied from IEEE1364-2001, with slight modifications for 64bit machines. */ static long rtl_dist_uniform(long *seed, long start, long end) { double r; long i; if (start >= end) return(start); /* NOTE: The cast of r to i can overflow and generate strange values, so cast to unsigned long first. This eliminates the underflow and gets the twos complement value. That in turn can be cast to the long value that is expected. */ if (end != UNIFORM_MAX) { end++; r = uniform(seed, start, end); if (r >= 0) { i = (unsigned long) r; } else { i = - ( (unsigned long) (-(r - 1)) ); } if (i < start) i = start; if (i >= end) i = end - 1; } else if (start != UNIFORM_MIN) { start--; r = uniform( seed, start, end) + 1.0; if (r >= 0) { i = (unsigned long) r; } else { i = - ( (unsigned long) (-(r - 1)) ); } if (i <= start) i = start + 1; if (i > end) i = end; } else { r = (uniform(seed, start, end) + 2147483648.0) / 4294967295.0; r = r * 4294967296.0 - 2147483648.0; if (r >= 0) { i = (unsigned long) r; } else { /* At least some compilers will notice that (r-1) is <0 when castling to unsigned long and replace the result with a zero. This causes much wrongness, so do the casting to the positive version and invert it back. */ i = - ( (unsigned long) (-(r - 1)) ); } } return i; } static double uniform(long *seed, long start, long end ) { double d = 0.00000011920928955078125; double a, b, c; unsigned long oldseed, newseed; oldseed = *seed; if (oldseed == 0) oldseed = 259341593; if (start >= end) { a = 0.0; b = 2147483647.0; } else { a = (double)start; b = (double)end; } /* Original routine used signed arithmetic, and the (frequent) * overflows trigger "Undefined Behavior" according to the * C standard (both c89 and c99). Using unsigned arithmetic * forces a conforming C implementation to get the result * that the IEEE-1364-2001 committee wants. */ newseed = 69069 * oldseed + 1; /* Emulate a 32-bit unsigned long, even if the native machine * uses wider words. */ #if ULONG_MAX > 4294967295UL newseed = newseed & 4294967295UL; #endif *seed = newseed; #if 0 /* Cadence-donated conversion from unsigned int to double */ { union { float s; unsigned stemp; } u; u.stemp = (newseed >> 9) | 0x3f800000; c = (double) u.s; } #else /* Equivalent conversion without assuming IEEE 32-bit float */ /* constant is 2^(-23) */ c = 1.0 + (newseed >> 9) * 0.00000011920928955078125; #endif c = c + (c*d); c = ((b - a) * (c - 1.0)) + a; return c; } static double normal(long *seed, long mean, long deviation) { double v1, v2, s; s = 1.0; while ((s >= 1.0) || (s == 0.0)) { v1 = uniform(seed, -1, 1); v2 = uniform(seed, -1, 1); s = v1 * v1 + v2 * v2; } s = v1 * sqrt(-2.0 * log(s) / s); v1 = (double) deviation; v2 = (double) mean; return s * v1 + v2; } static double exponential(long *seed, long mean) { double n; n = uniform(seed, 0, 1); if (n != 0.0) { n = -log(n) * mean; } return n; } static long poisson(long *seed, long mean) { long n; double p, q; n = 0; q = -(double) mean; p = exp(q); q = uniform(seed, 0, 1); while (p < q) { n++; q = uniform(seed, 0, 1) * q; } return n; } static double chi_square(long *seed, long deg_of_free) { double x; long k; if (deg_of_free % 2) { x = normal(seed, 0, 1); x = x * x; } else { x = 0.0; } for (k = 2; k <= deg_of_free; k = k + 2) { x = x + 2 * exponential(seed, 1); } return x; } static double t( long *seed, long deg_of_free) { double x, chi2, dv, root; chi2 = chi_square(seed, deg_of_free); dv = chi2 / (double) deg_of_free; root = sqrt(dv); x = normal(seed, 0, 1) / root; return x; } static double erlangian(long *seed, long k, long mean) { double x, a, b; long i; x = 1.0; for (i = 1; i <= k; i++) { x = x * uniform(seed, 0, 1); } a = (double) mean; b = (double) k; x = -a * log(x) / b; return x; } /* A seed can only be an integer/time variable or a register. */ static unsigned is_seed_obj(vpiHandle obj, vpiHandle callh, const char *name) { unsigned rtn = 0; assert(obj); switch (vpi_get(vpiType, obj)) { case vpiTimeVar: case vpiIntegerVar: case vpiIntVar: case vpiLongIntVar: rtn = 1; break; case vpiBitVar: case vpiReg: if (vpi_get(vpiSize, obj) < 32) { vpi_printf("Error: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's seed variable is less than 32 bits " " (%d).\n", name, (int)vpi_get(vpiSize, obj)); vpi_control(vpiFinish, 1); } else rtn = 1; break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's seed must be an integer/time" " variable or a register.\n", name); vpi_control(vpiFinish, 1); } return rtn; } static PLI_INT32 sys_rand_two_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle seed, arg2; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that there are at least two arguments. */ seed = vpi_scan(argv); /* This should never be zero. */ arg2 = vpi_scan(argv); if (arg2 == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The seed must be a time/integer variable or a register. */ if (! is_seed_obj(seed, callh, name)) return 0; /* The second argument must be numeric. */ if (! is_numeric_obj(arg2)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s second argument must be numeric.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that there is at most two arguments. */ check_for_extra_args(argv, callh, name, "two arguments", 0); return 0; } PLI_INT32 sys_rand_three_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle seed, arg2, arg3; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires three arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that there are at least three arguments. */ seed = vpi_scan(argv); /* This should never be zero. */ arg2 = vpi_scan(argv); if (arg2) { arg3 = vpi_scan(argv); } else { arg3 = 0; } if (arg2 == 0 || arg3 == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires three arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The seed must be a time/integer variable or a register. */ if (! is_seed_obj(seed, callh, name)) return 0; /* The second argument must be numeric. */ if (! is_numeric_obj(arg2)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s second argument must be numeric.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The third argument must be numeric. */ if (! is_numeric_obj(arg3)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s third argument must be numeric.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that there is at most three arguments. */ check_for_extra_args(argv, callh, name, "three arguments", 0); return 0; } PLI_INT32 sys_random_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); /* The seed is optional. */ if (argv == 0) return 0; /* The seed must be a time/integer variable or a register. */ if (! is_seed_obj(vpi_scan(argv), callh, name)) return 0; /* Check that there no extra arguments. */ check_for_extra_args(argv, callh, name, "one argument", 1); return 0; } static PLI_INT32 sys_random_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed = 0; s_vpi_value val; static long i_seed = 0; long a_seed; (void)name; /* Parameter is not used. */ /* Get the argument list and look for a seed. If it is there, get the value and reseed the random number generator. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); val.format = vpiIntVal; if (argv) { seed = vpi_scan(argv); vpi_free_object(argv); vpi_get_value(seed, &val); a_seed = val.value.integer; } else a_seed = i_seed; /* Calculate and return the result. */ val.value.integer = rtl_dist_uniform(&a_seed, INT_MIN, INT_MAX); vpi_put_value(callh, &val, 0, vpiNoDelay); /* If it exists send the updated seed back to seed parameter. */ if (seed) { val.value.integer = a_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); } else i_seed = a_seed; return 0; } /* From SystemVerilog. */ static PLI_INT32 sys_urandom_range_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s requires one or two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check that there is at least one argument. */ arg = vpi_scan(argv); /* This should never be zero. */ assert(arg); arg = vpi_scan(argv); /* Is this a single argument function call? */ if (arg == 0) return 0; /* These functions takes at most two argument. */ arg = vpi_scan(argv); if (arg != 0) { vpi_printf("ERROR: %s takes at most two argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* vpi_scan returning 0 (NULL) has already freed argv. */ return 0; } /* From SystemVerilog. */ static unsigned long urandom(long *seed, unsigned long max, unsigned long min) { static long i_seed = 0; unsigned long result; long max_i, min_i; max_i = max + INT_MIN; min_i = min + INT_MIN; if (seed != 0) i_seed = *seed; result = rtl_dist_uniform(&i_seed, min_i, max_i) - INT_MIN; if (seed != 0) *seed = i_seed; return result; } /* From SystemVerilog. */ static PLI_INT32 sys_urandom_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed = 0; s_vpi_value val; long i_seed; (void)name; /* Parameter is not used. */ /* Get the argument list and look for a seed. If it is there, get the value and reseed the random number generator. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); val.format = vpiIntVal; if (argv) { seed = vpi_scan(argv); vpi_free_object(argv); vpi_get_value(seed, &val); i_seed = val.value.integer; } /* Calculate and return the result. */ if (seed) { val.value.integer = urandom(&i_seed, UINT_MAX, 0); } else { val.value.integer = urandom(0, UINT_MAX, 0); } vpi_put_value(callh, &val, 0, vpiNoDelay); /* If it exists send the updated seed back to seed parameter. */ if (seed) { val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); } return 0; } /* From SystemVerilog. */ static PLI_INT32 sys_urandom_range_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, maxval, minval; s_vpi_value val; unsigned long i_maxval, i_minval; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); maxval = vpi_scan(argv); minval = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(maxval, &val); i_maxval = val.value.integer; /* Is this a two or one argument function call? */ if (minval) { vpi_get_value(minval, &val); i_minval = val.value.integer; vpi_free_object(argv); } else { i_minval = 0; } /* Swap the two arguments if they are out of order. */ if (i_minval > i_maxval) { unsigned long tmp = i_minval; i_minval = i_maxval; i_maxval = tmp; } /* Calculate and return the result. */ val.value.integer = urandom(0, i_maxval, i_minval); vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_dist_uniform_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, start, end; s_vpi_value val; long i_seed, i_start, i_end; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); start = vpi_scan(argv); end = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(start, &val); i_start = val.value.integer; vpi_get_value(end, &val); i_end = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_uniform(&i_seed, i_start, i_end); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_dist_normal_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, mean, sd; s_vpi_value val; long i_seed, i_mean, i_sd; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); mean = vpi_scan(argv); sd = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(mean, &val); i_mean = val.value.integer; vpi_get_value(sd, &val); i_sd = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_normal(&i_seed, i_mean, i_sd); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_dist_exponential_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, mean; s_vpi_value val; long i_seed, i_mean; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); mean = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(mean, &val); i_mean = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_exponential(&i_seed, i_mean); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_dist_poisson_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, mean; s_vpi_value val; long i_seed, i_mean; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); mean = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(mean, &val); i_mean = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_poisson(&i_seed, i_mean); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_dist_chi_square_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, df; s_vpi_value val; long i_seed, i_df; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); df = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(df, &val); i_df = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_chi_square(&i_seed, i_df); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_dist_t_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, df; s_vpi_value val; long i_seed, i_df; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); df = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(df, &val); i_df = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_t(&i_seed, i_df); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_dist_erlang_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, seed, k, mean; s_vpi_value val; long i_seed, i_k, i_mean; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); k = vpi_scan(argv); mean = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(k, &val); i_k = val.value.integer; vpi_get_value(mean, &val); i_mean = val.value.integer; /* Calculate and return the result. */ val.value.integer = rtl_dist_erlang(&i_seed, i_k, i_mean); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_rand_func_sizetf(PLI_BYTE8 *name) { (void)name; /* Parameter is not used. */ return 32; } void sys_random_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$random"; tf_data.calltf = sys_random_calltf; tf_data.compiletf = sys_random_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$random"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* From SystemVerilog. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncSized; tf_data.tfname = "$urandom"; tf_data.calltf = sys_urandom_calltf; tf_data.compiletf = sys_random_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$urandom"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* From SystemVerilog. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncSized; tf_data.tfname = "$urandom_range"; tf_data.calltf = sys_urandom_range_calltf; tf_data.compiletf = sys_urandom_range_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$urandom_range"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_uniform"; tf_data.calltf = sys_dist_uniform_calltf; tf_data.compiletf = sys_rand_three_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_uniform"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_normal"; tf_data.calltf = sys_dist_normal_calltf; tf_data.compiletf = sys_rand_three_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_normal"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_exponential"; tf_data.calltf = sys_dist_exponential_calltf; tf_data.compiletf = sys_rand_two_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_exponential"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_poisson"; tf_data.calltf = sys_dist_poisson_calltf; tf_data.compiletf = sys_rand_two_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_poisson"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_chi_square"; tf_data.calltf = sys_dist_chi_square_calltf; tf_data.compiletf = sys_rand_two_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_chi_square"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_t"; tf_data.calltf = sys_dist_t_calltf; tf_data.compiletf = sys_rand_two_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_t"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$dist_erlang"; tf_data.calltf = sys_dist_erlang_calltf; tf_data.compiletf = sys_rand_three_args_compiletf; tf_data.sizetf = sys_rand_func_sizetf; tf_data.user_data = "$dist_erlang"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_random.h000066400000000000000000000022521265551621300164700ustar00rootroot00000000000000#ifndef IVL_sys_random_H #define IVL_sys_random_H /* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include /* * Common compiletf routines for the different random implementations. */ extern PLI_INT32 sys_rand_three_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_random_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); #endif /* IVL_sys_random_H */ iverilog-10_1/vpi/sys_random_mti.c000066400000000000000000000116301265551621300173340ustar00rootroot00000000000000/* * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include "sys_random.h" # include # include # include # include # include "ivl_alloc.h" /* * Implement the $random system function using the ``Mersenne * Twister'' random number generator MT19937. */ /* make sure this matches N+1 in mti19937int.c */ #define NP1 624+1 /* Icarus seed cookie */ #define COOKIE 0x1ca1ca1c static struct context_s global_context = {NP1, {0} }; static long mti_dist_uniform(long*seed, long start, long end) { (void)seed; /* Parameter is not used. */ if (start >= end) return start; if ((start > LONG_MIN) || (end < LONG_MAX)) { long range = end - start; return start + genrand(&global_context)%range; } else { return genrand(&global_context); } } static PLI_INT32 sys_mti_dist_uniform_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh, argv, seed, start, end; s_vpi_value val; long i_seed, i_start, i_end; (void)name; /* Parameter is not used. */ /* Get the argument handles and convert them. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); seed = vpi_scan(argv); start = vpi_scan(argv); end = vpi_scan(argv); val.format = vpiIntVal; vpi_get_value(seed, &val); i_seed = val.value.integer; vpi_get_value(start, &val); i_start = val.value.integer; vpi_get_value(end, &val); i_end = val.value.integer; /* Calculate and return the result. */ val.value.integer = mti_dist_uniform(&i_seed, i_start, i_end); vpi_put_value(callh, &val, 0, vpiNoDelay); /* Return the seed. */ val.value.integer = i_seed; vpi_put_value(seed, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } static PLI_INT32 sys_mti_random_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh, argv, seed = 0; s_vpi_value val; int i_seed = COOKIE; struct context_s *context; (void)name; /* Parameter is not used. */ /* Get the argument list and look for a seed. If it is there, get the value and reseed the random number generator. */ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); val.format = vpiIntVal; if (argv) { seed = vpi_scan(argv); vpi_free_object(argv); vpi_get_value(seed, &val); i_seed = val.value.integer; /* Since there is a seed use the current context or create a new one */ context = (struct context_s *)vpi_get_userdata(callh); if (!context) { context = (struct context_s *)calloc(1, sizeof(*context)); context->mti = NP1; /* squirrel away context */ vpi_put_userdata(callh, (void *)context); } /* If the argument is not the Icarus cookie, then reseed context */ if (i_seed != COOKIE) sgenrand(context, i_seed); } else { /* use global context */ context = &global_context; } /* Calculate and return the result */ val.value.integer = genrand(context); vpi_put_value(callh, &val, 0, vpiNoDelay); /* mark seed with cookie */ if (seed && i_seed != COOKIE) { val.value.integer = COOKIE; vpi_put_value(seed, &val, 0, vpiNoDelay); } return 0; } void sys_random_mti_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$mti_random"; tf_data.calltf = sys_mti_random_calltf; tf_data.compiletf = sys_random_compiletf; tf_data.user_data = "$mti_random"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncInt; tf_data.tfname = "$mti_dist_uniform"; tf_data.calltf = sys_mti_dist_uniform_calltf; tf_data.compiletf = sys_rand_three_args_compiletf; tf_data.user_data = "$mti_dist_uniform"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_readmem.c000066400000000000000000000505361265551621300166250ustar00rootroot00000000000000/* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include # include # include # include "sys_readmem_lex.h" # include # include "ivl_alloc.h" char **search_list = NULL; unsigned sl_count = 0; static void get_mem_params(vpiHandle argv, vpiHandle callh, const char *name, char **fname, vpiHandle *mitem, vpiHandle *start_item, vpiHandle *stop_item) { /* Get the first parameter (file name). */ *fname = get_filename(callh, name, vpi_scan(argv)); /* Get the second parameter (memory). */ *mitem = vpi_scan(argv); /* Get optional third parameter (start address). */ *start_item = vpi_scan(argv); if (*start_item) { /* Warn the user if they gave a real value for the start * address. */ switch (vpi_get(vpiType, *start_item)) { case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, *start_item) != vpiRealConst) break; case vpiRealVar: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's third argument (start address) is a real " "value.\n", name); } /* Get optional fourth parameter (finish address). */ *stop_item = vpi_scan(argv); if (*stop_item) { /* Warn the user if they gave a real value for the finish * address. */ switch (vpi_get(vpiType, *stop_item)) { case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, *stop_item) != vpiRealConst) { break; } case vpiRealVar: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's fourth argument (finish address) is a " "real value.\n", name); } vpi_free_object(argv); } } else { *stop_item = 0; } } static int process_params(vpiHandle mitem, vpiHandle start_item, vpiHandle stop_item, vpiHandle callh, const char *name, int *start_addr, int *stop_addr, int *addr_incr, int *min_addr, int *max_addr) { s_vpi_value val; int left_addr, right_addr; /* Get left addr of memory */ val.format = vpiIntVal; vpi_get_value(vpi_handle(vpiLeftRange, mitem), &val); left_addr = val.value.integer; /* Get right addr of memory */ val.format = vpiIntVal; vpi_get_value(vpi_handle(vpiRightRange, mitem), &val); right_addr = val.value.integer; /* Get start_addr, stop_addr and addr_incr */ if (! start_item) { *start_addr = left_addr right_addr)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Standard inconsistency, following 1364-2005.\n", name); } /* Check that start_addr and stop_addr are within the memory range */ if (left_addr < right_addr) { if (*start_addr < left_addr || *start_addr > right_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Start address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *start_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } if (*stop_addr < left_addr || *stop_addr > right_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Finish address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *stop_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } } else { if (*start_addr < right_addr || *start_addr > left_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Start address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *start_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } if (*stop_addr < right_addr || *stop_addr > left_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Finish address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *stop_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } } return 0; } static PLI_INT32 sys_mem_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there is a file name argument. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_string_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a file name (string).\n", name); vpi_control(vpiFinish, 1); } /* Check that there is a memory argument. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (memory) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (vpi_get(vpiType, arg) != vpiMemory) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be a memory.\n", name); vpi_control(vpiFinish, 1); } /* Check if there is a starting address argument. */ arg = vpi_scan(argv); if (! arg) return 0; if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's third argument must be a start address " "(numeric).\n", name); vpi_control(vpiFinish, 1); } /* Check if there is a finish address argument. */ arg = vpi_scan(argv); if (! arg) return 0; if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's fourth argument must be a finish address " "(numeric).\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ check_for_extra_args(argv, callh, name, "four arguments", 1); return 0; } static PLI_INT32 sys_readmem_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { int code, wwid, addr; FILE*file; char *fname = 0; s_vpi_value value; vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle mitem = 0; vpiHandle start_item = 0; vpiHandle stop_item = 0; /* start_addr and stop_addr are the parameters given to $readmem in the Verilog code. When not specified, start_addr is equal to the lower of the [left,right]_addr and stop_addr is equal to the higher of the [left,right]_addr. */ int start_addr, stop_addr, addr_incr; /* min_addr and max_addr are equal to start_addr and stop_addr if start_addr 0 && fname[0] != '/') { unsigned idx; char path[4096]; for (idx = 0; idx < sl_count; idx += 1) { snprintf(path, sizeof(path), "%s/%s", search_list[idx], fname); path[sizeof(path)-1] = 0; if ((file = fopen(path, "r"))) break; } } if (file == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Unable to open %s for reading.\n", name, fname); free(fname); return 0; } /* We need this many words from the file. */ word_count = max_addr-min_addr+1; wwid = vpi_get(vpiSize, vpi_handle_by_index(mitem, min_addr)); /* variable that will be used by the lexer to pass values back to this code */ value.format = vpiVectorVal; value.value.vector = calloc((wwid+31)/32, sizeof(s_vpi_vecval)); /* Configure the readmem lexer */ if (strcmp(name,"$readmemb") == 0) sys_readmem_start_file(file, 1, wwid, value.value.vector); else sys_readmem_start_file(file, 0, wwid, value.value.vector); /*======================================== Read memory file */ /* Run through the input file and store the new contents in the memory */ addr = start_addr; while ((code = readmemlex()) != 0) { switch (code) { case MEM_ADDRESS: addr = value.value.vector->aval; if (addr < min_addr || addr > max_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): address (0x%x) is out of range " "[0x%x:0x%x]\n", name, fname, addr, start_addr, stop_addr); goto bailout; } /* if there is an address in the memory file, then turn off any possible warnings about not having enough words to load the memory. This is standard behavior from 1364-2005. */ word_count = 0; break; case MEM_WORD: if (addr >= min_addr && addr <= max_addr) { vpiHandle word_index; word_index = vpi_handle_by_index(mitem, addr); assert(word_index); vpi_put_value(word_index, &value, 0, vpiNoDelay); if (word_count > 0) word_count -= 1; } else { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): Too many words in the file for the " "requested range [%d:%d].\n", name, fname, start_addr, stop_addr); goto bailout; } addr += addr_incr; break; case MEM_ERROR: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): Invalid input character: %s\n", name, fname, readmem_error_token); goto bailout; break; default: assert(0); break; } } /* Print a warning if there are not enough words in the data file. */ if (word_count > 0) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): Not enough words in the file for the " "requested range [%d:%d].\n", name, fname, start_addr, stop_addr); } bailout: free(value.value.vector); free(fname); fclose(file); destroy_readmem_lexor(); return 0; } static PLI_INT32 free_readmempath(p_cb_data cb_data) { unsigned idx; (void)cb_data; /* Parameter is not used. */ for(idx = 0; idx < sl_count; idx += 1) { free(search_list[idx]); } free(search_list); search_list = NULL; sl_count = 0; return 0; } static PLI_INT32 sys_readmempath_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle paths = vpi_scan(argv); s_vpi_value val; unsigned len, idx; char *path; vpi_free_object(argv); /* Get the search path string. */ val.format = vpiStringVal; vpi_get_value(paths, &val); /* Verify that we have a string and that it is not NULL. */ if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's argument (%s) is not a valid string.\n", name, vpi_get_str(vpiType, paths)); return 0; } /* * Verify that the search path is composed of only printable * characters. */ len = strlen(val.value.str); for (idx = 0; idx < len; idx++) { if (! isprint((int)val.value.str[idx])) { char msg[64]; char *esc_path = as_escaped(val.value.str); snprintf(msg, sizeof(msg), "WARNING: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s's argument contains non-printable " "characters.\n", msg, name); vpi_printf("%*s \"%s\"\n", (int) strlen(msg), " ", esc_path); free(esc_path); return 0; } } /* Clear the old list before creating the new list. */ free_readmempath(NULL); /* * Break the string into individual paths and add them to the list. * Print a warning if the path is not valid. */ for (path = strtok(val.value.str, ":"); path; path = strtok(NULL, ":")) { int res; struct stat sb; /* Warn the user if the path is not valid. */ res = stat(path, &sb); if (res == 0) { if (!S_ISDIR(sb.st_mode)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's path element \"%s\" is not a " "directory!\n", name, path); continue; } } else { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s could not find directory \"%s\"!\n", name, path); continue; } /* Add a valid search path element to the list. */ sl_count += 1; search_list = (char **) realloc(search_list, sizeof(char **)*sl_count); search_list[sl_count-1] = strdup(path); } return 0; } static PLI_INT32 sys_writemem_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { int addr; FILE*file; char*fname = 0; unsigned cnt; s_vpi_value value; vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle mitem = 0; vpiHandle start_item = 0; vpiHandle stop_item = 0; int start_addr, stop_addr, addr_incr; int min_addr, max_addr; // Not used in this routine. /*======================================== Get parameters */ get_mem_params(argv, callh, name, &fname, &mitem, &start_item, &stop_item); if (fname == 0) return 0; /*======================================== Process parameters */ if (process_params(mitem, start_item, stop_item, callh, name, &start_addr, &stop_addr, &addr_incr, &min_addr, &max_addr)) { free(fname); return 0; } /* Open the data file. */ file = fopen(fname, "w"); if (file == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Unable to open %s for writing.\n", name, fname); free(fname); return 0; } if (strcmp(name,"$writememb")==0) value.format = vpiBinStrVal; else value.format = vpiHexStrVal; /*======================================== Write memory file */ cnt = 0; for(addr=start_addr; addr!=stop_addr+addr_incr; addr+=addr_incr, ++cnt) { vpiHandle word_index; if (cnt%16 == 0) fprintf(file, "// 0x%08x\n", cnt); word_index = vpi_handle_by_index(mitem, addr); assert(word_index); vpi_get_value(word_index, &value); fprintf(file, "%s\n", value.value.str); } fclose(file); free(fname); return 0; } void sys_readmem_register(void) { s_vpi_systf_data tf_data; vpiHandle res; s_cb_data cb_data; tf_data.type = vpiSysTask; tf_data.tfname = "$readmemh"; tf_data.calltf = sys_readmem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$readmemh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$readmemb"; tf_data.calltf = sys_readmem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$readmemb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$readmempath"; tf_data.calltf = sys_readmempath_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$readmempath"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$writememh"; tf_data.calltf = sys_writemem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writememh"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$writememb"; tf_data.calltf = sys_writemem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writememb"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); cb_data.reason = cbEndOfSimulation; cb_data.time = 0; cb_data.cb_rtn = free_readmempath; cb_data.user_data = "system"; vpi_register_cb(&cb_data); } iverilog-10_1/vpi/sys_readmem_lex.h000066400000000000000000000024061265551621300174730ustar00rootroot00000000000000#ifndef IVL_sys_readmem_lex_H #define IVL_sys_readmem_lex_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include # include "vpi_user.h" # define MEM_ADDRESS 257 # define MEM_WORD 258 # define MEM_ERROR 259 extern char *readmem_error_token; extern void sys_readmem_start_file(FILE*in, int bin_flag, unsigned width, struct t_vpi_vecval*val); extern int readmemlex(void); extern void destroy_readmem_lexor(void); #endif /* IVL_sys_readmem_lex_H */ iverilog-10_1/vpi/sys_readmem_lex.lex000066400000000000000000000104301265551621300200300ustar00rootroot00000000000000%option prefix="readmem" %option nounput %option noinput %{ /* * Copyright (c) 1999-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_readmem_lex.h" # include static void make_addr(void); static void make_hex_value(void); static void make_bin_value(void); static int save_state; char *readmem_error_token = 0; %} %x BIN %x HEX %x CCOMMENT %option noyywrap %% "//".* { ; } [ \t\f\n\r] { ; } @[0-9a-fA-F]+ { make_addr(); return MEM_ADDRESS; } [0-9a-fA-FxXzZ_]+ { make_hex_value(); return MEM_WORD; } [01xXzZ_]+ { make_bin_value(); return MEM_WORD; } "/*" { save_state = YY_START; BEGIN(CCOMMENT); } [^*]* { ; } "*" { ; } "*"+"/" { BEGIN(save_state); } /* Catch any invalid tokens and flagged them as an error. */ . { readmem_error_token = yytext; return MEM_ERROR; } %% static unsigned word_width = 0; static struct t_vpi_vecval*vecval = 0; static void make_addr(void) { sscanf(yytext+1, "%x", (unsigned int*)&vecval->aval); } static void make_hex_value(void) { char*beg = yytext; char*end = beg + strlen(beg); struct t_vpi_vecval*cur; int idx; int width = 0, word_max = word_width; for (idx = 0, cur = vecval ; idx < word_max ; idx += 32, cur += 1) { cur->aval = 0; cur->bval = 0; } cur = vecval; while ((width < word_max) && (end > beg)) { int aval = 0; int bval = 0; end -= 1; if (*end == '_') continue; switch (*end) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': aval = *end - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': aval = *end - 'a' + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': aval = *end - 'A' + 10; break; case 'x': case 'X': aval = 15; bval = 15; break; case 'z': case 'Z': bval = 15; break; } cur->aval |= aval << width; cur->bval |= bval << width; width += 4; if (width == 32) { cur += 1; width = 0; word_max -= 32; } } } static void make_bin_value(void) { char*beg = yytext; char*end = beg + strlen(beg); struct t_vpi_vecval*cur; int idx; int width = 0, word_max = word_width; for (idx = 0, cur = vecval ; idx < word_max ; idx += 32, cur += 1) { cur->aval = 0; cur->bval = 0; } cur = vecval; while ((width < word_max) && (end > beg)) { int aval = 0; int bval = 0; end -= 1; if (*end == '_') continue; switch (*end) { case '0': case '1': aval = *end - '0'; break; case 'x': case 'X': aval = 1; bval = 1; break; case 'z': case 'Z': bval = 1; break; } cur->aval |= aval << width; cur->bval |= bval << width; width += 1; if (width == 32) { cur += 1; width = 0; word_max -= 32; } } } void sys_readmem_start_file(FILE*in, int bin_flag, unsigned width, struct t_vpi_vecval *vv) { yyrestart(in); BEGIN(bin_flag? BIN : HEX); word_width = width; vecval = vv; } /* * Modern version of flex (>=2.5.9) can clean up the scanner data. */ void destroy_readmem_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif } iverilog-10_1/vpi/sys_scanf.c000066400000000000000000001242601265551621300163010ustar00rootroot00000000000000/* * Copyright (c) 2006-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* round() is ISO C99 from math.h. This define should enable it. */ # define _ISOC99_SOURCE 1 # define _SVID_SOURCE 1 # define _DEFAULT_SOURCE 1 # include "sys_priv.h" # include # include # include # include # include # include # include # include # include "ivl_alloc.h" /* * The wrapper routines below get a value from either a string or a file. * This structure is used to determine which one is used. The unused one * must be assigned a zero value. */ struct byte_source { const char *str; FILE *fd; }; /* * Wrapper routine to get a byte from either a string or a file descriptor. */ static int byte_getc(struct byte_source *src) { if (src->str) { assert(! src->fd); if (src->str[0] == 0) return EOF; return *(src->str)++; } assert(src->fd); return fgetc(src->fd); } /* * Wrapper routine to unget a byte to either a string or a file descriptor. */ static void byte_ungetc(struct byte_source *src, int ch) { if (ch == EOF) return; if (src->str) { assert(! src->fd); src->str -= 1; return; } assert(src->fd); ungetc(ch, src->fd); } /* * This function matches the input characters of a floating point * number and generates a floating point (double) from that string. * * Do we need support for +-Inf and NaN? It's not in the standard. * * The match variable is set to 1 for a match or 0 for no match. */ static double get_float(struct byte_source *src, unsigned width, int *match) { char *endptr; char *strval = malloc(1); unsigned len = 0; double result; int ch; /* Skip any leading space. */ ch = byte_getc(src); while (isspace(ch)) ch = byte_getc(src); /* If we are being asked for no digits then return a match fail. */ if (width == 0) { byte_ungetc(src, ch); free(strval); *match = 0; return 0.0; } /* Look for the optional sign. */ if ((ch == '+') || (ch == '-')) { /* If we have a sign then the width must not be * one since we need a sign and a digit. */ if (width == 1) { byte_ungetc(src, ch); free(strval); *match = 0; return 0.0; } strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } /* Get any digits before the optional decimal point, but no more * than width. */ while (isdigit(ch) && (len < width)) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } /* Get the optional decimal point and any following digits, but * no more than width total characters are copied. */ if ((ch == '.') && (len < width)) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); /* Get any trailing digits. */ while (isdigit(ch) && (len < width)) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } } /* No leading digits were matched. */ if ((len == 0) || ((len == 1) && ((strval[0] == '+') || (strval[0] == '-')))) { byte_ungetc(src, ch); free(strval); *match = 0; return 0.0; } /* Match an exponent. */ if (((ch == 'e') || (ch == 'E')) && (len < width)) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); /* We must have enough space for at least one digit after * the exponent. */ if (len == width) { byte_ungetc(src, ch); free(strval); *match = 0; return 0.0; } /* Check to see if the exponent has a sign. */ if ((ch == '-') || (ch == '+')) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); /* We must have enough space for at least one digit * after the exponent sign. */ if (len == width) { byte_ungetc(src, ch); free(strval); *match = 0; return 0.0; } } /* We must have at least one digit after the exponent/sign. */ if (! isdigit(ch)) { byte_ungetc(src, ch); free(strval); *match = 0; return 0.0; } /* Get the exponent digits, but no more than width total * characters are copied. */ while (isdigit(ch) && (len < width)) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } } strval[len] = 0; /* Put the last character back. */ byte_ungetc(src, ch); /* Calculate and return the result. */ result = strtod(strval, &endptr); /* If this asserts then there is a bug in the code above.*/ assert(*endptr == 0); free(strval); *match = 1; return result; } /* * Routine to return a floating point value (implements %e, %f and %g). * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_float(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name, char code) { vpiHandle arg; int match; s_vpi_value val; double result; /* Get the real value. */ result = get_float(src, width, &match); /* Nothing was matched. */ if (match == 0) return 0; /* If this match is being suppressed then return after consuming * the digits and report that no arguments were used. */ if (suppress_flag) return -1; /* We must have a variable to put the double value into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%%c format code.", name, code); vpi_control(vpiFinish, 1); return 0; } /* Put the value into the variable. */ val.format = vpiRealVal; val.value.real = result; vpi_put_value(arg, &val, 0, vpiNoDelay); /* We always consume one variable if it is available. */ return 1; } /* * Routine to return a floating point value scaled and rounded to the * current time scale and precision (implements %t). * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_float_time(vpiHandle callh, vpiHandle argv, struct byte_source*src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle scope = vpi_handle(vpiScope, callh); int time_units = vpi_get(vpiTimeUnit, scope); vpiHandle arg; int match; s_vpi_value val; double result; double scale; /* Get the raw real value. */ result = get_float(src, width, &match); /* Nothing was matched. */ if (match == 0) return 0; /* If this match is being suppressed then return after consuming * the digits and report that no arguments were used. */ if (suppress_flag) return -1; /* Round the raw value to the specified precision. Handle this by shifting the decimal point to the precision where we want to round, do the rounding, then shift the decimal point back */ scale = pow(10.0, timeformat_info.prec); result = round(result*scale) / scale; /* Scale the value from the timeformat units to the current * timescale units. To minimize the error keep the scale an * integer value. */ if (timeformat_info.units >= time_units) { scale = pow(10.0, timeformat_info.units - time_units); result *= scale; } else { scale = pow(10.0, time_units - timeformat_info.units); result /= scale; } /* We must have a variable to put the double value into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%t format code.", name); vpi_control(vpiFinish, 1); return 0; } /* Put the value into the variable. */ val.format = vpiRealVal; val.value.real = result; vpi_put_value(arg, &val, 0, vpiNoDelay); /* We always consume one variable if it is available. */ return 1; } /* * Base routine for getting binary, octal and hex values. * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_base(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name, const char *match, char code, PLI_INT32 type) { vpiHandle arg; char *strval = malloc(1); unsigned len = 0; s_vpi_value val; int ch; /* Skip any leading space. */ ch = byte_getc(src); while (isspace(ch)) ch = byte_getc(src); /* If we are being asked for no digits or the first character is * an underscore then return a match fail. */ if ((width == 0) || (ch == '_')) { byte_ungetc(src, ch); free(strval); return 0; } /* Get all the digits, but no more than width. */ while (strchr(match , ch) && (len < width)) { if (ch == '?') ch = 'x'; strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } strval[len] = 0; /* Put the last character back. */ byte_ungetc(src, ch); /* Nothing was matched. */ if (len == 0) { free(strval); return 0; } /* If this match is being suppressed then return after consuming * the digits and report that no arguments were used. */ if (suppress_flag) { free(strval); return -1; } /* We must have a variable to put the binary value into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%%c format code.", name, code); vpi_control(vpiFinish, 1); free(strval); return 0; } /* Put the value into the variable. */ val.format = type; val.value.str = strval; vpi_put_value(arg, &val, 0, vpiNoDelay); free(strval); /* We always consume one variable if it is available. */ return 1; } /* * Routine to return a binary value (implements %b). */ static int scan_format_binary(vpiHandle callh, vpiHandle argv, struct byte_source *src, int width, unsigned suppress_flag, PLI_BYTE8 *name) { return scan_format_base(callh, argv, src, width, suppress_flag, name, "01xzXZ?_", 'b', vpiBinStrVal); } /* * Routine to return a character value (implements %c). * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_char(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle arg; s_vpi_value val; int ch; /* If we are being asked for no digits then return a match fail. */ if (width == 0) return 0; /* Get the character to return. */ ch = byte_getc(src); /* Nothing was matched. */ if (ch == EOF) return 0; /* If this match is being suppressed then return after consuming * the character and report that no arguments were used. */ if (suppress_flag) return -1; /* We must have a variable to put the character value into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%c format code.", name); vpi_control(vpiFinish, 1); return 0; } /* Put the character into the variable. */ val.format = vpiIntVal; val.value.integer = ch; vpi_put_value(arg, &val, 0, vpiNoDelay); /* We always consume one variable if it is available. */ return 1; } /* * Routine to return a decimal value (implements %d). * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_decimal(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle arg; char *strval = malloc(1); s_vpi_value val; int ch; /* Skip any leading space. */ ch = byte_getc(src); while (isspace(ch)) ch = byte_getc(src); /* If we are being asked for no digits or the first character is * an underscore then return a match fail. */ if ((width == 0) || (ch == '_')) { byte_ungetc(src, ch); free(strval); return 0; } /* A decimal can match a single x/X, ? or z/Z character. */ if (strchr("xX?", ch)) { strval = realloc(strval, 2); strval[0] = 'x'; strval[1] = 0; } else if (strchr("zZ", ch)) { strval = realloc(strval, 2); strval[0] = 'z'; strval[1] = 0; } else { unsigned len = 0; /* To match a + or - we must have a digit after it. */ if (ch == '+') { /* If we have a '+' sign then the width must not be * one since we need a sign and a digit. */ if (width == 1) { free(strval); return 0; } ch = byte_getc(src); if (! isdigit(ch)) { byte_ungetc(src, ch); free(strval); return 0; } /* The '+' used up a character. */ width -= 1; } else if (ch == '-') { /* If we have a '-' sign then the width must not be * one since we need a sign and a digit. */ if (width == 1) { free(strval); return 0; } ch = byte_getc(src); if (isdigit(ch)) { strval = realloc(strval, len+2); strval[len++] = '-'; } else { byte_ungetc(src, ch); free(strval); return 0; } } /* Get all the characters, but no more than width. */ while ((isdigit(ch) || ch == '_') && (len < width)) { strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } strval[len] = 0; /* Put the last character back. */ byte_ungetc(src, ch); /* Nothing was matched. */ if (len == 0) { free(strval); return 0; } } /* If this match is being suppressed then return after consuming * the digits and report that no arguments were used. */ if (suppress_flag) { free(strval); return -1; } /* We must have a variable to put the decimal value into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%d format code.", name); vpi_control(vpiFinish, 1); free(strval); return 0; } /* Put the decimal value into the variable. */ val.format = vpiDecStrVal; val.value.str = strval; vpi_put_value(arg, &val, 0, vpiNoDelay); free(strval); /* We always consume one variable if it is available. */ return 1; } /* * Routine to return a hex value (implements %h). */ static int scan_format_hex(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { return scan_format_base(callh, argv, src, width, suppress_flag, name, "0123456789abcdefxzABCDEFXZ?_", 'h', vpiHexStrVal); } /* * Routine to return an octal value (implements %o). */ static int scan_format_octal(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { return scan_format_base(callh, argv, src, width, suppress_flag, name, "01234567xzXZ?_", 'o', vpiOctStrVal); } /* * Routine to return the current hierarchical path (implements %m). */ static int scan_format_module_path(vpiHandle callh, vpiHandle argv, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle scope, arg; char *module_path; s_vpi_value val; /* If this format code is being suppressed then just return that no * arguments were used. */ if (suppress_flag) return -1; /* We must have a variable to put the hierarchical path into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%m format code.", name); vpi_control(vpiFinish, 1); return 0; } /* Get the current hierarchical path. */ scope = vpi_handle(vpiScope, callh); assert(scope); module_path = vpi_get_str(vpiFullName, scope); /* Put the hierarchical path into the variable. */ val.format = vpiStringVal; val.value.str = module_path; vpi_put_value(arg, &val, 0, vpiNoDelay); /* We always consume one variable if it is available. */ return 1; } /* * Routine to return a string value (implements %s). * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_string(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle arg; char *strval = malloc(1); unsigned len = 0; s_vpi_value val; int ch; /* Skip any leading space. */ ch = byte_getc(src); while (isspace(ch)) ch = byte_getc(src); /* If we are being asked for no digits then return a match fail. */ if (width == 0) { byte_ungetc(src, ch); free(strval); return 0; } /* Get all the non-space characters, but no more than width. */ while (! isspace(ch) && (len < width)) { if (ch == EOF) break; strval = realloc(strval, len+2); strval[len++] = ch; ch = byte_getc(src); } strval[len] = 0; /* Nothing was matched (this can only happen at EOF). */ if (len == 0) { assert(ch == EOF); free(strval); return 0; } /* Put the last character back. */ byte_ungetc(src, ch); /* If this match is being suppressed then return after consuming * the string and report that no arguments were used. */ if (suppress_flag) { free(strval); return -1; } /* We must have a variable to put the string into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%s format code.", name); vpi_control(vpiFinish, 1); free(strval); return 0; } /* Put the string into the variable. */ val.format = vpiStringVal; val.value.str = strval; vpi_put_value(arg, &val, 0, vpiNoDelay); free(strval); /* We always consume one variable if it is available. */ return 1; } /* * Routine to return a two state value (implements %u). * * Note: Since this is a binary routine it also does not check for leading * space characters or use space as a boundary character. * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_two_state(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle arg; p_vpi_vecval val_ptr; s_vpi_value val; unsigned words, word; PLI_INT32 varlen; /* If we are being asked for no data then return a match fail. */ if (width == 0) return 0; /* Since this is a binary format we do not have an ending sequence. * Consequently we need to use the width to determine how many word * pairs to remove from the input stream. */ if (suppress_flag) { /* If no width was given then just remove one word pair. */ if (width == UINT_MAX) words = 1; else words = (width+31)/32; for (word = 0; word < words; word += 1) { unsigned byte; /* For a suppression we do not care about the endian order * of the bytes. */ for (byte = 0; byte < 4; byte += 1) { int ch = byte_getc(src); /* See the EOF comments below for more details. */ if (ch == EOF) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() only found %u of %u bytes " "needed by %%u format code.\n", name, word*4U + byte, words*4U); return 0; } } } return -1; } /* We must have a variable to put the bits into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%u format code.", name); vpi_control(vpiFinish, 1); return 0; } /* Extract either the given number of word pairs or enough to fill * the variable. */ varlen = (vpi_get(vpiSize, arg)+31)/32; assert(varlen > 0); val_ptr = (p_vpi_vecval) malloc(varlen*sizeof(s_vpi_vecval)); if (width == UINT_MAX) words = (unsigned)varlen; else words = (width+31)/32; for (word = 0; word < words; word += 1) { int byte; PLI_INT32 bits = 0; #ifdef WORDS_BIGENDIAN for (byte = 3; byte >= 0; byte -= 1) { #else for (byte = 0; byte <= 3; byte += 1) { #endif int ch = byte_getc(src); /* If there is an EOF while reading the bytes then treat * that as a non-match. It could be argued that the bytes * should be put back, but that is not practical and since * a binary read should be treated as an atomic operation * it's not helpful either. An EOF while reading is an * error in the data stream so print a message to help the * user debug what is going wrong. The calling routine has * already verified that there is at least one byte * available so this message is not printed when an EOF * occurs at a format code boundary. Which may not be an * error in the data stream. */ if (ch == EOF) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() only found %d of %u bytes needed by " "%%u format code.\n", name, word*4 + #ifdef WORDS_BIGENDIAN (3-byte), #else byte, #endif words*4U); free(val_ptr); return 0; } bits |= (ch & 0xff) << byte*8; } /* Only save the words that are in range. */ assert(varlen>=0); if (word < (unsigned)varlen) { val_ptr[word].aval = bits; val_ptr[word].bval = 0; } } /* Mask the upper bits to match the specified width when required. */ if (width != UINT_MAX) { PLI_INT32 mask = UINT32_MAX >> (32U - ((width - 1U) % 32U + 1U)); val_ptr[words-1].aval &= mask; } /* Not enough words were read to fill the variable so zero fill the * upper words. */ assert(varlen>=0); if (words < (unsigned)varlen) { for (word = words; word < (unsigned)varlen ; word += 1) { val_ptr[word].aval = 0; val_ptr[word].bval = 0; } } /* Put the two-state value into the variable. */ val.format = vpiVectorVal; val.value.vector = val_ptr; vpi_put_value(arg, &val, 0, vpiNoDelay); free(val_ptr); /* One variable was consumed. */ return 1; } /* * Routine to return a four state value (implements %z). * * Note: Since this is a binary routine it also does not check for leading * space characters or use space as a boundary character. * * Return: 1 for a match, 0 for no match/variable and -1 for a * suppressed match. No variable is fatal. */ static int scan_format_four_state(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, unsigned suppress_flag, PLI_BYTE8 *name) { vpiHandle arg; p_vpi_vecval val_ptr; s_vpi_value val; unsigned words, word; PLI_INT32 varlen; /* If we are being asked for no data then return a match fail. */ if (width == 0) return 0; /* Since this is a binary format we do not have an ending sequence. * Consequently we need to use the width to determine how many word * pairs to remove from the input stream. */ if (suppress_flag) { /* If no width was given then just remove one word pair. */ if (width == UINT_MAX) words = 1; else words = (width+31)/32; for (word = 0; word < words; word += 1) { unsigned byte; /* For a suppression we do not care about the endian order * of the bytes. */ for (byte = 0; byte < 8; byte += 1) { int ch = byte_getc(src); /* See the EOF comments below for more details. */ if (ch == EOF) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() only found %u of %u bytes " "needed by %%z format code.\n", name, word*8U + byte, words*8U); return 0; } } } return -1; } /* We must have a variable to put the bits into. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() ran out of variables for %%z format code.", name); vpi_control(vpiFinish, 1); return 0; } /* Extract either the given number of word pairs or enough to fill * the variable. */ varlen = (vpi_get(vpiSize, arg)+31)/32; assert(varlen > 0); val_ptr = (p_vpi_vecval) malloc(varlen*sizeof(s_vpi_vecval)); if (width == UINT_MAX) words = (unsigned)varlen; else words = (width+31)/32; for (word = 0; word < words; word += 1) { unsigned elem; for (elem = 0; elem < 2; elem += 1) { int byte; PLI_INT32 bits = 0; #ifdef WORDS_BIGENDIAN for (byte = 3; byte >= 0; byte -= 1) { #else for (byte = 0; byte <= 3; byte += 1) { #endif int ch = byte_getc(src); /* If there is an EOF while reading the bytes then * treat that as a non-match. It could be argued that * the bytes should be put back, but that is not * practical and since a binary read should be * treated as an atomic operation it's not helpful * either. An EOF while reading is an error in the * data stream so print a message to help the user * debug what is going wrong. The calling routine has * already verified that there is at least one byte * available so this message is not printed when an * EOF occurs at a format code boundary. Which may * not be an error in the data stream. */ if (ch == EOF) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() only found %d of %u bytes " "needed by %%z format code.\n", name, word*8 + elem*4 + #ifdef WORDS_BIGENDIAN (3-byte), #else byte, #endif words*8U); free(val_ptr); return 0; } bits |= (ch & 0xff) << byte*8; } /* Only save the words that are in range. */ if (word < (unsigned)varlen) { *(&val_ptr[word].aval+elem) = bits; } } } /* Mask the upper bits to match the specified width when required. */ if (width != UINT_MAX) { PLI_INT32 mask = UINT32_MAX >> (32U - ((width - 1U) % 32U + 1U)); val_ptr[words-1].aval &= mask; val_ptr[words-1].bval &= mask; } /* Not enough words were read to fill the variable so zero fill the * upper words. */ assert(varlen>=0); if (words < (unsigned)varlen) { for (word = words; word < (unsigned)varlen ; word += 1) { val_ptr[word].aval = 0; val_ptr[word].bval = 0; } } /* Put the four-state value into the variable. */ val.format = vpiVectorVal; val.value.vector = val_ptr; vpi_put_value(arg, &val, 0, vpiNoDelay); free(val_ptr); /* One variable was consumed. */ return 1; } /* * The $fscanf and $sscanf functions are the same except for the first * argument, which is the source. The wrapper functions below peel off * the first argument and make a byte_source object that then gets * passed to this function, which processes the rest of the function. */ static int scan_format(vpiHandle callh, struct byte_source*src, vpiHandle argv, PLI_BYTE8 *name) { s_vpi_value val; vpiHandle item; PLI_INT32 len, words, idx, mask; char *fmt, *fmtp; int rc = 0; int ch; int match = 1; /* Get the format string. */ item = vpi_scan(argv); assert(item); /* Look for an undefined bit (X/Z) in the format string. If one is * found just return EOF. */ len = vpi_get(vpiSize, item); words = ((len + 31) / 32) - 1; val.format = vpiVectorVal; vpi_get_value(item, &val); /* Check the full words for an undefined bit. */ for (idx = 0; idx < words; idx += 1) { if (val.value.vector[idx].bval) { match = 0; rc = EOF; break; } } /* The mask is defined to be 32 bits. */ mask = UINT32_MAX >> (32U - ((len - 1U) % 32U + 1U)); /* Check the top word for an undefined bit. */ if (match && (val.value.vector[words].bval & mask)) { match = 0; rc = EOF; } /* Now get the format as a string. */ val.format = vpiStringVal; vpi_get_value(item, &val); fmtp = fmt = strdup(val.value.str); /* See if we are at EOF before we even start. */ ch = byte_getc(src); if (ch == EOF) { rc = EOF; match = 0; } byte_ungetc(src, ch); while ( fmtp && *fmtp != 0 && match) { if (isspace((int)*fmtp)) { /* White space matches a string of white space in the * input. The number of spaces is not relevant, and * the match may be 0 or more spaces. */ while (*fmtp && isspace((int)*fmtp)) fmtp += 1; ch = byte_getc(src); while (isspace(ch)) ch = byte_getc(src); byte_ungetc(src, ch); } else if (*fmtp != '%') { /* Characters other than % match themselves. */ ch = byte_getc(src); if (ch != *fmtp) { byte_ungetc(src, ch); break; } fmtp += 1; } else { /* We are at a pattern character. The pattern has * the format %x no matter what the x code, so * parse it generically first. */ unsigned suppress_flag = 0; unsigned max_width = UINT_MAX; int code = 0; /* Look for the suppression character '*'. */ fmtp += 1; if (*fmtp == '*') { suppress_flag = 1; fmtp += 1; } /* Look for the maximum match width. */ if (isdigit((int)*fmtp)) { max_width = 0; while (isdigit((int)*fmtp)) { max_width *= 10; max_width += *fmtp - '0'; fmtp += 1; } } /* Get the format character. */ code = *fmtp; fmtp += 1; /* The format string is parsed: * - max_width is the width, * - code is the format code character, * - suppress_flag is true if the match is to be ignored. * Now interpret the code. */ switch (code) { /* Read a '%' character from the input. */ case '%': assert(max_width == -1U); assert(suppress_flag == 0); ch = byte_getc(src); if (ch != '%') { byte_ungetc(src, ch); match = 0; } break; case 'b': match = scan_format_binary(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 'c': match = scan_format_char(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 'd': match = scan_format_decimal(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 'e': case 'f': case 'g': match = scan_format_float(callh, argv, src, max_width, suppress_flag, name, code); if (match == 1) rc += 1; break; case 'h': case 'x': match = scan_format_hex(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 'm': /* Since this code does not consume any characters * the width makes no difference. */ match = scan_format_module_path(callh, argv, suppress_flag, name); if (match == 1) rc += 1; break; case 'o': match = scan_format_octal(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 's': match = scan_format_string(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 't': match = scan_format_float_time(callh, argv, src, max_width, suppress_flag, name); if (match == 1) rc += 1; break; case 'u': match = scan_format_two_state(callh, argv, src, max_width, suppress_flag, name); /* If a binary match fails and it is the first item * matched then treat that as an EOF. */ if ((match == 0) && (rc == 0)) rc = EOF; if (match == 1) rc += 1; break; case 'v': vpi_printf("SORRY: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() format code '%%%c' is not " "currently supported.\n", name, code); vpi_control(vpiFinish, 1); break; case 'z': match = scan_format_four_state(callh, argv, src, max_width, suppress_flag, name); /* If a binary match fails and it is the first item * matched then treat that as an EOF. */ if ((match == 0) && (rc == 0)) rc = EOF; if (match == 1) rc += 1; break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() was given an invalid format code: " "%%%c\n", name, code); vpi_control(vpiFinish, 1); break; } } } /* Clean up the allocated memory. */ free(fmt); vpi_free_object(argv); /* Return the number of successful matches. */ val.format = vpiIntVal; val.value.integer = rc; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Is the object a variable/register or a piece of one. */ static int is_assignable_obj(vpiHandle obj) { unsigned rtn = 0; assert(obj); switch(vpi_get(vpiType, obj)) { /* We can not assign to a vpiNetArray. */ case vpiMemoryWord: if (vpi_get(vpiType, vpi_handle(vpiParent, obj)) == vpiMemory) { rtn = 1; } break; case vpiPartSelect: if (! is_assignable_obj(vpi_handle(vpiParent, obj))) break; case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiReg: case vpiTimeVar: case vpiStringVar: rtn = 1; break; } return rtn; } static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8 *name) { vpiHandle arg; int cnt = 3, rtn = 0; /* The format (2nd) argument must be a string. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least three argument.\n", name); return 1; } if(! is_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s format argument must be a string.\n", name); rtn = 1; } /* The rest of the arguments must be assignable. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least three argument.\n", name); return 1; } do { if (! is_assignable_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s argument %d (a %s) is not assignable.\n", name, cnt, vpi_get_str(vpiType, arg)); rtn = 1; } arg = vpi_scan(argv); cnt += 1; } while (arg); return rtn; } static PLI_INT32 sys_fscanf_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least three argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a file descriptor. */ if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument (fd) must be numeric.\n", name); vpi_control(vpiFinish, 1); vpi_free_object(argv); return 0; } if (sys_check_args(callh, argv, name)) vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 sys_fscanf_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; struct byte_source src; FILE *fd; errno = 0; val.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &val); fd = vpi_get_file(val.value.integer); if (!fd) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("invalid file descriptor (0x%x) given to %s.\n", (int)val.value.integer, name); errno = EBADF; val.format = vpiIntVal; val.value.integer = EOF; vpi_put_value(callh, &val, 0, vpiNoDelay); vpi_free_object(argv); return 0; } src.str = 0; src.fd = fd; scan_format(callh, &src, argv, name); return 0; } static PLI_INT32 sys_sscanf_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle reg; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least three argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a register or a string. */ reg = vpi_scan(argv); /* This should never be zero. */ switch(vpi_get(vpiType, reg)) { case vpiReg: case vpiStringVar: break; case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, reg) == vpiStringConst) break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a register or a string.\n", name); vpi_control(vpiFinish, 1); vpi_free_object(argv); return 0; } if (sys_check_args(callh, argv, name)) vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 sys_sscanf_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; struct byte_source src; char *str; val.format = vpiStringVal; vpi_get_value(vpi_scan(argv), &val); str = strdup(val.value.str); src.str = str; src.fd = 0; scan_format(callh, &src, argv, name); free(str); return 0; } void sys_scanf_register(void) { s_vpi_systf_data tf_data; vpiHandle res; /*============================== fscanf */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fscanf"; tf_data.calltf = sys_fscanf_calltf; tf_data.compiletf = sys_fscanf_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fscanf"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /*============================== sscanf */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$sscanf"; tf_data.calltf = sys_sscanf_calltf; tf_data.compiletf = sys_sscanf_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$sscanf"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_sdf.c000066400000000000000000000241111265551621300157550ustar00rootroot00000000000000/* * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include "sdf_priv.h" # include # include # include /* * These are static context */ int sdf_flag_warning = 0; int sdf_flag_inform = 0; int sdf_min_typ_max; /* Scope of the $sdf_annotate call. Annotation starts here. */ static vpiHandle sdf_scope; static vpiHandle sdf_callh = 0; /* The cell in process. */ static vpiHandle sdf_cur_cell; static vpiHandle find_scope(vpiHandle scope, const char*name) { vpiHandle idx = vpi_iterate(vpiModule, scope); /* If this scope has no modules then it can't have the one we * are looking for so just return 0. */ if (idx == 0) return 0; vpiHandle cur; while ( (cur = vpi_scan(idx)) ) { if ( strcmp(name, vpi_get_str(vpiName,cur)) == 0) { vpi_free_object(idx); return cur; } } return 0; } /* * These functions are called by the SDF parser during parsing to * handling items discovered in the parse. */ void sdf_select_instance(const char*celltype, const char*cellinst) { char buffer[128]; /* First follow the hierarchical parts of the cellinst name to get to the cell that I'm looking for. */ vpiHandle scope = sdf_scope; const char*src = cellinst; const char*dp; while ( (dp=strchr(src, '.')) ) { unsigned len = dp - src; assert(dp >= src); assert(len < sizeof buffer); strncpy(buffer, src, len); buffer[len] = 0; vpiHandle tmp_scope = find_scope(scope, buffer); if (tmp_scope == 0) { vpi_printf("SDF WARNING: %s:%d: ", vpi_get_str(vpiFile, sdf_callh), (int)vpi_get(vpiLineNo, sdf_callh)); vpi_printf("Cannot find %s in scope %s.\n", buffer, vpi_get_str(vpiFullName, scope)); break; } assert(tmp_scope); scope = tmp_scope; src = dp + 1; } /* Now find the cell. */ if (src[0] == 0) sdf_cur_cell = sdf_scope; else sdf_cur_cell = find_scope(scope, src); if (sdf_cur_cell == 0) { vpi_printf("SDF WARNING: %s:%d: ", vpi_get_str(vpiFile, sdf_callh), (int)vpi_get(vpiLineNo, sdf_callh)); vpi_printf("Unable to find %s in scope %s.\n", src, vpi_get_str(vpiFullName, scope)); return; } /* The scope that matches should be a module. */ if (vpi_get(vpiType,sdf_cur_cell) != vpiModule) { vpi_printf("SDF WARNING: %s:%d: ", vpi_get_str(vpiFile, sdf_callh), (int)vpi_get(vpiLineNo, sdf_callh)); vpi_printf("Scope %s in %s is not a module.\n", src, vpi_get_str(vpiFullName, scope)); } /* The matching scope (a module) should have the expected type. */ if (strcmp(celltype,vpi_get_str(vpiDefName,sdf_cur_cell)) != 0) { vpi_printf("SDF WARNING: %s:%d: ", vpi_get_str(vpiFile, sdf_callh), (int)vpi_get(vpiLineNo, sdf_callh)); vpi_printf("Module %s in %s is not a %s; it is a ", src, vpi_get_str(vpiFullName, scope), celltype); vpi_printf("%s\n", vpi_get_str(vpiDefName, sdf_cur_cell)); } } static const char*edge_str(int vpi_edge) { if (vpi_edge == vpiNoEdge) return ""; if (vpi_edge == vpiPosedge) return "posedge "; if (vpi_edge == vpiNegedge) return "negedge "; return "edge.. "; } void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst, const struct sdf_delval_list_s*delval_list) { vpiHandle iter, path; int match_count = 0; if (sdf_cur_cell == 0) return; iter = vpi_iterate(vpiModPath, sdf_cur_cell); /* Search for the modpath that matches the IOPATH by looking for the modpath that uses the same ports as the ports that the parser has found. */ if (iter) while ( (path = vpi_scan(iter)) ) { s_vpi_delay delays; struct t_vpi_time delay_vals[12]; int idx; vpiHandle path_t_in = vpi_handle(vpiModPathIn,path); vpiHandle path_t_out = vpi_handle(vpiModPathOut,path); vpiHandle path_in = vpi_handle(vpiExpr,path_t_in); vpiHandle path_out = vpi_handle(vpiExpr,path_t_out); /* The expressions for the path terms must be signals, vpiNet or vpiReg. */ assert(vpi_get(vpiType,path_in) == vpiNet); assert(vpi_get(vpiType,path_out) == vpiNet || vpi_get(vpiType,path_out) == vpiReg); /* If the src name doesn't match, go on. */ if (strcmp(src,vpi_get_str(vpiName,path_in)) != 0) continue; /* The edge type must match too. But note that if this IOPATH has no edge, then it matches with all edges of the modpath object. */ /* --> Is this correct in the context of the 10, 01, etc. edges? */ if (vpi_edge != vpiNoEdge && vpi_get(vpiEdge,path_t_in) != vpi_edge) continue; /* If the dst name doesn't match, go on. */ if (strcmp(dst,vpi_get_str(vpiName,path_out)) != 0) continue; /* Ah, this must be a match! */ delays.da = delay_vals; delays.no_of_delays = delval_list->count; delays.time_type = vpiScaledRealTime; delays.mtm_flag = 0; delays.append_flag = 0; delays.plusere_flag = 0; vpi_get_delays(path, &delays); for (idx = 0 ; idx < delval_list->count ; idx += 1) { delay_vals[idx].type = vpiScaledRealTime; if (delval_list->val[idx].defined) { delay_vals[idx].real = delval_list->val[idx].value; } } vpi_put_delays(path, &delays); match_count += 1; } if (match_count == 0) { vpi_printf("SDF WARNING: %s:%d: ", vpi_get_str(vpiFile, sdf_callh), (int)vpi_get(vpiLineNo, sdf_callh)); vpi_printf("Unable to match ModPath %s%s -> %s in %s\n", edge_str(vpi_edge), src, dst, vpi_get_str(vpiFullName, sdf_cur_cell)); } } static void check_command_line_args(void) { struct t_vpi_vlog_info vlog_info; int idx; static int sdf_command_line_done = 0; if (sdf_command_line_done) return; vpi_get_vlog_info(&vlog_info); for (idx = 0 ; idx < vlog_info.argc ; idx += 1) { if (strcmp(vlog_info.argv[idx],"-sdf-warn") == 0) { sdf_flag_warning = 1; } else if (strcmp(vlog_info.argv[idx],"-sdf-info") == 0) { sdf_flag_inform = 1; } else if (strcmp(vlog_info.argv[idx],"-sdf-verbose") == 0) { sdf_flag_warning = 1; sdf_flag_inform = 1; } } sdf_command_line_done = 1; } static PLI_INT32 sys_sdf_annotate_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall,0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle module; check_command_line_args(); /* Check that we have a file name argument. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a file name argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_string_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's file name must be a string.\n", name); vpi_control(vpiFinish, 1); } /* The module argument is optional. */ module = vpi_scan(argv); if (module == 0) return 0; if (vpi_get(vpiType, module) != vpiModule) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be a module instance.\n", name); vpi_control(vpiFinish, 1); } /* Warn the user that we only use the first two arguments. */ if (vpi_scan(argv) != 0) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s currently only uses the first two argument.\n", name); vpi_free_object(argv); } return 0; } static PLI_INT32 sys_sdf_annotate_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); FILE *sdf_fd; char *fname = get_filename(callh, name, vpi_scan(argv)); if (fname == 0) return 0; sdf_fd = fopen(fname, "r"); if (sdf_fd == 0) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unable to open SDF file \"%s\"." " Skipping this annotation.\n", fname); return 0; } /* The optional second argument is the scope to annotate. */ sdf_scope = vpi_scan(argv); if (sdf_scope) vpi_free_object(argv); else sdf_scope = vpi_handle(vpiScope, callh); /* Select which delay to use. */ sdf_min_typ_max = vpi_get(_vpiDelaySelection, 0); sdf_cur_cell = 0; sdf_callh = callh; sdf_process_file(sdf_fd, fname); sdf_callh = 0; fclose(sdf_fd); free(fname); return 0; } void sys_sdf_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysTask; tf_data.tfname = "$sdf_annotate"; tf_data.calltf = sys_sdf_annotate_calltf; tf_data.compiletf = sys_sdf_annotate_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$sdf_annotate"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_table.c000066400000000000000000000145431265551621300163000ustar00rootroot00000000000000/* * Copyright (c) 1999-2010,2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vpi_config.h" # include "vpi_user.h" # include # include # include extern void sys_convert_register(void); extern void sys_countdrivers_register(void); extern void sys_darray_register(void); extern void sys_fileio_register(void); extern void sys_finish_register(void); extern void sys_deposit_register(void); extern void sys_display_register(void); extern void sys_plusargs_register(void); extern void sys_queue_register(void); extern void sys_random_register(void); extern void sys_random_mti_register(void); extern void sys_readmem_register(void); extern void sys_scanf_register(void); extern void sys_sdf_register(void); extern void sys_time_register(void); extern void sys_vcd_register(void); extern void sys_vcdoff_register(void); extern void sys_special_register(void); extern void table_model_register(void); extern void vams_simparam_register(void); #ifdef HAVE_LIBZ #ifdef HAVE_LIBBZ2 extern void sys_lxt_register(void); #else static void sys_lxt_register(void) { fputs("LXT support disabled since libbzip2 not available\n",stderr); exit(1); } #endif extern void sys_lxt2_register(void); extern void sys_fst_register(void); #else static void sys_lxt_register(void) { fputs("LXT support disabled since zlib not available\n",stderr); exit(1); } static void sys_lxt2_register(void) { fputs("LXT2 support disabled since zlib not available\n",stderr); exit(1); } static void sys_fst_register(void) { fputs("FST support disabled since zlib not available\n",stderr); exit(1); } #endif static void sys_lxt_or_vcd_register(void) { int idx; struct t_vpi_vlog_info vlog_info; const char*dumper; /* Get the dumper of choice from the IVERILOG_DUMPER environment variable. */ dumper = getenv("IVERILOG_DUMPER"); if (dumper) { char*cp = strchr(dumper,'='); if (cp != 0) dumper = cp + 1; } else { dumper = "vcd"; } /* Scan the extended arguments, looking for flags that select major features. This can override the environment variable settings. */ vpi_get_vlog_info(&vlog_info); for (idx = 0 ; idx < vlog_info.argc ; idx += 1) { if (strcmp(vlog_info.argv[idx],"-fst") == 0) { dumper = "fst"; } else if (strcmp(vlog_info.argv[idx],"-fst-space") == 0) { dumper = "fst"; } else if (strcmp(vlog_info.argv[idx],"-fst-speed") == 0) { dumper = "fst"; } else if (strcmp(vlog_info.argv[idx],"-fst-space-speed") == 0) { dumper = "fst"; } else if (strcmp(vlog_info.argv[idx],"-fst-speed-space") == 0) { dumper = "fst"; } else if (strcmp(vlog_info.argv[idx],"-fst-none") == 0) { dumper = "none"; } else if (strcmp(vlog_info.argv[idx],"-lxt") == 0) { dumper = "lxt"; } else if (strcmp(vlog_info.argv[idx],"-lxt-space") == 0) { dumper = "lxt"; } else if (strcmp(vlog_info.argv[idx],"-lxt-speed") == 0) { dumper = "lxt"; } else if (strcmp(vlog_info.argv[idx],"-lxt-none") == 0) { dumper = "none"; } else if (strcmp(vlog_info.argv[idx],"-lxt2") == 0) { dumper = "lxt2"; } else if (strcmp(vlog_info.argv[idx],"-lxt2-space") == 0) { dumper = "lxt2"; } else if (strcmp(vlog_info.argv[idx],"-lxt2-speed") == 0) { dumper = "lxt2"; } else if (strcmp(vlog_info.argv[idx],"-lxt2-none") == 0) { dumper = "none"; } else if (strcmp(vlog_info.argv[idx],"-lx2") == 0) { dumper = "lxt2"; } else if (strcmp(vlog_info.argv[idx],"-lx2-space") == 0) { dumper = "lxt2"; } else if (strcmp(vlog_info.argv[idx],"-lx2-speed") == 0) { dumper = "lxt2"; } else if (strcmp(vlog_info.argv[idx],"-lx2-none") == 0) { dumper = "none"; } else if (strcmp(vlog_info.argv[idx],"-vcd") == 0) { dumper = "vcd"; } else if (strcmp(vlog_info.argv[idx],"-vcd-off") == 0) { dumper = "none"; } else if (strcmp(vlog_info.argv[idx],"-vcd-none") == 0) { dumper = "none"; } else if (strcmp(vlog_info.argv[idx],"-none") == 0) { dumper = "none"; } } if (strcmp(dumper, "vcd") == 0) sys_vcd_register(); else if (strcmp(dumper, "VCD") == 0) sys_vcd_register(); else if (strcmp(dumper, "fst") == 0) sys_fst_register(); else if (strcmp(dumper, "FST") == 0) sys_fst_register(); else if (strcmp(dumper, "lxt") == 0) sys_lxt_register(); else if (strcmp(dumper, "LXT") == 0) sys_lxt_register(); else if (strcmp(dumper, "lxt2") == 0) sys_lxt2_register(); else if (strcmp(dumper, "LXT2") == 0) sys_lxt2_register(); else if (strcmp(dumper, "lx2") == 0) sys_lxt2_register(); else if (strcmp(dumper, "LX2") == 0) sys_lxt2_register(); else if (strcmp(dumper, "none") == 0) sys_vcdoff_register(); else if (strcmp(dumper, "NONE") == 0) sys_vcdoff_register(); else { vpi_mcd_printf(1, "system.vpi: Unknown dumper format: %s," " using VCD instead.\n", dumper); sys_vcd_register(); } } void (*vlog_startup_routines[])(void) = { sys_convert_register, sys_countdrivers_register, sys_darray_register, sys_fileio_register, sys_finish_register, sys_deposit_register, sys_display_register, sys_plusargs_register, sys_queue_register, sys_random_register, sys_random_mti_register, sys_readmem_register, sys_scanf_register, sys_time_register, sys_lxt_or_vcd_register, sys_sdf_register, sys_special_register, table_model_register, vams_simparam_register, 0 }; iverilog-10_1/vpi/sys_time.c000066400000000000000000000116001265551621300161360ustar00rootroot00000000000000/* * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sys_priv.h" #include #include #include static PLI_INT32 sys_time_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_value val; s_vpi_time now; vpiHandle call_handle; vpiHandle mod; int units, prec; long scale; call_handle = vpi_handle(vpiSysTfCall, 0); assert(call_handle); mod = sys_func_module(call_handle); now.type = vpiSimTime; vpi_get_time(0, &now); /* All the variants but $simtime return the time in units of the local scope. The $simtime function returns the simulation time. */ if (strcmp(name, "$simtime") == 0) units = vpi_get(vpiTimePrecision, 0); else units = vpi_get(vpiTimeUnit, mod); prec = vpi_get(vpiTimePrecision, 0); scale = 1; while (units > prec) { scale *= 10; units -= 1; } assert(8*sizeof(long long) >= 64); { long frac; long long tmp_now = ((long long)now.high) << 32; tmp_now += (long long)now.low; frac = tmp_now % (long long)scale; tmp_now /= (long long)scale; /* Round to the nearest integer, which may be up. */ if ((scale > 1) && (frac >= scale/2)) tmp_now += 1; now.low = tmp_now & 0xffffffff; now.high = tmp_now >> 32LL; } val.format = vpiTimeVal; val.value.time = &now; vpi_put_value(call_handle, &val, 0, vpiNoDelay); return 0; } /* This also supports $abstime() from VAMS-2.3. */ static PLI_INT32 sys_realtime_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_value val; s_vpi_time now; vpiHandle callh; vpiHandle mod; callh = vpi_handle(vpiSysTfCall, 0); assert(callh); mod = sys_func_module(callh); now.type = vpiScaledRealTime; vpi_get_time(mod, &now); /* For $abstime() we return the time in second. */ if (strcmp(name, "$abstime") == 0) { PLI_INT32 scale = vpi_get(vpiTimeUnit, mod); if (scale >= 0) now.real *= pow(10.0, scale); else now.real /= pow(10.0, -scale); } val.format = vpiRealVal; val.value.real = now.real; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } void sys_time_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.tfname = "$time"; tf_data.sysfunctype = vpiTimeFunc; tf_data.calltf = sys_time_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$time"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.tfname = "$realtime"; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = sys_realtime_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$realtime"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.tfname = "$stime"; tf_data.sysfunctype = vpiIntFunc; tf_data.calltf = sys_time_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$stime"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.tfname = "$simtime"; tf_data.sysfunctype = vpiTimeFunc; tf_data.calltf = sys_time_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$simtime"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.tfname = "$abstime"; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = sys_realtime_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$abstime"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_vcd.c000066400000000000000000000622631265551621300157670ustar00rootroot00000000000000/* * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include "vcd_priv.h" /* * This file contains the implementations of the VCD related functions. */ # include # include # include # include # include # include "ivl_alloc.h" static char *dump_path = NULL; static FILE *dump_file = NULL; struct vcd_info { vpiHandle item; vpiHandle cb; struct t_vpi_time time; const char *ident; struct vcd_info *next; struct vcd_info *dmp_next; int scheduled; }; static struct vcd_info *vcd_list = NULL; static struct vcd_info *vcd_dmp_list = NULL; static PLI_UINT64 vcd_cur_time = 0; static int dump_is_off = 0; static long dump_limit = 0; static int dump_is_full = 0; static int finish_status = 0; static const char*units_names[] = { "s", "ms", "us", "ns", "ps", "fs" }; static char vcdid[8] = "!"; static void gen_new_vcd_id(void) { static unsigned value = 0; unsigned v = ++value; unsigned int i; for (i=0; i < sizeof(vcdid)-1; i++) { vcdid[i] = (char)((v%94)+33); /* for range 33..126 */ v /= 94; if(!v) { vcdid[i+1] = '\0'; return; } } // This should never happen since 94**7 is a lot if identifiers! assert(0); } static char *truncate_bitvec(char *s) { char r; r=*s; if(r=='1') return s; else s += 1; for(;;s++) { char l; l=r; r=*s; if(!r) return (s-1); if(l!=r) return(((l=='0')&&(r=='1'))?s:s-1); } } static void show_this_item(struct vcd_info*info) { s_vpi_value value; PLI_INT32 type = vpi_get(vpiType, info->item); if (type == vpiRealVar) { value.format = vpiRealVal; vpi_get_value(info->item, &value); fprintf(dump_file, "r%.16g %s\n", value.value.real, info->ident); } else if (type == vpiNamedEvent) { fprintf(dump_file, "1%s\n", info->ident); } else if (vpi_get(vpiSize, info->item) == 1) { value.format = vpiBinStrVal; vpi_get_value(info->item, &value); fprintf(dump_file, "%s%s\n", value.value.str, info->ident); } else { value.format = vpiBinStrVal; vpi_get_value(info->item, &value); fprintf(dump_file, "b%s %s\n", truncate_bitvec(value.value.str), info->ident); } } /* Dump values for a $dumpoff. */ static void show_this_item_x(struct vcd_info*info) { PLI_INT32 type = vpi_get(vpiType, info->item); if (type == vpiRealVar) { /* Some tools dump nothing here...? */ fprintf(dump_file, "rNaN %s\n", info->ident); } else if (type == vpiNamedEvent) { /* Do nothing for named events. */ } else if (vpi_get(vpiSize, info->item) == 1) { fprintf(dump_file, "x%s\n", info->ident); } else { fprintf(dump_file, "bx %s\n", info->ident); } } /* * managed qsorted list of scope names/variables for duplicates bsearching */ struct vcd_names_list_s vcd_tab = { 0, 0, 0, 0 }; struct vcd_names_list_s vcd_var = { 0, 0, 0, 0 }; static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */ static PLI_UINT64 dumpvars_time; __inline__ static int dump_header_pending(void) { return dumpvars_status != 2; } /* * This function writes out all the traced variables, whether they * changed or not. */ static void vcd_checkpoint(void) { struct vcd_info*cur; for (cur = vcd_list ; cur ; cur = cur->next) show_this_item(cur); } static void vcd_checkpoint_x(void) { struct vcd_info*cur; for (cur = vcd_list ; cur ; cur = cur->next) show_this_item_x(cur); } static PLI_INT32 variable_cb_2(p_cb_data cause) { struct vcd_info* info = vcd_dmp_list; PLI_UINT64 now = timerec_to_time64(cause->time); if (now != vcd_cur_time) { fprintf(dump_file, "#%" PLI_UINT64_FMT "\n", now); vcd_cur_time = now; } do { show_this_item(info); info->scheduled = 0; } while ((info = info->dmp_next) != 0); vcd_dmp_list = 0; return 0; } static PLI_INT32 variable_cb_1(p_cb_data cause) { struct t_cb_data cb; struct vcd_info*info = (struct vcd_info*)cause->user_data; if (dump_is_full) return 0; if (dump_is_off) return 0; if (dump_header_pending()) return 0; if (info->scheduled) return 0; if ((dump_limit > 0) && (ftell(dump_file) > dump_limit)) { dump_is_full = 1; vpi_printf("WARNING: Dump file limit (%ld bytes) " "exceeded.\n", dump_limit); fprintf(dump_file, "$comment Dump file limit (%ld bytes) " "exceeded. $end\n", dump_limit); return 0; } if (!vcd_dmp_list) { cb = *cause; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); } info->scheduled = 1; info->dmp_next = vcd_dmp_list; vcd_dmp_list = info; return 0; } static PLI_INT32 dumpvars_cb(p_cb_data cause) { if (dumpvars_status != 1) return 0; dumpvars_status = 2; dumpvars_time = timerec_to_time64(cause->time); vcd_cur_time = dumpvars_time; fprintf(dump_file, "$enddefinitions $end\n"); if (!dump_is_off) { fprintf(dump_file, "#%" PLI_UINT64_FMT "\n", dumpvars_time); fprintf(dump_file, "$dumpvars\n"); vcd_checkpoint(); fprintf(dump_file, "$end\n"); } return 0; } static PLI_INT32 finish_cb(p_cb_data cause) { struct vcd_info *cur, *next; if (finish_status != 0) return 0; finish_status = 1; dumpvars_time = timerec_to_time64(cause->time); if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) { fprintf(dump_file, "#%" PLI_UINT64_FMT "\n", dumpvars_time); } fclose(dump_file); for (cur = vcd_list ; cur ; cur = next) { next = cur->next; free((char *)cur->ident); free(cur); } vcd_list = 0; vcd_names_delete(&vcd_tab); vcd_names_delete(&vcd_var); nexus_ident_delete(); free(dump_path); dump_path = 0; return 0; } __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; static struct t_vpi_time now; if (dumpvars_status == 1) return 0; if (dumpvars_status == 2) { vpi_printf("VCD warning: $dumpvars ignored, previously" " called at simtime %" PLI_UINT64_FMT "\n", dumpvars_time); return 1; } now.type = vpiSimTime; cb.time = &now; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; cb.obj = 0x0; vpi_register_cb(&cb); cb.reason = cbEndOfSimulation; cb.cb_rtn = finish_cb; vpi_register_cb(&cb); dumpvars_status = 1; return 0; } static PLI_INT32 sys_dumpoff_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; dump_is_off = 1; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { fprintf(dump_file, "#%" PLI_UINT64_FMT "\n", now64); vcd_cur_time = now64; } fprintf(dump_file, "$dumpoff\n"); vcd_checkpoint_x(); fprintf(dump_file, "$end\n"); return 0; } static PLI_INT32 sys_dumpon_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (!dump_is_off) return 0; dump_is_off = 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { fprintf(dump_file, "#%" PLI_UINT64_FMT "\n", now64); vcd_cur_time = now64; } fprintf(dump_file, "$dumpon\n"); vcd_checkpoint(); fprintf(dump_file, "$end\n"); return 0; } static PLI_INT32 sys_dumpall_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { s_vpi_time now; PLI_UINT64 now64; (void)name; /* Parameter is not used. */ if (dump_is_off) return 0; if (dump_file == 0) return 0; if (dump_header_pending()) return 0; now.type = vpiSimTime; vpi_get_time(0, &now); now64 = timerec_to_time64(&now); if (now64 > vcd_cur_time) { fprintf(dump_file, "#%" PLI_UINT64_FMT "\n", now64); vcd_cur_time = now64; } fprintf(dump_file, "$dumpall\n"); vcd_checkpoint(); fprintf(dump_file, "$end\n"); return 0; } static void open_dumpfile(vpiHandle callh) { if (dump_path == 0) dump_path = strdup("dump.vcd"); dump_file = fopen(dump_path, "w"); if (dump_file == 0) { vpi_printf("VCD Error: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); free(dump_path); dump_path = 0; return; } else { int prec = vpi_get(vpiTimePrecision, 0); unsigned scale = 1; unsigned udx = 0; time_t walltime; vpi_printf("VCD info: dumpfile %s opened for output.\n", dump_path); time(&walltime); assert(prec >= -15); while (prec < 0) { udx += 1; prec += 3; } while (prec > 0) { scale *= 10; prec -= 1; } fprintf(dump_file, "$date\n"); fprintf(dump_file, "\t%s",asctime(localtime(&walltime))); fprintf(dump_file, "$end\n"); fprintf(dump_file, "$version\n"); fprintf(dump_file, "\tIcarus Verilog\n"); fprintf(dump_file, "$end\n"); fprintf(dump_file, "$timescale\n"); fprintf(dump_file, "\t%u%s\n", scale, units_names[udx]); fprintf(dump_file, "$end\n"); } } static PLI_INT32 sys_dumpfile_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); char *path; /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { char msg[64]; snprintf(msg, sizeof(msg), "VCD warning: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; vpi_printf("%s %s called after $dumpvars started,\n", msg, name); vpi_printf("%*s using existing file (%s).\n", (int) strlen(msg), " ", dump_path); vpi_free_object(argv); return 0; } path = get_filename(callh, name, vpi_scan(argv)); vpi_free_object(argv); if (! path) return 0; if (dump_path) { vpi_printf("VCD warning: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Overriding dump file %s with %s.\n", dump_path, path); free(dump_path); } dump_path = path; return 0; } static PLI_INT32 sys_dumpflush_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ if (dump_file) fflush(dump_file); return 0; } static PLI_INT32 sys_dumplimit_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); s_vpi_value val; (void)name; /* Parameter is not used. */ /* Get the value and set the dump limit. */ val.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &val); dump_limit = val.value.integer; vpi_free_object(argv); return 0; } static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; const char *type; const char *name; const char *fullname; const char *prefix; const char *ident; int nexus_id; unsigned size; PLI_INT32 item_type; /* Get the displayed type for the various $var and $scope types. */ /* Not all of these are supported now, but they should be in a * future development version. */ item_type = vpi_get(vpiType, item); switch (item_type) { case vpiNamedEvent: type = "event"; break; case vpiIntVar: case vpiIntegerVar: type = "integer"; break; case vpiParameter: type = "parameter"; break; /* Icarus converts realtime to real. */ case vpiRealVar: type = "real"; break; case vpiMemoryWord: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiLongIntVar: case vpiReg: type = "reg"; break; /* Icarus converts a time to a plain register. */ case vpiTimeVar: type = "time"; break; case vpiNet: switch (vpi_get(vpiNetType, item)) { case vpiWand: type = "wand"; break; case vpiWor: type = "wor"; break; case vpiTri: type = "tri"; break; case vpiTri0: type = "tri0"; break; case vpiTri1: type = "tri1"; break; case vpiTriReg: type = "trireg"; break; case vpiTriAnd: type = "triand"; break; case vpiTriOr: type = "trior"; break; case vpiSupply1: type = "supply1"; break; case vpiSupply0: type = "supply0"; break; default: type = "wire"; break; } break; case vpiNamedBegin: type = "begin"; break; case vpiGenScope: type = "begin"; break; case vpiNamedFork: type = "fork"; break; case vpiFunction: type = "function"; break; case vpiModule: type = "module"; break; case vpiTask: type = "task"; break; default: vpi_printf("VCD warning: $dumpvars: Unsupported argument " "type (%s)\n", vpi_get_str(vpiType, item)); return; } /* Do some special processing/checking on array words. Dumping * array words is an Icarus extension. */ if (item_type == vpiMemoryWord) { /* Turn a non-constant array word select into a constant * word select. */ if (vpi_get(vpiConstantSelect, item) == 0) { vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ /* This does not work as expected since we always find at * least the array word. We likely need a custom routine. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("VCD warning: array word %s will conflict " "with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } } fullname = vpi_get_str(vpiFullName, item); /* Generate the $var or $scope commands. */ switch (item_type) { case vpiParameter: vpi_printf("VCD sorry: $dumpvars: can not dump parameters.\n"); break; case vpiNamedEvent: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiMemoryWord: case vpiReg: case vpiTimeVar: case vpiNet: /* If we are skipping all signal or this is in an automatic * scope then just return. */ if (skip || vpi_get(vpiAutomatic, item)) return; /* Skip this signal if it has already been included. * This can only happen for implicitly given signals. */ if (vcd_names_search(&vcd_var, fullname)) return; /* Declare the variable in the VCD file. */ name = vpi_get_str(vpiName, item); prefix = is_escaped_id(name) ? "\\" : ""; /* Some signals can have an alias so handle that. */ nexus_id = vpi_get(_vpiNexusId, item); ident = 0; if (nexus_id) ident = find_nexus_ident(nexus_id); if (!ident) { ident = strdup(vcdid); gen_new_vcd_id(); if (nexus_id) set_nexus_ident(nexus_id, ident); /* Add a callback for the signal. */ info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->ident = ident; info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->dmp_next = 0; info->next = vcd_list; vcd_list = info; info->cb = vpi_register_cb(&cb); } /* Named events do not have a size, but other tools use * a size of 1 and some viewers do not accept a width of * zero so we will also use a width of one for events. */ if (item_type == vpiNamedEvent) size = 1; else size = vpi_get(vpiSize, item); fprintf(dump_file, "$var %s %u %s %s%s", type, size, ident, prefix, name); /* Add a range for vectored values. */ if (size > 1 || vpi_get(vpiLeftRange, item) != 0) { fprintf(dump_file, " [%i:%i]", (int)vpi_get(vpiLeftRange, item), (int)vpi_get(vpiRightRange, item)); } fprintf(dump_file, " $end\n"); break; case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: if (depth > 0) { /* list of types to iterate upon */ static int types[] = { /* Value */ vpiNamedEvent, vpiNet, /* vpiParameter, */ vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiGenScope, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; int i; int nskip = (vcd_names_search(&vcd_tab, fullname) != 0); /* We have to always scan the scope because the * depth could be different for this call. */ if (nskip) { vpi_printf("VCD warning: ignoring signals in " "previously scanned scope %s.\n", fullname); } else { vcd_names_add(&vcd_tab, fullname); } name = vpi_get_str(vpiName, item); fprintf(dump_file, "$scope %s %s $end\n", type, name); for (i=0; types[i]>0; i++) { vpiHandle hand; vpiHandle argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } /* Sort any signals that we added above. */ fprintf(dump_file, "$upscope $end\n"); } break; } } static int draw_scope(vpiHandle item, vpiHandle callh) { int depth; const char *name; const char *type; vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) return 0; depth = 1 + draw_scope(scope, callh); name = vpi_get_str(vpiName, scope); switch (vpi_get(vpiType, scope)) { case vpiNamedBegin: type = "begin"; break; case vpiGenScope: type = "begin"; break; case vpiTask: type = "task"; break; case vpiFunction: type = "function"; break; case vpiNamedFork: type = "fork"; break; case vpiModule: type = "module"; break; default: type = "invalid"; vpi_printf("VCD Error: %s:%d: $dumpvars: Unsupported scope " "type (%d)\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), (int)vpi_get(vpiType, item)); assert(0); } fprintf(dump_file, "$scope %s %s $end\n", type, name); return depth; } static PLI_INT32 sys_dumpvars_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle item; s_vpi_value value; unsigned depth = 0; (void)name; /* Parameter is not used. */ if (dump_file == 0) { open_dumpfile(callh); if (dump_file == 0) { if (argv) vpi_free_object(argv); return 0; } } if (install_dumpvars_callback()) { if (argv) vpi_free_object(argv); return 0; } /* Get the depth if it exists. */ if (argv) { value.format = vpiIntVal; vpi_get_value(vpi_scan(argv), &value); depth = value.value.integer; } if (!depth) depth = 10000; /* This dumps all the modules in the design if none are given. */ if (!argv || !(item = vpi_scan(argv))) { argv = vpi_iterate(vpiModule, 0x0); assert(argv); /* There must be at least one top level module. */ item = vpi_scan(argv); } for ( ; item; item = vpi_scan(argv)) { char *scname; const char *fullname; int add_var = 0; int dep; PLI_INT32 item_type = vpi_get(vpiType, item); /* If this is a signal make sure it has not already * been included. */ switch (item_type) { case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiMemoryWord: case vpiNamedEvent: case vpiNet: case vpiParameter: case vpiRealVar: case vpiReg: case vpiTimeVar: /* Warn if the variables scope (which includes the * variable) or the variable itself was already * included. A scope does not automatically include * memory words so do not check the scope for them. */ scname = strdup(vpi_get_str(vpiFullName, vpi_handle(vpiScope, item))); fullname = vpi_get_str(vpiFullName, item); if (((item_type != vpiMemoryWord) && vcd_names_search(&vcd_tab, scname)) || vcd_names_search(&vcd_var, fullname)) { vpi_printf("VCD warning: skipping signal %s, " "it was previously included.\n", fullname); free(scname); continue; } else { add_var = 1; } free(scname); } dep = draw_scope(item, callh); scan_item(depth, item, 0); /* The scope list must be sorted after we scan an item. */ vcd_names_sort(&vcd_tab); while (dep--) fprintf(dump_file, "$upscope $end\n"); /* Add this signal to the variable list so we can verify it * is not included twice. This must be done after it has * been added */ if (add_var) { vcd_names_add(&vcd_var, vpi_get_str(vpiFullName, item)); vcd_names_sort(&vcd_var); } } return 0; } void sys_vcd_register(void) { s_vpi_systf_data tf_data; vpiHandle res; /* All the compiletf routines are located in vcd_priv.c. */ tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpvars"; tf_data.calltf = sys_dumpvars_calltf; tf_data.compiletf = sys_dumpvars_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpvars"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/sys_vcdoff.c000066400000000000000000000075321265551621300164600ustar00rootroot00000000000000/* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" /* * This file contains do nothing stubs of all the VCD routines. */ # include # include # include # include # include # include "vcd_priv.h" static int dump_flag = 0; static PLI_INT32 sys_dummy_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 0; } static PLI_INT32 sys_dumpvars_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ if (dump_flag == 0) { vpi_printf("VCD info: dumping is suppressed.\n"); dump_flag = 1; } return 0; } void sys_vcdoff_register(void) { s_vpi_systf_data tf_data; vpiHandle res; /* All the compiletf routines are located in vcd_priv.c. */ tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dummy_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dummy_calltf; tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dummy_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dummy_calltf; tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dummy_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dummy_calltf; tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpvars"; tf_data.calltf = sys_dumpvars_calltf; tf_data.compiletf = sys_dumpvars_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpvars"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/system.sft000066400000000000000000000014011265551621300161760ustar00rootroot00000000000000# # This is the system function descriptor table for the # builtin (system) functions. # $random vpiSysFuncInt $rtoi vpiSysFuncInt $urandom vpiSysFuncSized 32 unsigned $urandom_range vpiSysFuncSized 32 unsigned $dist_uniform vpiSysFuncInt $dist_normal vpiSysFuncInt $dist_exponential vpiSysFuncInt $dist_poisson vpiSysFuncInt $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt $clog2 vpiSysFuncInt $q_full vpiSysFuncInt $abstime vpiSysFuncReal $simparam vpiSysFuncReal $simparam$str vpiSysFuncSized 1024 unsigned $table_model vpiSysFuncReal $ivl_string_method$len vpiSysFuncInt $ivl_string_method$to_vec vpiSysFuncVoid iverilog-10_1/vpi/table_mod.c000066400000000000000000000536451265551621300162470ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "sys_priv.h" #include #include #include #include #include #include #include "table_mod.h" #include "ivl_alloc.h" /* * Flag used to enable/disable debug output. */ static unsigned table_model_debug = 0; static p_table_mod *tables = 0; static unsigned table_count = 0; /* * Routine to cleanup the table model data at the end of simulation. */ static PLI_INT32 cleanup_table_mod(p_cb_data cause) { unsigned idx; (void)cause; /* Parameter is not used. */ for (idx = 0; idx < table_count; idx += 1) { free(tables[idx]->indep); free(tables[idx]->indep_val); if (tables[idx]->have_fname) free(tables[idx]->file.name); if (tables[idx]->have_ctl) { free(tables[idx]->control.info.interp); free(tables[idx]->control.info.extrap_low); free(tables[idx]->control.info.extrap_high); } free(tables[idx]); } free(tables); tables = 0; table_count = 0; return 0; } /* * Create an empty table model object and add it to the list of table * model objects. */ static p_table_mod create_table(void) { /* Create an empty table model object. */ p_table_mod obj = (p_table_mod) malloc(sizeof(s_table_mod)); assert(obj); /* Add it to the list of tables. */ table_count += 1; tables = (p_table_mod *) realloc(tables, sizeof(p_table_mod)*table_count); tables[table_count-1] = obj; /* Initialize and return the table object. */ obj->indep = 0; obj->indep_val = 0; obj->have_fname = 0; obj->have_ctl = 0; obj->control.arg = 0; obj->depend = 0; obj->dims = 0; obj->fields = 0; return obj; } /* * Check to see if this is a constant string. It returns 1 if the argument * is a constant string otherwise it returns 0. */ static unsigned is_const_string_obj(vpiHandle arg) { unsigned rtn = 0; assert(arg); switch (vpi_get(vpiType, arg)) { case vpiConstant: case vpiParameter: /* This must be a constant string. */ if (vpi_get(vpiConstType, arg) == vpiStringConst) rtn = 1; } return rtn; } /* * Get any command line flags. For now we only have a debug flag. */ static void check_command_line_flags(void) { struct t_vpi_vlog_info vlog_info; static unsigned command_line_processed = 0; /* If we have already processed the arguments then just return. */ if (command_line_processed) return; vpi_get_vlog_info(&vlog_info); for (int idx = 0; idx < vlog_info.argc; idx += 1) { if (strcmp(vlog_info.argv[idx], "-table-model-debug") == 0) { table_model_debug = 1; } } command_line_processed = 1; } /* * Check that the given $table_model() call has valid arguments. */ static PLI_INT32 sys_table_model_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; p_table_mod table = create_table(); /* See if there are any table model arguments. */ check_command_line_flags(); /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires at least two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } /* The first N (dimensions) arguments must be numeric. */ for (arg = vpi_scan(argv); arg && is_numeric_obj(arg); arg = vpi_scan(argv)) { table->dims += 1; table->indep = (vpiHandle *)realloc(table->indep, sizeof(vpiHandle)*table->dims); table->indep[table->dims-1] = arg; } /* We must have a data file. */ if (arg == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a file name argument.\n", name); vpi_control(vpiFinish, 1); return 0; } /* For now we only allow a constant string (file name). */ if (! is_const_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s file name argument must be a constant string.\n", name); vpi_control(vpiFinish, 1); return 0; } table->file.arg = arg; /* There may be an optional constant string (control string). */ arg = vpi_scan(argv); if (arg) { if (! is_const_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s control string argument must be a constant " "string.\n", name); vpi_control(vpiFinish, 1); return 0; } check_for_extra_args(argv, callh, name, "N numeric and 1 or 2 " " constant string arguments", 0); table->control.arg = arg; } /* Save the table data information. */ vpi_put_userdata(callh, table); return 0; } /* * Initialize the control string fields with the default value. */ static void initialize_control_fields_def(p_table_mod table) { char *val; unsigned width = table->dims; /* Set the default interpolation for each dimension. */ val = (char *) malloc(width); assert(val); table->control.info.interp = memset(val, IVL_LINEAR_INTERP, width); /* Set the default low extrapolation for each dimension. */ val = (char *) malloc(width); assert(val); table->control.info.extrap_low = memset(val, IVL_LINEAR_EXTRAP, width); /* Set the default low extrapolation for each dimension. */ val = (char *) malloc(width); assert(val); table->control.info.extrap_high = memset(val, IVL_LINEAR_EXTRAP, width); /* By default the dependent data column is the first one after the * independent data column(s). */ table->depend = 1; /* Set the number of control fields. */ table->fields = width; /* We have defined the interpolation/extrapolation fields. */ table->have_ctl = 1; } /* * Parse the extrapolation control codes. */ static unsigned parse_extrap(vpiHandle callh, p_table_mod table, unsigned idx, char **extrap, char *control) { /* Advance to the first extrapolation code. */ *extrap += 1; /* By default both the low and high extrapolation is set by the * first extrapolation character. We'll override the high value * later if there is a second character. */ switch (**extrap) { /* Clamp (no) extrapolation. */ case 'C': table->control.info.extrap_low[idx] = IVL_CONSTANT_EXTRAP; table->control.info.extrap_high[idx] = IVL_CONSTANT_EXTRAP; break; /* Linear extrapolation. */ case 'L': table->control.info.extrap_low[idx] = IVL_LINEAR_EXTRAP; table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; break; /* Error on extrapolation. */ case 'E': table->control.info.extrap_low[idx] = IVL_ERROR_EXTRAP; table->control.info.extrap_high[idx] = IVL_ERROR_EXTRAP; break; /* There are no extrapolation characters so use the default * (linear extrapolation). */ case 0: case ',': table->control.info.extrap_low[idx] = IVL_LINEAR_EXTRAP; table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; return 0; /* Anything else is an error. */ default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unknown extrapolation code '%c' for dimension " "%u: %s\n", **extrap, idx+1, control); vpi_control(vpiFinish, 1); return 1; } /* Advance to the next (high) extrapolation code. */ *extrap += 1; /* Override the high extrapolation value if needed. */ switch (**extrap) { /* Clamp (no) extrapolation. */ case 'C': table->control.info.extrap_high[idx] = IVL_CONSTANT_EXTRAP; break; /* Linear extrapolation. */ case 'L': table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; break; /* Error on extrapolation. */ case 'E': table->control.info.extrap_high[idx] = IVL_ERROR_EXTRAP; break; /* There is no high extrapolation character so just return. */ case 0: case ',': return 0; /* Anything else is an error. */ default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unknown high extrapolation code '%c' for dimension " "%u: %s\n", **extrap, idx+1, control); vpi_control(vpiFinish, 1); return 1; } /* Advance to the next field. */ *extrap += 1; return 0; } /* * Load the control string fields for the given string. Return 0 if there * is a problem parsing the control string. */ static unsigned initialize_control_fields(vpiHandle callh, p_table_mod table, char *control) { unsigned num_fields, num_ignore, idx; char *cp; /* If we need to fail these must be defined to zero until memory * has actually been allocated. */ table->control.info.interp = 0; table->control.info.extrap_low = 0; table->control.info.extrap_high = 0; /* We have defined the interpolation/extrapolation fields. */ table->have_ctl = 1; /* Look for the dependent data field. */ cp = strchr(control, ';'); if (cp) { size_t len; unsigned long val; char *end; /* NULL terminate the independent part of the string. */ *cp = 0; cp += 1; len = strlen(cp); /* If the length is zero or not all digits then the string * is invalid. */ // HERE: Do we need to support/ignore an underscore? What about in the file? if ((! len) || (len != strspn(cp, "0123456789"))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Control string dependent selector is " "invalid: %s.\n", cp); vpi_control(vpiFinish, 1); return 1; } val = strtoul(cp, &end, 10); assert(*end == 0); /* If the value is too big also report an error. */ if (val > UINT_MAX) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Control string dependent value is " "to large: %lu.\n", val); vpi_control(vpiFinish, 1); return 1; } table->depend = (unsigned) val; } else table->depend = 1; /* Find the number of interpolation/extrapolation control fields. */ num_fields = 1; cp = control; while ((cp = strchr(cp, ','))) { cp += 1; num_fields += 1; } /* We must have at least as many control fields as dimensions. */ if (num_fields < table->dims) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Not enough control field(s) (%u) for the dimension(s) " "(%u).", num_fields, table->dims); vpi_control(vpiFinish, 1); return 1; } /* Allocate space for the interpolation/extrapolation values. */ table->control.info.interp = (char *) malloc(num_fields); assert(table->control.info.interp); table->control.info.extrap_low = (char *) malloc(num_fields); assert(table->control.info.extrap_low); table->control.info.extrap_high = (char *) malloc(num_fields); assert(table->control.info.extrap_high); /* Parse the individual dimension control string. */ num_ignore = 0; cp = control; for (idx = 0; idx < num_fields; idx += 1) { switch (*cp) { /* Closest point interpolation. */ case 'D': table->control.info.interp[idx] = IVL_CLOSEST_POINT; if (parse_extrap(callh, table, idx, &cp, control)) return 0; break; /* Linear interpolation. */ case '1': table->control.info.interp[idx] = IVL_LINEAR_INTERP; if (parse_extrap(callh, table, idx, &cp, control)) return 0; break; /* Quadratic interpolation. */ case '2': table->control.info.interp[idx] = IVL_QUADRATIC_INTERP; if (parse_extrap(callh, table, idx, &cp, control)) return 0; break; /* Cubic interpolation. */ case '3': table->control.info.interp[idx] = IVL_CUBIC_INTERP; if (parse_extrap(callh, table, idx, &cp, control)) return 0; break; /* Ignore column. No extrapolation codes are allowed. */ case 'I': table->control.info.interp[idx] = IVL_IGNORE_COLUMN; table->control.info.extrap_low[idx] = IVL_ERROR_EXTRAP; table->control.info.extrap_high[idx] = IVL_ERROR_EXTRAP; cp += 1; num_ignore += 1; break; /* This field is not specified so use the default values. * Linear interpolation and extrapolation. */ case 0: assert(idx == num_fields-1); case ',': table->control.info.interp[idx] = IVL_LINEAR_INTERP; table->control.info.extrap_low[idx] = IVL_LINEAR_EXTRAP; table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; break; /* Anything else is an error. */ default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unknown interpolation code '%c' for dimension " "%u: %s\n", *cp, idx+1, control); vpi_control(vpiFinish, 1); return 1; } /* Verify that there is no extra junk at the end of the field. */ if (*cp && (*cp != ',')) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Extra control characters found for dimension " "%u: %s\n", idx+1, control); vpi_control(vpiFinish, 1); return 1; } /* Advance to the next field if we are not at the end of the * control string. */ if (*cp == ',') cp += 1; } /* We must have a usable control field for each dimensions. */ if ((num_fields - num_ignore) != table->dims) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("Usable control field(s) (%u) do not match dimension(s) " "(%u).", (num_fields - num_ignore), table->dims); vpi_control(vpiFinish, 1); return 1; } /* Set the number of control fields. */ table->fields = num_fields; return 0; } /* * Print out the interpolation code. */ static void dump_interp(char interp) { switch (interp) { case IVL_CLOSEST_POINT: fprintf(stderr, "D"); break; case IVL_LINEAR_INTERP: fprintf(stderr, "1"); break; case IVL_QUADRATIC_INTERP: fprintf(stderr, "2"); break; case IVL_CUBIC_INTERP: fprintf(stderr, "3"); break; case IVL_IGNORE_COLUMN: fprintf(stderr, "I"); break; default: fprintf(stderr, "<%d>", interp); break; } } /* * Print out the extrapolation code. */ static void dump_extrap(char extrap) { switch (extrap) { case IVL_CONSTANT_EXTRAP: fprintf(stderr, "C"); break; case IVL_LINEAR_EXTRAP: fprintf(stderr, "L"); break; case IVL_ERROR_EXTRAP: fprintf(stderr, "E"); break; default: fprintf(stderr, "<%d>", extrap); break; } } /* * Print some diagnostic information about the $table_model() call. */ static void dump_diag_info(vpiHandle callh, p_table_mod table) { unsigned idx; char msg[64]; snprintf(msg, sizeof(msg), "DEBUG: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; fprintf(stderr, "%s Building %u variable table using \"%s\" and\n", msg, table->dims, table->file.name); fprintf(stderr, "%*s the following control string: ", (int) strlen(msg), " "); dump_interp(table->control.info.interp[0]); dump_extrap(table->control.info.extrap_low[0]); dump_extrap(table->control.info.extrap_high[0]); for (idx = 1; idx < table->fields; idx += 1) { fprintf(stderr, ","); dump_interp(table->control.info.interp[idx]); dump_extrap(table->control.info.extrap_low[idx]); dump_extrap(table->control.info.extrap_high[idx]); } fprintf(stderr, ";%u\n", table->depend); fprintf(stderr, "%*s The data file must have at least %u columns.\n", (int) strlen(msg), " ", table->fields+table->depend); } /* * Initialize the table model data structure. * * The first time $table_model() is called we need to evaluate the file name * and control strings. Then we must load the data from the file. */ static unsigned initialize_table_model(vpiHandle callh, const char *name, p_table_mod table) { s_vpi_value val; FILE *fp; /* Get the table file name. */ table->file.name = get_filename(callh, name, table->file.arg); table->have_fname = 1; if (table->file.name == 0) return 1; /* Open the file. */ fp = fopen(table->file.name, "r"); if (fp == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() cannot open file \"%s\" for reading (%s).\n", name, table->file.name, strerror(errno)); return 1; } /* Check to see if there is a control string. */ if (table->control.arg) { val.format = vpiStringVal; vpi_get_value(table->control.arg, &val); if (*(val.value.str) == 0) { /* If there is an empty control string field then we just * use the default values for interpolation/extrapolation. * We also assume that there only dimensions + 1 fields. */ initialize_control_fields_def(table); } else { if (initialize_control_fields(callh, table, val.value.str)) { return 1; } } } else { /* If there is no control string field then we just use the * default values for interpolation/extrapolation. We also * assume that there only dimensions + 1 fields. */ initialize_control_fields_def(table); } /* If requested dump some diagnostic information. */ if (table_model_debug) dump_diag_info(callh, table); /* Parse the given file and produce the basic data structure. We * need to have columns for each control string field and for the * dependent data. */ if (parse_table_model(fp, callh, table)) return 1; // HERE: once we are done with the control string should we build a table // of function pointer for the interp/extrap? /* Close the file now that we have loaded all the data. */ if (fclose(fp)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() cannot close file \"%s\" (%s).\n", name, table->file.name, strerror(errno)); return 1; } /* Allocate space for the current argument values. */ table->indep_val = (double*) malloc(sizeof(double)*table->dims); assert(table->indep_val); return 0; } /* * Routine to evaluate the table model using the current input values. */ static double eval_table_model(vpiHandle callh, p_table_mod table) { unsigned idx; (void)callh; /* Parameter is not used. */ fprintf(stderr, "Evaluating table \"%s\" with %u variables\n", table->file.name, table->dims); for (idx = 0; idx < table->dims; idx += 1) { fprintf(stderr, " Arg %u: %#g\n", idx, table->indep_val[idx]); } return 0.0; } /* * Runtime routine for the $table_model(). */ static PLI_INT32 sys_table_model_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); s_vpi_value val; p_table_mod table; unsigned idx; double result; /* Retrieve the table data. */ table = vpi_get_userdata(callh); /* If this is the first call then build the data structure. */ if ((table->have_fname == 0) && initialize_table_model(callh, name, table)) { vpi_control(vpiFinish, 1); return 0; } /* Load the current argument values into the table structure. */ for (idx = 0; idx < table->dims; idx += 1) { val.format = vpiRealVal; vpi_get_value(table->indep[idx], &val); table->indep_val[idx] = val.value.real; } /* Interpolate/extrapolate the data structure to find the value. */ result = eval_table_model(callh, table); /* Return the calculated value. */ val.format = vpiRealVal; val.value.real = result; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Routine to register the system function provided in this file. */ void table_model_register(void) { s_vpi_systf_data tf_data; s_cb_data cb; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncReal; tf_data.tfname = "$table_model"; tf_data.calltf = sys_table_model_calltf; tf_data.compiletf = sys_table_model_compiletf; tf_data.sizetf = 0; /* Not needed for a vpiSysFuncReal. */ tf_data.user_data = "$table_model"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* Create a callback to clear all the table model memory when the * simulator finishes. */ cb.time = NULL; cb.reason = cbEndOfSimulation; cb.cb_rtn = cleanup_table_mod; cb.user_data = 0x0; cb.obj = 0x0; vpi_register_cb(&cb); } iverilog-10_1/vpi/table_mod.h000066400000000000000000000062621265551621300162450ustar00rootroot00000000000000#ifndef IVL_table_mod_H #define IVL_table_mod_H /* * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include /* * Define the allowed interpolation control values. */ #define IVL_CLOSEST_POINT 0 /* D */ #define IVL_LINEAR_INTERP 1 /* 1 - Default */ #define IVL_QUADRATIC_INTERP 2 /* 2 */ #define IVL_CUBIC_INTERP 3 /* 3 */ #define IVL_IGNORE_COLUMN 4 /* I */ /* * Define the allowed extrapolation control values. */ #define IVL_CONSTANT_EXTRAP 0 /* C */ #define IVL_LINEAR_EXTRAP 1 /* L - Default */ #define IVL_ERROR_EXTRAP 2 /* E */ /* * Structure that represents an individual iso line element. */ typedef struct t_indep { double value; struct t_indep *left; /* previous element. */ struct t_indep *right; /* next element. */ union { double data; struct t_indep *child; struct t_build *build; } data; } s_indep, *p_indep; /* * This is used as an interface element while building the iso lines. It will * be removed when the list is converted to a balanced threaded tree. It is * used to make in order insertion faster. */ typedef struct t_build { p_indep first; p_indep last; unsigned count; } s_build, *p_build; /* * This structure is saved for each table model instance. */ typedef struct t_table_mod { vpiHandle *indep; /* Independent variable arguments. */ double *indep_val; /* Current independent variable values. */ union { /* Data file or argument to get the data file. */ char *name; vpiHandle arg; } file; union { /* Control string argument. */ struct { char *interp; char *extrap_low; char *extrap_high; } info; vpiHandle arg; } control; // HERE need a pointer to the dependent data and the final table. unsigned dims; /* The number of independent variables. */ unsigned fields; /* The number of control fields. */ unsigned depend; /* Where the dependent column is located. */ char have_fname; /* Has the file name been allocated? */ char have_ctl; /* Has the file name been allocated? */ } s_table_mod, *p_table_mod; /* * Define the non-static table model routines. */ extern unsigned parse_table_model(FILE *fp, vpiHandle callh, p_table_mod table); extern int tblmodlex(void); extern void destroy_tblmod_lexor(void); extern void init_tblmod_lexor(FILE *fp); #endif /* IVL_table_mod_H */ iverilog-10_1/vpi/table_mod_lexor.lex000066400000000000000000000075131265551621300200170ustar00rootroot00000000000000%option prefix="tblmod" %option never-interactive %option noinput %option nounput %option noyywrap %{ /* * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include "table_mod.h" #include "table_mod_parse.h" #include "ivl_alloc.h" static double get_scaled_real(const char *text); static double get_real(const char *text); #define yylval tblmodlval %} SPACE [ \t] EOL [\r\n]+ SCALE [afpnumkKMGT] %% /* Skip comment lines. */ "#".*{EOL} { tblmodlloc.first_line += 1; } /* Skip white space. */ {SPACE} { ; } {EOL} { tblmodlloc.first_line += 1; return END_LINE; } /* Recognize a real value with a Verilog-AMS scale. */ [+-]?[0-9][0-9_]*(\.[0-9][0-9_]*)?{SCALE} { yylval.real = get_scaled_real(yytext); return REAL; } /* Recognize and other numeric value. */ [+-]?[0-9][0-9_]*(\.[0-9][0-9_]*)?([eE][+-]?[0-9][0-9_]*)? { yylval.real = get_real(yytext); return REAL; } %% /* * Convert a Verilog-AMS scaled real number to a real value. */ double get_scaled_real(const char *text) { double result; size_t len = strlen(text); char *buf = malloc(len + 5); const char *scale; /* Copy the number to the buffer. */ strcpy(buf, text); /* Convert the scale character to exponential form. */ switch (text[len-1]) { case 'a': scale = "e-18"; break; /* atto */ case 'f': scale = "e-15"; break; /* femto */ case 'p': scale = "e-12"; break; /* pico */ case 'n': scale = "e-9"; break; /* nano */ case 'u': scale = "e-6"; break; /* micro */ case 'm': scale = "e-3"; break; /* milli */ case 'k': case 'K': scale = "e3"; break; /* kilo */ case 'M': scale = "e6"; break; /* mega */ case 'G': scale = "e9"; break; /* giga */ case 'T': scale = "e12"; break; /* tera */ default: scale = ""; assert(0); break; } buf[len-1] = 0; strcat(buf, scale); /* Convert the exponential string to a real value, */ result = get_real(buf); free(buf); return result; } /* * Convert a normal number to a real value. */ double get_real(const char *text) { double result; char *buf = malloc(strlen(text) + 1); char *cptr = buf; char *end; unsigned idx; /* We must have a buffer to remove any '_' characters. */ assert(buf); /* Remove any '_' characters from the string. */ for (idx = 0; text[idx]; idx += 1) { if (text[idx] == '_') continue; *cptr = text[idx]; cptr += 1; } *cptr = 0; /* Convert the buffer to a double value and check that the * conversion was done successfully. */ result = strtod(buf, &end); assert(*end == 0); free(buf); return result; } void init_tblmod_lexor(FILE *fp) { yyrestart(fp); } /* * Modern version of flex (>=2.5.9) can clean up the scanner data. */ void destroy_tblmod_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 # if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif # endif } iverilog-10_1/vpi/table_mod_parse.y000066400000000000000000000140031265551621300174500ustar00rootroot00000000000000 %{ /* * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include "table_mod.h" #include "ivl_alloc.h" /* * The number of parse errors encountered while parsing the data file. */ static unsigned errors = 0; /* * The minimum number of columns needed in the data file to satisfy the * control string/input variable requirements. */ static unsigned minimum_columns; /* * The number of independent values we expect to keep based on the control * string/independent variables. */ static unsigned indep_values; /* * The total number of independent columns to expect. This includes any * ignored columns. */ static unsigned indep_columns; /* * The zero based dependent column location. This is just minimum_columns - 1. */ static unsigned dep_column; /* * The number of columns to expect in this data file. This must be * greater than or equal to the minimum_columns above. */ static unsigned number_of_columns = 0; /* * The number of columns that this point currently has. */ static unsigned cur_columns; /* * The zero based current independent value we are looking for. */ static unsigned cur_value; /* * The usable values for this point. The independent values are first and * the dependent value is the last entry. */ static double *values; /* * The name of the file we are getting data from. */ static const char *in_file_name; /* * The table structure that we are getting data for. */ static p_table_mod table_def; /* * The lexor and error routines. */ extern int tblmodlex(void); static void yyerror(const char *fmt, ...); static void process_point(void) { assert(cur_value == indep_values); #if 0 unsigned idx; fprintf(stderr, "%#g", values[0]); for (idx = 1; idx < indep_values; idx += 1) { fprintf(stderr, ", %#g", values[idx]); } fprintf(stderr, " => %#g\n", values[indep_values]); #endif } %} %locations %union { double real; }; %token END_LINE %token REAL %% /* * A table is just a bunch of points. The lexor ignores any comments. */ table : point | table point ; /* * An individual point is just a bunch of columns followed by one or more * end of line characters. */ point : columns END_LINE { /* * If the number of columns has been defined then we must have * enough columns and we expect every line to have the same * number of columns. */ if (number_of_columns) { if (cur_columns < minimum_columns) { yyerror("Found %u columns, need at least %u.", cur_columns, minimum_columns); } else if (cur_columns != number_of_columns) { yyerror("Found %u columns, expected %u.", cur_columns, number_of_columns); } else process_point(); /* * If this is the first point then we must have enough columns. * If there are enough then we define this to be the number of * columns that the file must have. */ } else { if (cur_columns < minimum_columns) { yyerror("Found %u columns, need at least %u.", cur_columns, minimum_columns); } else { number_of_columns = cur_columns; process_point(); } } } ; /* * Each column is a real value. We only save the columns we care about. */ columns : REAL { if (table_def->control.info.interp[0] != IVL_IGNORE_COLUMN) { values[0] = $1; cur_value = 1; } else cur_value = 0; cur_columns = 1; } | columns REAL { if (cur_columns < indep_columns) { if (table_def->control.info.interp[cur_columns] != IVL_IGNORE_COLUMN) { values[cur_value] = $2; cur_value += 1; } } else if (cur_columns == dep_column) values[indep_values] = $2; cur_columns += 1; } ; %% /* * Setup the various variables, then parse the input file and then cleanup * the lexor and the allocated memory. */ unsigned parse_table_model(FILE *fp, vpiHandle callh, p_table_mod table) { unsigned rtn = 0; assert(table); assert(table->fields > 0); assert(table->depend > 0); /* Initialize the variables used by the parser. */ table_def = table; indep_values = table->dims; indep_columns = table->fields; minimum_columns = table->fields + table->depend; dep_column = minimum_columns - 1; values = malloc(sizeof(double)*(indep_values+1)); assert(values); in_file_name = table->file.name; /* Parse the input file. */ init_tblmod_lexor(fp); /* If there are errors then print a message. */ if (yyparse()) errors += 1; if (errors) { vpi_printf("ERROR: %s:%u: ", vpi_get_str(vpiFile, callh), (int) vpi_get(vpiLineNo, callh)); vpi_printf("Had %u error(s) while reading table file.\n", errors); rtn = 1; } /* Cleanup the lexor and allocated memory. */ destroy_tblmod_lexor(); free(values); return rtn; } /* * Currently every parse error is reported after the newline so subtract * one from the line count. */ void yyerror(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s:%d: TABLE ERROR: ", in_file_name, yylloc.first_line-1); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); errors += 1; } iverilog-10_1/vpi/v2005_math.c000066400000000000000000000242241265551621300160750ustar00rootroot00000000000000/* * Verilog-2005 math library for Icarus Verilog * http://www.icarus.com/eda/verilog/ * * Copyright (C) 2007-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vpi_config.h" #include #include #include #include #include "vpi_user.h" #include "ivl_alloc.h" /* Single argument functions. */ typedef struct s_single_data { const char *name; double (*func)(double); } t_single_data; static t_single_data va_single_data[]= { {"$sqrt", sqrt}, {"$ln", log}, {"$log10", log10}, {"$exp", exp}, {"$ceil", ceil}, {"$floor", floor}, {"$sin", sin}, {"$cos", cos}, {"$tan", tan}, {"$asin", asin}, {"$acos", acos}, {"$atan", atan}, {"$sinh", sinh}, {"$cosh", cosh}, {"$tanh", tanh}, {"$asinh", asinh}, {"$acosh", acosh}, {"$atanh", atanh}, {0, 0} /* Must be NULL terminated! */ }; /* Double argument functions. */ typedef struct s_double_data { const char *name; double (*func)(double, double); } t_double_data; static t_double_data va_double_data[]= { {"$pow", pow}, {"$atan2", atan2}, {"$hypot", hypot}, {0, 0} /* Must be NULL terminated! */ }; /* * This structure holds the single argument information. */ typedef struct { vpiHandle arg; double (*func)(double); } va_single_t; /* * This structure holds the double argument information. */ typedef struct { vpiHandle arg1; vpiHandle arg2; double (*func)(double, double); } va_double_t; /* * Cleanup the allocated memory at the end of simulation. */ static va_single_t** single_funcs = 0; static unsigned single_funcs_count = 0; static va_double_t** double_funcs = 0; static unsigned double_funcs_count = 0; static PLI_INT32 sys_end_of_simulation(p_cb_data cb_data) { unsigned idx; (void)cb_data; /* Parameter is not used. */ for (idx = 0; idx < single_funcs_count; idx += 1) { free(single_funcs[idx]); } free(single_funcs); single_funcs = 0; single_funcs_count = 0; for (idx = 0; idx < double_funcs_count; idx += 1) { free(double_funcs[idx]); } free(double_funcs); double_funcs = 0; double_funcs_count = 0; return 0; } /* * Standard error message routine. The format string must take one * string argument (the name of the function). */ static void va_error_message(vpiHandle callh, const char *format, const char *name) { vpi_printf("%s:%d: error: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf(format, name); vpi_control(vpiFinish, 1); } /* * Process an argument. */ static vpiHandle va_process_argument(vpiHandle callh, const char *name, vpiHandle arg, const char *post) { PLI_INT32 type; if (arg == NULL) return 0; type = vpi_get(vpiType, arg); /* Math function cannot do anything with a string. */ if ((type == vpiConstant || type == vpiParameter) && (vpi_get(vpiConstType, arg) == vpiStringConst)) { const char* basemsg = "%s cannot process strings"; char* msg = malloc(strlen(basemsg)+strlen(post)+3); strcpy(msg, basemsg); strcat(msg, post); strcat(msg, ".\n"); va_error_message(callh, msg, name); free(msg); return 0; } return arg; } /* * Routine to check all the single argument math functions. */ static PLI_INT32 va_single_argument_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh, argv, arg; const t_single_data *data; const char *name; va_single_t* fun_data; assert(ud != 0); callh = vpi_handle(vpiSysTfCall, 0); assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); data = (const t_single_data *) ud; name = data->name; fun_data = malloc(sizeof(va_single_t)); /* Check that there are arguments. */ if (argv == 0) { va_error_message(callh, "%s requires one argument.\n", name); free(fun_data); return 0; } /* In Icarus if we have an argv we have at least one argument. */ arg = vpi_scan(argv); fun_data->arg = va_process_argument(callh, name, arg, ""); /* These functions only take one argument. */ arg = vpi_scan(argv); if (arg != 0) { va_error_message(callh, "%s takes only one argument.\n", name); vpi_free_object(argv); } /* Get the function that is to be used by the calltf routine. */ fun_data->func = data->func; vpi_put_userdata(callh, fun_data); single_funcs_count += 1; single_funcs = (va_single_t **)realloc(single_funcs, single_funcs_count*sizeof(va_single_t **)); single_funcs[single_funcs_count-1] = fun_data; /* vpi_scan() returning 0 (NULL) has already freed argv. */ return 0; } /* * Routine to implement the single argument math functions. */ static PLI_INT32 va_single_argument_calltf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); s_vpi_value val; va_single_t* fun_data; (void)ud; /* Parameter is not used. */ /* Retrieve the function and argument data. */ fun_data = vpi_get_userdata(callh); /* Calculate the result */ val.format = vpiRealVal; vpi_get_value(fun_data->arg, &val); val.value.real = (fun_data->func)(val.value.real); /* Return the result */ vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Routine to check all the double argument math functions. */ static PLI_INT32 va_double_argument_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh, argv, arg; const t_double_data *data; const char *name; va_double_t* fun_data; assert(ud != 0); callh = vpi_handle(vpiSysTfCall, 0); assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); data = (const t_double_data *) ud; name = data->name; fun_data = malloc(sizeof(va_double_t)); /* Check that there are arguments. */ if (argv == 0) { va_error_message(callh, "%s requires two arguments.\n", name); free(fun_data); return 0; } /* In Icarus if we have an argv we have at least one argument. */ arg = vpi_scan(argv); fun_data->arg1 = va_process_argument(callh, name, arg, " (arg1)"); /* Check that there are at least two arguments. */ arg = vpi_scan(argv); if (arg == 0) { va_error_message(callh, "%s requires two arguments.\n", name); } fun_data->arg2 = va_process_argument(callh, name, arg, " (arg2)"); /* These functions only take two arguments. */ arg = vpi_scan(argv); if (arg != 0) { va_error_message(callh, "%s takes only two arguments.\n", name); vpi_free_object(argv); } /* Get the function that is to be used by the calltf routine. */ fun_data->func = data->func; vpi_put_userdata(callh, fun_data); double_funcs_count += 1; double_funcs = (va_double_t **)realloc(double_funcs, double_funcs_count*sizeof(va_double_t **)); double_funcs[double_funcs_count-1] = fun_data; /* vpi_scan() returning 0 (NULL) has already freed argv. */ return 0; } /* * Routine to implement the double argument math functions. */ static PLI_INT32 va_double_argument_calltf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); s_vpi_value val; double first_arg; va_double_t* fun_data; (void)ud; /* Parameter is not used. */ /* Retrieve the function and argument data. */ fun_data = vpi_get_userdata(callh); /* Calculate the result */ val.format = vpiRealVal; vpi_get_value(fun_data->arg1, &val); first_arg = val.value.real; vpi_get_value(fun_data->arg2, &val); val.value.real = (fun_data->func)(first_arg, val.value.real); /* Return the result */ vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Register all the functions with Verilog. */ static void sys_v2005_math_register(void) { s_cb_data cb_data; s_vpi_systf_data tf_data; vpiHandle res; unsigned idx; /* Register the single argument functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = va_single_argument_calltf; tf_data.compiletf = va_single_argument_compiletf; tf_data.sizetf = 0; for (idx=0; va_single_data[idx].name != 0; idx++) { tf_data.tfname = va_single_data[idx].name; tf_data.user_data = (PLI_BYTE8 *) &va_single_data[idx]; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } /* Register the double argument functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = va_double_argument_calltf; tf_data.compiletf = va_double_argument_compiletf; tf_data.sizetf = 0; for (idx=0; va_double_data[idx].name != 0; idx++) { tf_data.tfname = va_double_data[idx].name; tf_data.user_data = (PLI_BYTE8 *) &va_double_data[idx]; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } /* We need to clean up the userdata. */ cb_data.reason = cbEndOfSimulation; cb_data.time = 0; cb_data.cb_rtn = sys_end_of_simulation; cb_data.user_data = "system"; vpi_register_cb(&cb_data); } /* * Hook to get Icarus Verilog to find the registration function. */ extern void sys_clog2_register(void); void (*vlog_startup_routines[])(void) = { sys_v2005_math_register, sys_clog2_register, 0 }; iverilog-10_1/vpi/v2005_math.sft000066400000000000000000000011701265551621300164420ustar00rootroot00000000000000# # This is the system function descriptor table for the # Verilog-2005 math functions. # # Single argument functions. $sqrt vpiSysFuncReal $ln vpiSysFuncReal $log10 vpiSysFuncReal $exp vpiSysFuncReal $ceil vpiSysFuncReal $floor vpiSysFuncReal $sin vpiSysFuncReal $cos vpiSysFuncReal $tan vpiSysFuncReal $asin vpiSysFuncReal $acos vpiSysFuncReal $atan vpiSysFuncReal $sinh vpiSysFuncReal $cosh vpiSysFuncReal $tanh vpiSysFuncReal $asinh vpiSysFuncReal $acosh vpiSysFuncReal $atanh vpiSysFuncReal # Double argument functions. $pow vpiSysFuncReal $atan2 vpiSysFuncReal $hypot vpiSysFuncReal iverilog-10_1/vpi/v2009.sft000066400000000000000000000005721265551621300154420ustar00rootroot00000000000000# # This is the system function descriptor table for the # builtin (SystemVerilog) functions. # $dimensions vpiSysFuncInt $unpacked_dimensions vpiSysFuncInt $left vpiSysFuncInt $right vpiSysFuncInt $low vpiSysFuncInt $high vpiSysFuncInt $increment vpiSysFuncInt $size vpiSysFuncInt $ivl_array_method$to_vec vpiSysFuncVoid $ivl_array_method$from_vec vpiSysFuncVoid iverilog-10_1/vpi/v2009_array.c000066400000000000000000000221771265551621300162730ustar00rootroot00000000000000/* * Copyright (C) 2013 Cary R. (cygcary@yahoo.com) * Copyright (C) 2014 Stephen Williams (steve@icarus.com) * Copyright (C) 2014 CERN * @author Maciej Suminski (maciej.suminski@cern.ch) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ # include "sys_priv.h" # include static PLI_INT32 one_array_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; argv = vpi_iterate(vpiArgument, callh); if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires an array argument.\n", name); vpi_control(vpiFinish, 1); return 0; } arg = vpi_scan(argv); if (arg == 0) return 0; arg = vpi_scan(argv); if (arg != 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s has too many arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } return 0; } // Checks if a function is passed an array and optionally an integer. static PLI_INT32 array_int_opt_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { #if defined(__GNUC__) const int MAX_ARGC = 3; // one more is to verify there are at most 2 args #else #define MAX_ARGC 3 #endif vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, arg[MAX_ARGC]; PLI_INT32 arg_type[MAX_ARGC]; int argc; argv = vpi_iterate(vpiArgument, callh); argc = 0; if(argv) { for(int i = 0; i < MAX_ARGC; ++i) { arg[i] = vpi_scan(argv); if(arg[i]) { arg_type[i] = vpi_get(vpiType, arg[i]); ++argc; } else { break; } } } if (!argv || argc == MAX_ARGC || (arg_type[0] != vpiRegArray && arg_type[0] != vpiStringVar) || (argc == 2 && arg_type[1] != vpiIntegerVar && !(arg_type[1] == vpiConstant && vpi_get(vpiConstType, arg[1]) == vpiBinaryConst))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s expects an array and optionally an integer.\n", name); if(argc == MAX_ARGC) vpi_free_object(argv); vpi_control(vpiFinish, 0); return 0; } return 0; } static PLI_INT32 func_not_implemented_compiletf(ICARUS_VPI_CONST PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpi_printf("SORRY: %s:%d: function %s() is not currently implemented.\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), name); vpi_control(vpiFinish, 1); return 0; } static PLI_INT32 array_get_property(int property, ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, array, dim; s_vpi_value value; argv = vpi_iterate(vpiArgument, callh); if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires an array argument.\n", name); vpi_control(vpiFinish, 1); return 0; } array = vpi_scan(argv); dim = vpi_scan(argv); if(dim != 0) { vpi_printf("SORRY: %s:%d: multiple dimensions are not handled yet.\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_control(vpiFinish, 1); } value.format = vpiIntVal; value.value.integer = vpi_get(property, array); vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } static PLI_INT32 left_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { return array_get_property(vpiLeftRange, name); } static PLI_INT32 right_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { return array_get_property(vpiRightRange, name); } static void high_array(const char*name, vpiHandle callh, vpiHandle arg) { s_vpi_value value; int array_type; int size; switch ( (array_type = vpi_get(vpiArrayType, arg)) ) { case vpiDynamicArray: case vpiQueueArray: size = vpi_get(vpiSize, arg); value.format = vpiIntVal; value.value.integer = size - 1; vpi_put_value(callh, &value, 0, vpiNoDelay); break; default: vpi_printf("SORRY: %s:%d: function %s() argument object code is %d\n", vpi_get_str(vpiFile,callh), (int)vpi_get(vpiLineNo, callh), name, array_type); break; } } static PLI_INT32 high_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; int object_code; (void)name; /* Parameter is not used. */ argv = vpi_iterate(vpiArgument, callh); assert(argv); arg = vpi_scan(argv); assert(arg); vpi_free_object(argv); switch ( (object_code = vpi_get(vpiType, arg)) ) { case vpiArrayVar: high_array(name, callh, arg); break; default: vpi_printf("SORRY: %s:%d: function %s() argument object code is %d\n", vpi_get_str(vpiFile,callh), (int)vpi_get(vpiLineNo, callh), name, object_code); return 0; } return 0; } static void low_array(const char*name, vpiHandle callh, vpiHandle arg) { s_vpi_value value; int array_type; switch ( (array_type = vpi_get(vpiArrayType, arg)) ) { case vpiDynamicArray: case vpiQueueArray: value.format = vpiIntVal; value.value.integer = 0; vpi_put_value(callh, &value, 0, vpiNoDelay); break; default: vpi_printf("SORRY: %s:%d: function %s() argument object code is %d\n", vpi_get_str(vpiFile,callh), (int)vpi_get(vpiLineNo, callh), name, array_type); break; } } static PLI_INT32 low_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; int object_code; (void)name; /* Parameter is not used. */ argv = vpi_iterate(vpiArgument, callh); assert(argv); arg = vpi_scan(argv); assert(arg); vpi_free_object(argv); switch ( (object_code = vpi_get(vpiType, arg)) ) { case vpiArrayVar: low_array(name, callh, arg); break; default: vpi_printf("SORRY: %s:%d: function %s() argument object code is %d\n", vpi_get_str(vpiFile,callh), (int)vpi_get(vpiLineNo, callh), name, object_code); return 0; } return 0; } void v2009_array_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.calltf = 0; tf_data.compiletf = func_not_implemented_compiletf;; tf_data.sizetf = 0; tf_data.tfname = "$high"; tf_data.user_data = "$high"; tf_data.compiletf = one_array_arg_compiletf; tf_data.calltf = high_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$low"; tf_data.user_data = "$low"; tf_data.compiletf = one_array_arg_compiletf; tf_data.calltf = low_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$left"; tf_data.user_data = "$left"; tf_data.compiletf = array_int_opt_arg_compiletf; tf_data.calltf = left_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$right"; tf_data.user_data = "$right"; tf_data.compiletf = array_int_opt_arg_compiletf; tf_data.calltf = right_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* These functions are not currently implemented. */ tf_data.tfname = "$dimensions"; tf_data.user_data = "$dimensions"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$unpacked_dimensions"; tf_data.user_data = "$unpacked_dimensions"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.tfname = "$increment"; tf_data.user_data = "$increment"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/v2009_enum.c000066400000000000000000000425451265551621300161220ustar00rootroot00000000000000/* * Copyright (c) 2010-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vpi_config.h" # include "sv_vpi_user.h" # include # include # include /* * The compiletf routine for the enumeration next() and prev() methods. */ static PLI_INT32 ivl_enum_method_next_prev_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle arg_enum, arg_var, arg_count; /* These routines must have arguments. */ if (argv == 0) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("No arguments given for enum method %s().\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check for the enumeration type argument. */ arg_enum = vpi_scan(argv); if (arg_enum == 0) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("No enumeration type argument given for enum " "method %s().\n", name); vpi_control(vpiFinish, 1); return 0; } if (vpi_get(vpiType, arg_enum) != vpiEnumTypespec) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("The first argument to enum method %s() must be an " "enumeration type, found a %s.\n", name, vpi_get_str(vpiType, arg_enum)); vpi_control(vpiFinish, 1); } /* Check for the enumeration variable. */ arg_var = vpi_scan(argv); if (arg_var == 0) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("No enumeration variable argument given for enum " "method %s().\n", name); vpi_control(vpiFinish, 1); return 0; } switch(vpi_get(vpiType, arg_var)) { case vpiBitVar: case vpiByteVar: case vpiIntegerVar: case vpiIntVar: case vpiLongIntVar: case vpiReg: case vpiShortIntVar: break; default: vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("The second argument to enum method %s() must be an " "enumeration variable, found a %s.\n", name, vpi_get_str(vpiType, arg_var)); vpi_control(vpiFinish, 1); } /* We can have an optional numeric count value. */ arg_count = vpi_scan(argv); if (arg_count) { switch(vpi_get(vpiType, arg_count)) { case vpiBitVar: case vpiByteVar: case vpiIntegerVar: case vpiIntVar: case vpiLongIntVar: case vpiMemoryWord: case vpiNet: case vpiPartSelect: case vpiRealVar: case vpiReg: case vpiShortIntVar: case vpiTimeVar: break; case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, arg_count) != vpiStringConst) break; default: vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("The second argument to enum method %s() must be " "numeric, found a %s.\n", name, vpi_get_str(vpiType, arg_count)); vpi_control(vpiFinish, 1); } } /* If we have a count argument then check to see if any extra * arguments were given. */ if (arg_count && (vpi_scan(argv) != 0)) { vpi_free_object(argv); vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("Extra argument(s) given to enum method %s().\n", name); vpi_control(vpiFinish, 1); } /* The method return value and the enum variable must be the * same size */ if (vpi_get(vpiSize, sys) != vpi_get(vpiSize, arg_var)) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("The enum method %s() return width (%d) must match " "the enum variable width (%d).\n", name, (int) vpi_get(vpiSize, sys), (int) vpi_get(vpiSize, arg_var)); vpi_control(vpiFinish, 1); } return 0; } /* * Compare it two values are equal to each other. */ static int compare_value_eequal(s_vpi_value*ref1, s_vpi_value*ref2, PLI_INT32 wid) { /* Two integer values are easy. */ if (ref1->format == vpiIntVal && ref2->format == vpiIntVal) return ref1->value.integer == ref2->value.integer; /* For two vectors compare them word by word. */ if (ref1->format == vpiVectorVal && ref2->format == vpiVectorVal) { PLI_INT32 words = (wid-1)/32 + 1; PLI_INT32 idx; for (idx = 0 ; idx < words ; idx += 1) { if (ref1->value.vector[idx].aval != ref2->value.vector[idx].aval) return 0; if (ref1->value.vector[idx].bval != ref2->value.vector[idx].bval) return 0; } return 1; } /* Swap the order so the code below can be used. */ if (ref1->format == vpiVectorVal && ref2->format == vpiIntVal) { s_vpi_value*tmp = ref1; ref1 = ref2; ref2 = tmp; } /* Compare an integer to a vector. */ if (ref1->format == vpiIntVal && ref2->format == vpiVectorVal) { PLI_INT32 aval = ref1->value.integer; PLI_INT32 words = (wid-1)/32 + 1; PLI_INT32 idx; for (idx = 0 ; idx < words ; idx += 1) { if (aval != ref2->value.vector[idx].aval) return 0; if (ref2->value.vector[idx].bval) return 0; aval = 0; } return 1; } /* Unsupported types. */ vpi_printf("XXXX formats are: %d vs %d\n", ref1->format, ref2->format); assert(0); return 0; } /* * If the current value is not found in the enumeration. Then we need to * return X/0 for the next()/prev() value. */ static void fill_handle_with_init(vpiHandle arg, int is_two_state) { s_vpi_value val; PLI_INT32 words = ((vpi_get(vpiSize, arg) - 1) / 32) + 1; PLI_INT32 idx; p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); PLI_INT32 fill = (is_two_state) ? 0x0 : 0xffffffff; assert(val_ptr); /* Fill the vector with X. */ for (idx=0; idx < words; idx += 1) { val_ptr[idx].aval = fill; val_ptr[idx].bval = fill; } /* Put the vector to the argument. */ val.format = vpiVectorVal; val.value.vector = val_ptr; vpi_put_value(arg, &val, 0, vpiNoDelay); free(val_ptr); } /* * Implement the next()/prev() enumeration methods. */ static PLI_INT32 ivl_enum_method_next_prev_calltf(PLI_BYTE8*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle arg_enum = vpi_scan(argv); vpiHandle arg_var = vpi_scan(argv); vpiHandle arg_count = vpi_scan(argv); vpiHandle enum_list; vpiHandle cur; PLI_INT32 var_width = vpi_get(vpiSize, arg_var); PLI_UINT32 enum_size = vpi_get(vpiSize, arg_enum); PLI_UINT32 count = 1; PLI_UINT32 loc = 0; s_vpi_value cur_val, var_val; /* Get the count value if one was given. */ if (arg_count) { s_vpi_value count_val; /* Get the count value. */ count_val.format = vpiIntVal; vpi_get_value(arg_count, &count_val); count = count_val.value.integer; /* Remove any multiple loops around the enumeration. */ count %= enum_size; /* Free the argument iterator. */ vpi_free_object(argv); } /* Get the current value. */ var_val.format = vpiObjTypeVal; vpi_get_value(arg_var, &var_val); /* If the count is zero then just return the current value. */ if (count == 0) { vpi_put_value(sys, &var_val, 0, vpiNoDelay); return 0; } /* If the current value is a vector, then make a safe copy of it so that other vpi_get_value() calls don't trash the value. */ if (var_val.format == vpiVectorVal) { PLI_INT32 idx; PLI_INT32 words = (var_width - 1)/32 + 1; p_vpi_vecval nvec = malloc(words*sizeof(s_vpi_vecval)); for (idx = 0 ; idx < words ; idx += 1) { nvec[idx].aval = var_val.value.vector[idx].aval; nvec[idx].bval = var_val.value.vector[idx].bval; } var_val.value.vector = nvec; } /* Search for the current value in the enumeration list. */ enum_list = vpi_iterate(vpiEnumConst, arg_enum); assert(enum_list); do { cur = vpi_scan(enum_list); if (cur == 0) break; cur_val.format = vpiObjTypeVal; vpi_get_value(cur, &cur_val); assert(var_width == vpi_get(vpiSize, cur)); loc += 1; } while (! compare_value_eequal(&cur_val, &var_val, var_width)); /* If the variable is a vector then free the copy we created above. */ if (var_val.format == vpiVectorVal) free(var_val.value.vector); /* The current value was not found in the list so return X/0. */ if (cur == 0) { /* This only works correctly since we don't really define the * the correct base typespec. */ int is_two_state = vpi_get(vpiBaseTypespec, arg_enum) != vpiReg; fill_handle_with_init(sys, is_two_state); } else { if (strcmp(name, "$ivl_enum_method$next") == 0) { /* Check to see if we need to wrap to the beginning. */ if (loc + count > enum_size) { /* Free the current iterator before creating a new * one that is at the beginning of the list. */ vpi_free_object(enum_list); enum_list = vpi_iterate(vpiEnumConst, arg_enum); assert(enum_list); count -= (enum_size - loc); } } else if (strcmp(name, "$ivl_enum_method$prev") == 0) { /* Because of wrapping the count could be either before * or after the current element. */ if (loc <= count ) { /* The element we want is after the current * element. */ count = enum_size - count; } else { /* The element we want is before the current * element (at the beginning of the list). Free * the current iterator before creating a new * one that is at the beginning of the list. */ vpi_free_object(enum_list); enum_list = vpi_iterate(vpiEnumConst, arg_enum); assert(enum_list); count = loc - count; } } else assert(0); /* Find the correct element. */ while (count) { cur = vpi_scan(enum_list); count -= 1; } assert(cur != 0); /* Free the iterator. */ vpi_free_object(enum_list); /* Get the value and return it. */ cur_val.format = vpiObjTypeVal; vpi_get_value(cur, &cur_val); vpi_put_value(sys, &cur_val, 0, vpiNoDelay); } return 0; } /* * The compiletf routine for the enumeration name() method. */ static PLI_INT32 ivl_enum_method_name_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle arg_enum, arg_var; /* This routine must have arguments. */ if (argv == 0) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("No arguments given for enum method %s().\n", name); vpi_control(vpiFinish, 1); return 0; } /* Check for the enumeration type argument. */ arg_enum = vpi_scan(argv); if (arg_enum == 0) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("No enumeration type argument given for enum " "method %s().\n", name); vpi_control(vpiFinish, 1); return 0; } if (vpi_get(vpiType, arg_enum) != vpiEnumTypespec) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("The first argument to enum method %s() must be an " "enumeration type, found a %s.\n", name, vpi_get_str(vpiType, arg_enum)); vpi_control(vpiFinish, 1); } /* Check for the enumeration variable. */ arg_var = vpi_scan(argv); if (arg_var == 0) { vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("No enumeration variable argument given for enum " "method %s().\n", name); vpi_control(vpiFinish, 1); return 0; } switch(vpi_get(vpiType, arg_var)) { case vpiBitVar: case vpiByteVar: case vpiIntegerVar: case vpiIntVar: case vpiLongIntVar: case vpiReg: case vpiShortIntVar: break; default: vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("The second argument to enum method %s() must be an " "enumeration variable, found a %s.\n", name, vpi_get_str(vpiType, arg_var)); vpi_control(vpiFinish, 1); } /* Only two arguments are supported. */ if (vpi_scan(argv) != 0) { vpi_free_object(argv); vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), (int) vpi_get(vpiLineNo,sys)); vpi_printf("Extra argument(s) given to enum method %s().\n", name); vpi_control(vpiFinish, 1); } return 0; } /* * Implement the name() enumeration method. */ static PLI_INT32 ivl_enum_method_name_calltf(PLI_BYTE8*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle arg_enum = vpi_scan(argv); vpiHandle arg_var = vpi_scan(argv); vpiHandle enum_list; vpiHandle cur; PLI_INT32 var_width = vpi_get(vpiSize, arg_var); s_vpi_value cur_val, var_val; (void)name; /* Parameter is not used. */ /* Free the argument iterator. */ vpi_free_object(argv); /* Get the current value. */ var_val.format = vpiObjTypeVal; vpi_get_value(arg_var, &var_val); /* If the current value is a vector, then make a safe copy of it so that other vpi_get_value() calls don't trash the value. */ if (var_val.format == vpiVectorVal) { PLI_INT32 idx; PLI_INT32 words = (var_width - 1)/32 + 1; p_vpi_vecval nvec = malloc(words*sizeof(s_vpi_vecval)); for (idx = 0 ; idx < words ; idx += 1) { nvec[idx].aval = var_val.value.vector[idx].aval; nvec[idx].bval = var_val.value.vector[idx].bval; } var_val.value.vector = nvec; } /* Search for the current value in the enumeration list. */ enum_list = vpi_iterate(vpiEnumConst, arg_enum); assert(enum_list); do { cur = vpi_scan(enum_list); if (cur == 0) break; cur_val.format = vpiObjTypeVal; vpi_get_value(cur, &cur_val); assert(var_width == vpi_get(vpiSize, cur)); } while (! compare_value_eequal(&cur_val, &var_val, var_width)); /* If the variable is a vector then free the copy we created above. */ if (var_val.format == vpiVectorVal) free(var_val.value.vector); /* The current value was not found in the list so return an empty * string. */ cur_val.format = vpiStringVal; if (cur == 0) { cur_val.value.str = ""; } else { cur_val.value.str = vpi_get_str(vpiName, cur); /* Free the iterator. */ vpi_free_object(enum_list); } /* Return the appropriate string value. */ vpi_put_value(sys, &cur_val, 0, vpiNoDelay); return 0; } void v2009_enum_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.calltf = ivl_enum_method_name_calltf; tf_data.compiletf = ivl_enum_method_name_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$ivl_enum_method$name"; tf_data.user_data = "$ivl_enum_method$name"; res = vpi_register_systf(&tf_data); /* This is not a user defined system function so hide it. */ vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.calltf = ivl_enum_method_next_prev_calltf; tf_data.compiletf = ivl_enum_method_next_prev_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$ivl_enum_method$next"; tf_data.user_data = "$ivl_enum_method$next"; res = vpi_register_systf(&tf_data); /* This is not a user defined system function so hide it. */ vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.calltf = ivl_enum_method_next_prev_calltf; tf_data.compiletf = ivl_enum_method_next_prev_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$ivl_enum_method$prev"; tf_data.user_data = "$ivl_enum_method$prev"; res = vpi_register_systf(&tf_data); /* This is not a user defined system function so hide it. */ vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/v2009_string.c000066400000000000000000000150401265551621300164520ustar00rootroot00000000000000/* * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "sys_priv.h" # include # include # include # include static PLI_INT32 one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; argv = vpi_iterate(vpiArgument, callh); if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a string argument.\n", name); vpi_control(vpiFinish, 1); return 0; } arg = vpi_scan(argv); if (arg == 0) return 0; arg = vpi_scan(argv); if (arg != 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s has too many arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } return 0; } static PLI_INT32 len_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv; vpiHandle arg; (void)name; /* Parameter is not used. */ argv = vpi_iterate(vpiArgument, callh); assert(argv); arg = vpi_scan(argv); assert(arg); vpi_free_object(argv); int res = vpi_get(vpiSize, arg); s_vpi_value value; value.format = vpiIntVal; value.value.integer = res; vpi_put_value(callh, &value, 0, vpiNoDelay); return 0; } static PLI_INT32 to_vec_compiletf(ICARUS_VPI_CONST PLI_BYTE8*user_data) { (void) user_data; /* Parameter is not used. */ vpiHandle systf_handle, arg_iterator, arg_handle; PLI_INT32 arg_type[2]; /* obtain a handle to the system task instance */ systf_handle = vpi_handle(vpiSysTfCall, NULL); if (systf_handle == NULL) { vpi_printf("ERROR: $ivl_string_method$to_vec failed to obtain systf handle\n"); vpi_control(vpiFinish,0); /* abort simulation */ return 0; } /* obtain handles to system task arguments */ arg_iterator = vpi_iterate(vpiArgument, systf_handle); if (arg_iterator == NULL) { vpi_printf("ERROR: $ivl_string_method$to_vec requires 2 arguments\n"); vpi_control(vpiFinish, 0); return 0; } /* check the type of object in system task arguments */ arg_handle = vpi_scan(arg_iterator); for(int i = 0; i < 2; ++i) { arg_type[i] = vpi_get(vpiType, arg_handle); arg_handle = vpi_scan(arg_iterator); } if (arg_handle != NULL) { /* are there more arguments? */ vpi_printf("ERROR: $ivl_string_method$to_vec can only have 2 arguments\n"); vpi_free_object(arg_iterator); vpi_control(vpiFinish, 0); return 0; } if ((arg_type[0] != vpiStringVar) || (arg_type[1] != vpiNet && arg_type[1] != vpiReg)) { vpi_printf("ERROR: $ivl_string_method$to_vec value arguments must be a string and a net or reg\n"); vpi_free_object(arg_iterator); vpi_control(vpiFinish, 0); return 0; } return 0; } static PLI_INT32 to_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, str, vec; s_vpi_value str_val; s_vpi_vecval*vec_val; /* Fetch arguments */ argv = vpi_iterate(vpiArgument, callh); assert(argv); str = vpi_scan(argv); assert(str); vec = vpi_scan(argv); assert(vec); vpi_free_object(argv); int str_size = vpi_get(vpiSize, str); int vec_size = vpi_get(vpiSize, vec); if(str_size <= 0) { vpi_printf("ERROR: Cannot cast empty string"); vpi_control(vpiFinish, 0); return 0; } if(vec_size != str_size * 8) { vpi_printf("ERROR: String and vector size do not match"); vpi_control(vpiFinish, 0); return 0; } str_val.format = vpiStringVal; vpi_get_value(str, &str_val); assert(str_val.value.str); /* Conversion part */ int vec_number = ceil((double)str_size / sizeof(PLI_INT32)); vec_val = calloc(vec_number, sizeof(s_vpi_vecval)); PLI_BYTE8*str_ptr = &str_val.value.str[str_size - 1]; /* We have to reverse the order of string, no memcpy here */ for(int i = 0; i < vec_number; ++i) { int copy_size = str_size > (int)sizeof(PLI_INT32) ? (int)sizeof(PLI_INT32) : str_size; /* Clear the part responsible for X & Z values */ memset(&vec_val[i].bval, 0x00, sizeof(PLI_INT32)); PLI_BYTE8*dest = (PLI_BYTE8*)&vec_val[i].aval; for(int j = 0; j < copy_size; ++j) *dest++ = *str_ptr--; str_size -= copy_size; } str_val.format = vpiVectorVal; str_val.value.vector = vec_val; vpi_put_value(vec, &str_val, 0, vpiNoDelay); free(vec_val); return 0; } void v2009_string_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$ivl_string_method$len"; tf_data.calltf = len_calltf; tf_data.compiletf = one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ivl_string_method$len"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysTask; tf_data.sysfunctype = 0; tf_data.tfname = "$ivl_string_method$to_vec"; tf_data.calltf = to_vec_calltf; tf_data.compiletf = to_vec_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$ivl_string_method$to_vec"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/v2009_table.c000066400000000000000000000021311265551621300162300ustar00rootroot00000000000000/* * Copyright (c) 2010-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern void v2009_array_register(void); extern void v2009_enum_register(void); extern void v2009_string_register(void); void (*vlog_startup_routines[])(void) = { v2009_array_register, v2009_enum_register, v2009_string_register, 0 }; iverilog-10_1/vpi/va_math.c000066400000000000000000000253421265551621300157310ustar00rootroot00000000000000/* * Verilog-A math library for Icarus Verilog * http://www.icarus.com/eda/verilog/ * * Copyright (C) 2007-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "vpi_config.h" #include #include #include #include #include "vpi_user.h" #include "ivl_alloc.h" /* * Compile time options: (set in the Makefile.) * * The functions fmax() and fmin() may not be available in pre-c99 * libraries, so if they are not available you need to uncomment the * -DUSE_MY_FMAX_AND_FMIN in the Makefile. * * Most of the math functions have been moved to v2005_math. This * allows them to be supported separately from the Verilog-A * specific functions. */ /* * These are functionally equivalent fmax() and fmin() implementations * that can be used when needed. */ #ifndef HAVE_FMIN static double va_fmin(const double x, const double y) { if (x != x) {return y;} /* x is NaN so return y. */ if (y != y) {return x;} /* y is NaN so return x. */ return (x < y) ? x : y; } #endif #ifndef HAVE_FMAX static double va_fmax(const double x, const double y) { if (x != x) {return y;} /* x is NaN so return y. */ if (y != y) {return x;} /* y is NaN so return x. */ return (x > y) ? x : y; } #endif /* Single argument functions. */ typedef struct s_single_data { const char *name; double (*func)(double); } t_single_data; static t_single_data va_single_data[]= { {"$abs", fabs}, {0, 0} /* Must be NULL terminated! */ }; /* Double argument functions. */ typedef struct s_double_data { const char *name; double (*func)(double, double); } t_double_data; static t_double_data va_double_data[]= { #ifdef HAVE_FMAX {"$max", fmax}, #else {"$max", va_fmax}, #endif #ifdef HAVE_FMIN {"$min", fmin}, #else {"$min", va_fmin}, #endif {0, 0} /* Must be NULL terminated! */ }; /* * This structure holds the single argument information. */ typedef struct { vpiHandle arg; double (*func)(double); } va_single_t; /* * This structure holds the double argument information. */ typedef struct { vpiHandle arg1; vpiHandle arg2; double (*func)(double, double); } va_double_t; /* * Cleanup the allocated memory at the end of simulation. */ static va_single_t** single_funcs = 0; static unsigned single_funcs_count = 0; static va_double_t** double_funcs = 0; static unsigned double_funcs_count = 0; static PLI_INT32 sys_end_of_simulation(p_cb_data cb_data) { unsigned idx; (void)cb_data; /* Parameter is not used. */ for (idx = 0; idx < single_funcs_count; idx += 1) { free(single_funcs[idx]); } free(single_funcs); single_funcs = 0; single_funcs_count = 0; for (idx = 0; idx < double_funcs_count; idx += 1) { free(double_funcs[idx]); } free(double_funcs); double_funcs = 0; double_funcs_count = 0; return 0; } /* * Standard error message routine. The format string must take one * string argument (the name of the function). */ static void va_error_message(vpiHandle callh, const char *format, const char *name) { vpi_printf("%s:%d: error: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf(format, name); vpi_control(vpiFinish, 1); } /* * Process an argument. */ static vpiHandle va_process_argument(vpiHandle callh, const char *name, vpiHandle arg, const char *post) { PLI_INT32 type; if (arg == NULL) return 0; type = vpi_get(vpiType, arg); /* Math function cannot do anything with a string. */ if ((type == vpiConstant || type == vpiParameter) && (vpi_get(vpiConstType, arg) == vpiStringConst)) { const char* basemsg = "%s cannot process strings"; char* msg = malloc(strlen(basemsg)+strlen(post)+3); strcpy(msg, basemsg); strcat(msg, post); strcat(msg, ".\n"); va_error_message(callh, msg, name); free(msg); return 0; } return arg; } /* * Routine to check all the single argument math functions. */ static PLI_INT32 va_single_argument_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh, argv, arg; const t_single_data *data; const char *name; va_single_t* fun_data; assert(ud != 0); callh = vpi_handle(vpiSysTfCall, 0); assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); data = (const t_single_data *) ud; name = data->name; fun_data = malloc(sizeof(va_single_t)); /* Check that there are arguments. */ if (argv == 0) { va_error_message(callh, "%s requires one argument.\n", name); free(fun_data); return 0; } /* In Icarus if we have an argv we have at least one argument. */ arg = vpi_scan(argv); fun_data->arg = va_process_argument(callh, name, arg, ""); /* These functions only take one argument. */ arg = vpi_scan(argv); if (arg != 0) { va_error_message(callh, "%s takes only one argument.\n", name); vpi_free_object(argv); } /* Get the function that is to be used by the calltf routine. */ fun_data->func = data->func; vpi_put_userdata(callh, fun_data); single_funcs_count += 1; single_funcs = (va_single_t **)realloc(single_funcs, single_funcs_count*sizeof(va_single_t **)); single_funcs[single_funcs_count-1] = fun_data; /* vpi_scan() returning 0 (NULL) has already freed argv. */ return 0; } /* * Routine to implement the single argument math functions. */ static PLI_INT32 va_single_argument_calltf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); s_vpi_value val; va_single_t* fun_data; (void)ud; /* Parameter is not used. */ /* Retrieve the function and argument data. */ fun_data = vpi_get_userdata(callh); /* Calculate the result */ val.format = vpiRealVal; vpi_get_value(fun_data->arg, &val); val.value.real = (fun_data->func)(val.value.real); /* Return the result */ vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Routine to check all the double argument math functions. */ static PLI_INT32 va_double_argument_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh, argv, arg; const t_double_data *data; const char *name; va_double_t* fun_data; assert(ud != 0); callh = vpi_handle(vpiSysTfCall, 0); assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); data = (const t_double_data *) ud; name = data->name; fun_data = malloc(sizeof(va_double_t)); /* Check that there are arguments. */ if (argv == 0) { va_error_message(callh, "%s requires two arguments.\n", name); free(fun_data); return 0; } /* In Icarus if we have an argv we have at least one argument. */ arg = vpi_scan(argv); fun_data->arg1 = va_process_argument(callh, name, arg, " (arg1)"); /* Check that there are at least two arguments. */ arg = vpi_scan(argv); if (arg == 0) { va_error_message(callh, "%s requires two arguments.\n", name); } fun_data->arg2 = va_process_argument(callh, name, arg, " (arg2)"); /* These functions only take two arguments. */ arg = vpi_scan(argv); if (arg != 0) { va_error_message(callh, "%s takes only two arguments.\n", name); vpi_free_object(argv); } /* Get the function that is to be used by the calltf routine. */ fun_data->func = data->func; vpi_put_userdata(callh, fun_data); double_funcs_count += 1; double_funcs = (va_double_t **)realloc(double_funcs, double_funcs_count*sizeof(va_double_t **)); double_funcs[double_funcs_count-1] = fun_data; /* vpi_scan() returning 0 (NULL) has already freed argv. */ return 0; } /* * Routine to implement the double argument math functions. */ static PLI_INT32 va_double_argument_calltf(ICARUS_VPI_CONST PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); s_vpi_value val; double first_arg; va_double_t* fun_data; (void)ud; /* Parameter is not used. */ /* Retrieve the function and argument data. */ fun_data = vpi_get_userdata(callh); /* Calculate the result */ val.format = vpiRealVal; vpi_get_value(fun_data->arg1, &val); first_arg = val.value.real; vpi_get_value(fun_data->arg2, &val); val.value.real = (fun_data->func)(first_arg, val.value.real); /* Return the result */ vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } /* * Register all the functions with Verilog. */ static void va_math_register(void) { s_cb_data cb_data; s_vpi_systf_data tf_data; vpiHandle res; unsigned idx; /* Register the single argument functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = va_single_argument_calltf; tf_data.compiletf = va_single_argument_compiletf; tf_data.sizetf = 0; for (idx=0; va_single_data[idx].name != 0; idx++) { tf_data.tfname = va_single_data[idx].name; tf_data.user_data = (PLI_BYTE8 *) &va_single_data[idx]; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } /* Register the double argument functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = va_double_argument_calltf; tf_data.compiletf = va_double_argument_compiletf; tf_data.sizetf = 0; for (idx=0; va_double_data[idx].name != 0; idx++) { tf_data.tfname = va_double_data[idx].name; tf_data.user_data = (PLI_BYTE8 *) &va_double_data[idx]; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } /* We need to clean up the userdata. */ cb_data.reason = cbEndOfSimulation; cb_data.time = 0; cb_data.cb_rtn = sys_end_of_simulation; cb_data.user_data = "system"; vpi_register_cb(&cb_data); } /* * Hook to get Icarus Verilog to find the registration function. */ void (*vlog_startup_routines[])(void) = { va_math_register, 0 }; iverilog-10_1/vpi/va_math.sft000066400000000000000000000003271265551621300162770ustar00rootroot00000000000000# # This is the system function descriptor table for the # Verilog-A math functions. # # Single argument functions. $abs vpiSysFuncReal # Double argument functions. $min vpiSysFuncReal $max vpiSysFuncReal iverilog-10_1/vpi/vams_simparam.c000066400000000000000000000224121265551621300171440ustar00rootroot00000000000000/* * Copyright (C) 2008-2014 Cary R. (cygcary@yahoo.com) * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "sys_priv.h" #include #include #include #include #include #include "version_base.h" /* Once we have real string objects replace this with a dynamic string. */ #define MAX_STRING_RESULT 1024 /* * Check that the routines are called with the correct arguments. */ static PLI_INT32 simparam_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name_ext) { vpiHandle callh, argv, arg; callh = vpi_handle(vpiSysTfCall, 0); assert(callh != 0); argv = vpi_iterate(vpiArgument, callh); /* We must have at least one argument. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("$simparam%s requires a string argument.\n", name_ext); vpi_control(vpiFinish, 1); return 0; } /* The first argument must be a string. */ arg = vpi_scan(argv); if (! is_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("The first argument to $simparam%s must be a string.\n", name_ext); vpi_control(vpiFinish, 1); } /* The second argument (default value) is optional. */ arg = vpi_scan(argv); if (arg == 0) return 0; /* For the string version the default must also be a string. */ if (strcmp(name_ext, "$str") == 0) { if (! is_string_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("When provided, the second argument to $simparam%s" "must be a string.\n", name_ext); vpi_control(vpiFinish, 1); } /* For the rest the default must be numeric. */ } else { if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("When provided, the second argument to $simparam%s" "must be numeric.\n", name_ext); vpi_control(vpiFinish, 1); } } /* We can have a maximum of two arguments. */ if (vpi_scan(argv) != 0) { char msg[64]; unsigned argc; snprintf(msg, sizeof(msg), "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); msg[sizeof(msg)-1] = 0; argc = 1; while (vpi_scan(argv)) argc += 1; vpi_printf("%s $simparam%s takes at most two arguments.\n", msg, name_ext); vpi_printf("%*s Found %u extra argument%s.\n", (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); vpi_control(vpiFinish, 1); } return 0; } static PLI_INT32 simparam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name_ext) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; char *param; unsigned have_def_val = 0; double retval, defval = 0.0; /* Get the parameter we are looking for. */ arg = vpi_scan(argv); val.format = vpiStringVal; vpi_get_value(arg, &val); param = strdup(val.value.str); /* See if there is a default value. */ arg = vpi_scan(argv); if (arg != 0) { vpi_free_object(argv); have_def_val = 1; val.format = vpiRealVal; vpi_get_value(arg, &val); defval = val.value.real; } /* Now check the various things we can return. */ if (strcmp(param, "gdev") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "gmin") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "imax") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "imelt") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "iteration") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "scale") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "shrink") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "simulatorSubversion") == 0) { retval = VERSION_MINOR; } else if (strcmp(param, "simulatorVersion") == 0) { retval = VERSION_MAJOR; } else if (strcmp(param, "sourceScaleFactor") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "tnom") == 0) { retval = 0.0; /* Nothing for now. */ } else if (strcmp(param, "timeUnit") == 0) { retval = pow(10, vpi_get(vpiTimeUnit, sys_func_module(callh))); } else if (strcmp(param, "timePrecision") == 0) { retval = pow(10, vpi_get(vpiTimePrecision, sys_func_module(callh))); } else if (strcmp(param, "CPUWordSize") == 0) { retval = 8.0*sizeof(long); } else { if (! have_def_val) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("$simparam%s unknown parameter name \"%s\".\n", name_ext, param); } retval = defval; } free(param); /* Return the value to the system. */ val.format = vpiRealVal; val.value.real = retval; vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } static PLI_INT32 simparam_str_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name_ext) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; char *param; char *retval, *defval = NULL; /* Get the parameter we are looking for. */ arg = vpi_scan(argv); val.format = vpiStringVal; vpi_get_value(arg, &val); param = strdup(val.value.str); /* See if there is a default value. */ arg = vpi_scan(argv); if (arg != 0) { vpi_free_object(argv); val.format = vpiStringVal; vpi_get_value(arg, &val); defval = strdup(val.value.str); } /* Now check the various things we can return. */ /* For now we limit the result to 1024 characters. */ if (strcmp(param, "analysis_name") == 0) { retval = strdup("N/A"); /* Nothing for now. */ } else if (strcmp(param, "analysis_type") == 0) { retval = strdup("N/A"); /* Nothing for now. */ } else if (strcmp(param, "cwd") == 0) { char path [MAX_STRING_RESULT]; char *ptr = getcwd(path, MAX_STRING_RESULT); if (ptr == NULL) { strcpy(path, ""); } retval = strdup(path); } else if (strcmp(param, "module") == 0) { retval = strdup(vpi_get_str(vpiDefName, sys_func_module(callh))); } else if (strcmp(param, "instance") == 0) { retval = strdup(vpi_get_str(vpiFullName, sys_func_module(callh))); } else if (strcmp(param, "path") == 0) { retval = strdup(vpi_get_str(vpiFullName, vpi_handle(vpiScope,callh))); } else { if (defval == NULL) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("$simparam%s unknown parameter name \"%s\".\n", name_ext, param); defval = strdup(""); } retval = defval; } free(param); /* Return the value to the system. */ val.format = vpiStringVal; val.value.str = retval; vpi_put_value(callh, &val, 0, vpiNoDelay); if (defval != retval) free(defval); free(retval); return 0; } static PLI_INT32 simparam_str_sizetf(PLI_BYTE8 *name_ext) { (void) name_ext; /* Parameter is not used. */ return MAX_STRING_RESULT; /* 128 characters max! */ } /* * Register the function with Verilog. */ void vams_simparam_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = simparam_calltf; tf_data.compiletf = simparam_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$simparam"; tf_data.user_data = ""; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; /* What should this be? */ tf_data.calltf = simparam_str_calltf; tf_data.compiletf = simparam_compiletf; tf_data.sizetf = simparam_str_sizetf; /* Only 128 characters! */ tf_data.tfname = "$simparam$str"; tf_data.user_data = "$str"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } iverilog-10_1/vpi/vcd_priv.c000066400000000000000000000146651265551621300161340ustar00rootroot00000000000000/* * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sys_priv.h" #include "vcd_priv.h" #include "ivl_alloc.h" #include #include #include #include #include #include "stringheap.h" int is_escaped_id(const char *name) { assert(name); /* The first digit must be alpha or '_' to be a normal id. */ if (isalpha((int)name[0]) || name[0] == '_') { int lp; for (lp=1; name[lp] != '\0'; lp++) { /* If this digit is not alpha-numeric or '_' we have * an escaped identifier. */ if (!(isalnum((int)name[lp]) || name[lp] == '_') || name[lp] == '$') { return 1; } } /* We looked at all the digits, so this is a normal id. */ return 0; } return 1; } struct stringheap_s name_heap = {0, 0}; struct vcd_names_s { const char *name; struct vcd_names_s *next; }; void vcd_names_add(struct vcd_names_list_s*tab, const char *name) { struct vcd_names_s *nl = (struct vcd_names_s *) malloc(sizeof(struct vcd_names_s)); nl->name = strdup_sh(&name_heap, name); nl->next = tab->vcd_names_list; tab->vcd_names_list = nl; tab->listed_names ++; } void vcd_names_delete(struct vcd_names_list_s*tab) { struct vcd_names_s *cur, *tmp; for (cur = tab->vcd_names_list; cur; cur = tmp) { tmp = cur->next; free(cur); } tab->vcd_names_list = 0; tab->listed_names = 0; free(tab->vcd_names_sorted); tab->vcd_names_sorted = 0; tab->sorted_names = 0; string_heap_delete(&name_heap); } static int vcd_names_compare(const void *s1, const void *s2) { const char *v1 = *(const char * const *) s1; const char *v2 = *(const char * const *) s2; return strcmp(v1, v2); } const char *vcd_names_search(struct vcd_names_list_s*tab, const char *key) { const char **v; if (tab->vcd_names_sorted == 0) return 0; v = (const char **) bsearch(&key, tab->vcd_names_sorted, tab->sorted_names, sizeof(const char *), vcd_names_compare ); return(v ? *v : NULL); } void vcd_names_sort(struct vcd_names_list_s*tab) { if (tab->listed_names) { struct vcd_names_s *r; const char **l; tab->sorted_names += tab->listed_names; tab->vcd_names_sorted = (const char **) realloc(tab->vcd_names_sorted, tab->sorted_names*(sizeof(const char *))); l = tab->vcd_names_sorted + tab->sorted_names - tab->listed_names; tab->listed_names = 0; r = tab->vcd_names_list; tab->vcd_names_list = 0x0; while (r) { struct vcd_names_s *rr = r; r = rr->next; *(l++) = rr->name; free(rr); } qsort(tab->vcd_names_sorted, tab->sorted_names, sizeof(const char **), vcd_names_compare); } } /* * Since the compiletf routines are all the same they are located here, * so we only need a single copy. Some are generic enough they can use * the ones in sys_priv.c (no arg, one numeric argument, etc.). */ /* $dumpvars takes a variety of arguments. */ PLI_INT32 sys_dumpvars_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* No arguments is OK, dump everything. */ if (argv == 0) return 0; /* The first argument is the numeric level. */ if (! is_numeric_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's argument must be numeric.\n", name); vpi_control(vpiFinish, 1); } /* The rest of the arguments are either a module or a variable. */ while ((arg=vpi_scan(argv)) != NULL) { switch(vpi_get(vpiType, arg)) { case vpiMemoryWord: /* * We need to allow non-constant selects to support the following: * * for (lp = 0; lp < max ; lp = lp + 1) $dumpvars(0, array[lp]); * * We need to do a direct callback on the selected element vs using * the &A<> structure. The later will not give us what we want. * This is implemented in the calltf routine. */ #if 0 if (vpi_get(vpiConstantSelect, arg) == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot dump a non-constant select %s.\n", name, vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); } #endif /* The module types. */ case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: /* The variable types. */ #if 0 case vpiParameter: /* A constant! */ #endif case vpiNet: case vpiReg: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: case vpiRealVar: case vpiNamedEvent: break; case vpiParameter: /* A constant! */ vpi_printf("SORRY: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot currently dump a parameter.\n", name); break; default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot dump a %s.\n", name, vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); } } return 0; } iverilog-10_1/vpi/vcd_priv.h000066400000000000000000000076771265551621300161460ustar00rootroot00000000000000#ifndef IVL_vcd_priv_H #define IVL_vcd_priv_H /* * Copyright (c) 2003-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vpi_user.h" #ifdef __cplusplus # define EXTERN extern "C" #else # define EXTERN extern #endif EXTERN int is_escaped_id(const char *name); struct vcd_names_s; EXTERN struct stringheap_s name_heap; struct vcd_names_list_s { struct vcd_names_s *vcd_names_list; const char **vcd_names_sorted; int listed_names, sorted_names; }; EXTERN void vcd_names_add(struct vcd_names_list_s*tab, const char *name); EXTERN const char *vcd_names_search(struct vcd_names_list_s*tab, const char *key); EXTERN void vcd_names_sort(struct vcd_names_list_s*tab); EXTERN void vcd_names_delete(struct vcd_names_list_s*tab); /* * Keep a map of nexus ident's to help with alias detection. */ EXTERN const char*find_nexus_ident(int nex); EXTERN void set_nexus_ident(int nex, const char *id); EXTERN void nexus_ident_delete(void); /* * Keep a set of scope names to help with duplicate detection. */ EXTERN void vcd_scope_names_add(const char*name); EXTERN int vcd_scope_names_test(const char*name); EXTERN void vcd_scope_names_delete(void); /* * Implement a work queue that can be used to send commands to a * dumper thread. */ typedef enum vcd_work_item_type_e { WT_NONE, WT_EMIT_BITS, WT_EMIT_DOUBLE, WT_DUMPON, WT_DUMPOFF, WT_FLUSH, WT_TERMINATE } vcd_work_item_type_t; struct lxt2_wr_symbol; struct vcd_work_item_s { vcd_work_item_type_t type; uint64_t time; union { struct lxt2_wr_symbol*lxt2; } sym_; union { double val_double; char*val_char; } op_; }; /* * The thread_peek and thread_pop functions work as pairs. The work * thread processing work items uses vcd_work_thread_peek to look at * the first item in the work queue. The work thread can be assured * that the work item it stable. When it is done with the work item, * it calls vcd_work_thread_pop to cause it to be popped from the work * queue. */ EXTERN struct vcd_work_item_s* vcd_work_thread_peek(void); EXTERN void vcd_work_thread_pop(void); /* * Create work threads with the vcd_work_start function, and terminate * the work thread (gracefully) with the vcd_work_terminate * function. Synchronize with the work thread with the vcd_work_sync * function. This blocks until the work thread is done all the work it * has so far. */ EXTERN void vcd_work_start( void* (*fun) (void*arg), void*arg); EXTERN void vcd_work_terminate(void); EXTERN void vcd_work_sync(void); /* * The remaining vcd_work_* functions send messages to the work thread * causing it to perform various VCD-related tasks. */ EXTERN void vcd_work_flush(void); /* Drain output caches. */ EXTERN void vcd_work_set_time(uint64_t val); EXTERN void vcd_work_dumpon(void); EXTERN void vcd_work_dumpoff(void); EXTERN void vcd_work_emit_double(struct lxt2_wr_symbol*sym, double val); EXTERN void vcd_work_emit_bits(struct lxt2_wr_symbol*sym, const char*bits); /* The compiletf routines are common for the VCD, LXT and LXT2 dumpers. */ EXTERN PLI_INT32 sys_dumpvars_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); #undef EXTERN #endif /* IVL_vcd_priv_H */ iverilog-10_1/vpi/vcd_priv2.cc000066400000000000000000000176701265551621300163600ustar00rootroot00000000000000/* * Copyright (c) 2010-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vcd_priv.h" # include # include # include # include # include # include # include /* Nexus Id cache In structural models, many signals refer to the same nexus. Some structural models also have very many signals. This cache saves nexus_id - vcd_id pairs, and reuses the vcd_id when a signal refers to a nexus that is already dumped. The new signal will be listed as a $var, but no callback will be installed. This saves considerable CPU time and leads to smaller VCD files. The _vpiNexusId is a private (int) property of IVL simulators. */ static std::map nexus_ident_map; extern "C" const char*find_nexus_ident(int nex) { std::map::const_iterator cur = nexus_ident_map.find(nex); if (cur == nexus_ident_map.end()) return 0; else return cur->second; } extern "C" void set_nexus_ident(int nex, const char*id) { nexus_ident_map[nex] = id; } extern "C" void nexus_ident_delete() { nexus_ident_map.clear(); } static std::set vcd_scope_names_set; extern "C" void vcd_scope_names_add(const char*name) { vcd_scope_names_set .insert(name); } extern "C" int vcd_scope_names_test(const char*name) { if (vcd_scope_names_set.find(name) == vcd_scope_names_set.end()) return 0; else return 1; } extern "C" void vcd_scope_names_delete(void) { vcd_scope_names_set.clear(); } static pthread_t work_thread; static const unsigned WORK_QUEUE_SIZE = 128*1024; static const unsigned WORK_QUEUE_BATCH_MIN = 4*1024; static const unsigned WORK_QUEUE_BATCH_MAX = 32*1024; static struct vcd_work_item_s work_queue[WORK_QUEUE_SIZE]; static volatile unsigned work_queue_next = 0; static volatile unsigned work_queue_fill = 0; static pthread_mutex_t work_queue_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t work_queue_is_empty_sig = PTHREAD_COND_INITIALIZER; static pthread_cond_t work_queue_notempty_sig = PTHREAD_COND_INITIALIZER; static pthread_cond_t work_queue_minfree_sig = PTHREAD_COND_INITIALIZER; extern "C" struct vcd_work_item_s* vcd_work_thread_peek(void) { // There must always only be 1 vcd work thread, and only the // work thread decreases the fill, so if the work_queue_fill // is non-zero, I can reliably assume that there is at least // one item that I can peek at. I only need to lock if I must // wait for the work_queue_fill to become non-zero. if (work_queue_fill == 0) { pthread_mutex_lock(&work_queue_mutex); while (work_queue_fill == 0) pthread_cond_wait(&work_queue_notempty_sig, &work_queue_mutex); pthread_mutex_unlock(&work_queue_mutex); } return work_queue + work_queue_next; } extern "C" void vcd_work_thread_pop(void) { pthread_mutex_lock(&work_queue_mutex); unsigned use_fill = work_queue_fill - 1; work_queue_fill = use_fill; unsigned use_next = work_queue_next; struct vcd_work_item_s*cell = work_queue + use_next; if (cell->type == WT_EMIT_BITS) { free(cell->op_.val_char); } use_next += 1; if (use_next >= WORK_QUEUE_SIZE) use_next = 0; work_queue_next = use_next; if (use_fill == WORK_QUEUE_SIZE-WORK_QUEUE_BATCH_MIN) pthread_cond_signal(&work_queue_minfree_sig); else if (use_fill == 0) pthread_cond_signal(&work_queue_is_empty_sig); pthread_mutex_unlock(&work_queue_mutex); } /* * Work queue items are created in batches to reduce thread * bouncing. When the producer gets a free work item, it actually * locks the queue in order to produce a batch. The queue stays locked * until the batch is complete. Then the releases the whole lot to the * consumer. */ static uint64_t work_queue_next_time = 0; static unsigned current_batch_cnt = 0; static unsigned current_batch_alloc = 0; static unsigned current_batch_base = 0; extern "C" void vcd_work_start( void* (*fun) (void*), void*arg ) { pthread_create(&work_thread, 0, fun, arg); } static struct vcd_work_item_s* grab_item(void) { if (current_batch_alloc == 0) { pthread_mutex_lock(&work_queue_mutex); while ((WORK_QUEUE_SIZE-work_queue_fill) < WORK_QUEUE_BATCH_MIN) pthread_cond_wait(&work_queue_minfree_sig, &work_queue_mutex); current_batch_base = work_queue_next + work_queue_fill; current_batch_alloc = WORK_QUEUE_SIZE - work_queue_fill; pthread_mutex_unlock(&work_queue_mutex); if (current_batch_base >= WORK_QUEUE_SIZE) current_batch_base -= WORK_QUEUE_SIZE; if (current_batch_alloc > WORK_QUEUE_BATCH_MAX) current_batch_alloc = WORK_QUEUE_BATCH_MAX; current_batch_cnt = 0; } assert(current_batch_cnt < current_batch_alloc); unsigned cur = current_batch_base + current_batch_cnt; if (cur >= WORK_QUEUE_SIZE) cur -= WORK_QUEUE_SIZE; // Write the new timestamp into the work item. struct vcd_work_item_s*cell = work_queue + cur; cell->time = work_queue_next_time; return cell; } static void end_batch(void) { pthread_mutex_lock(&work_queue_mutex); unsigned use_fill = work_queue_fill; bool was_empty_flag = (use_fill==0) && (current_batch_cnt > 0); use_fill += current_batch_cnt; work_queue_fill = use_fill; current_batch_alloc = 0; current_batch_cnt = 0; if (was_empty_flag) pthread_cond_signal(&work_queue_notempty_sig); pthread_mutex_unlock(&work_queue_mutex); } static inline void unlock_item(bool flush_batch =false) { current_batch_cnt += 1; if (current_batch_cnt == current_batch_alloc || flush_batch) end_batch(); } extern "C" void vcd_work_sync(void) { if (current_batch_alloc > 0) end_batch(); if (work_queue_fill > 0) { pthread_mutex_lock(&work_queue_mutex); while (work_queue_fill > 0) pthread_cond_wait(&work_queue_is_empty_sig, &work_queue_mutex); pthread_mutex_unlock(&work_queue_mutex); } } extern "C" void vcd_work_flush(void) { struct vcd_work_item_s*cell = grab_item(); cell->type = WT_FLUSH; unlock_item(true); } extern "C" void vcd_work_dumpon(void) { struct vcd_work_item_s*cell = grab_item(); cell->type = WT_DUMPON; unlock_item(); } extern "C" void vcd_work_dumpoff(void) { struct vcd_work_item_s*cell = grab_item(); cell->type = WT_DUMPOFF; unlock_item(); } extern "C" void vcd_work_set_time(uint64_t val) { work_queue_next_time = val; } extern "C" void vcd_work_emit_double(struct lxt2_wr_symbol*sym, double val) { struct vcd_work_item_s*cell = grab_item(); cell->type = WT_EMIT_DOUBLE; cell->sym_.lxt2 = sym; cell->op_.val_double = val; unlock_item(); } extern "C" void vcd_work_emit_bits(struct lxt2_wr_symbol*sym, const char* val) { struct vcd_work_item_s*cell = grab_item(); cell->type = WT_EMIT_BITS; cell->sym_.lxt2 = sym; cell->op_.val_char = strdup(val); unlock_item(); } extern "C" void vcd_work_terminate(void) { struct vcd_work_item_s*cell = grab_item(); cell->type = WT_TERMINATE; unlock_item(true); pthread_join(work_thread, 0); } iverilog-10_1/vpi/vhdl_sys.sft000066400000000000000000000002141265551621300165060ustar00rootroot00000000000000$ivlh_attribute_event vpiSysFuncSized 1 unsigned $ivlh_rising_edge vpiSysFuncSized 1 unsigned $ivlh_falling_edge vpiSysFuncSized 1 unsigned iverilog-10_1/vpi/vhdl_table.c000066400000000000000000000161221265551621300164120ustar00rootroot00000000000000/* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vpi_config.h" # include "vpi_user.h" # include # include "ivl_alloc.h" /* * The $ivlh_attribute_event implements the VHDL 'event * attribute. It does this by monitoring value-change events on the * operand, and noting the time. If the $ivlh_attribute_event is * called at the same simulation time as a value-change, then the * function returns logic true. Otherwise it returns false. * * $ivlh_{rising,falling}_edge implement the VHDL rising_edge() and * falling_edge() system functions. */ struct monitor_data { struct t_vpi_time last_event; struct t_vpi_value last_value; }; static struct monitor_data **mdata = 0; static unsigned mdata_count = 0; typedef enum { EVENT = 0, RISING_EDGE = 1, FALLING_EDGE = 2 } event_type_t; static const char* func_names[] = { "$ivlh_attribute_event", "$ivlh_rising_edge", "$ivlh_falling_edge" }; /* To keep valgrind happy free the allocated memory. */ static PLI_INT32 cleanup_mdata(p_cb_data cause) { unsigned idx; (void) cause; /* Parameter is not used. */ for (idx= 0; idx < mdata_count; idx += 1) { free(mdata[idx]); } free(mdata); mdata = 0; mdata_count = 0; return 0; } static PLI_INT32 monitor_events(struct t_cb_data*cb) { struct monitor_data*mon = (struct monitor_data*)(cb->user_data); assert(cb->time); assert(cb->time->type == vpiSimTime); mon->last_event = *(cb->time); mon->last_value = *(cb->value); return 0; } static PLI_INT32 ivlh_attribute_event_compiletf(ICARUS_VPI_CONST PLI_BYTE8*data) { event_type_t type = (event_type_t) data; vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle arg; struct monitor_data*mon; struct t_cb_data cb; struct t_vpi_time tb; struct t_vpi_value vb; /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys), (int)vpi_get(vpiLineNo, sys)); vpi_printf("(compiler error) %s requires a single argument.\n", func_names[type]); vpi_control(vpiFinish, 1); return 0; } /* Icarus either returns 0 above or has one argument. */ arg = vpi_scan(argv); assert(arg); mon = malloc(sizeof(struct monitor_data)); /* Add this to the list of data. */ mdata_count += 1; mdata = (struct monitor_data **) realloc(mdata, sizeof(struct monitor_data **) * mdata_count); mdata[mdata_count-1] = mon; tb.type = vpiSimTime; vb.format = vpiScalarVal; cb.reason = cbValueChange; cb.cb_rtn = monitor_events; cb.obj = arg; cb.time = &tb; cb.value = &vb; cb.user_data = (char*) (mon); vpi_register_cb(&cb); vpi_put_userdata(sys, mon); /* Check that there is no more than one argument. */ arg = vpi_scan(argv); if (arg != 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys), (int)vpi_get(vpiLineNo, sys)); vpi_printf("(compiler error) %s only takes a single argument.\n", func_names[type]); vpi_free_object(argv); vpi_control(vpiFinish, 1); } return 0; } static PLI_INT32 ivlh_attribute_event_calltf(ICARUS_VPI_CONST PLI_BYTE8*data) { event_type_t type = (event_type_t) data; vpiHandle sys = vpi_handle(vpiSysTfCall, 0); struct t_vpi_value rval; struct monitor_data*mon; rval.format = vpiScalarVal; mon = (struct monitor_data*) (vpi_get_userdata(sys)); if (mon->last_event.type == 0) { rval.value.scalar = vpi0; } else { struct t_vpi_time tnow; tnow.type = vpiSimTime; vpi_get_time(0, &tnow); rval.value.scalar = vpi1; // Detect if change occurred in this moment if (mon->last_event.high != tnow.high) rval.value.scalar = vpi0; if (mon->last_event.low != tnow.low) rval.value.scalar = vpi0; // Determine the edge, if required if (type == RISING_EDGE && mon->last_value.value.scalar != vpi1) rval.value.scalar = vpi0; else if (type == FALLING_EDGE && mon->last_value.value.scalar != vpi0) rval.value.scalar = vpi0; } vpi_put_value(sys, &rval, 0, vpiNoDelay); return 0; } static PLI_INT32 ivlh_attribute_event_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type) { (void) type; /* Parameter is not used. */ return 1; } static void vhdl_register(void) { s_vpi_systf_data tf_data; s_cb_data cb; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; tf_data.tfname = func_names[EVENT]; tf_data.user_data = (PLI_BYTE8*) EVENT; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; tf_data.tfname = func_names[RISING_EDGE]; tf_data.user_data = (PLI_BYTE8*) RISING_EDGE; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; tf_data.tfname = func_names[FALLING_EDGE]; tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* Create a callback to clear the monitor data memory when the * simulator finishes. */ cb.time = NULL; cb.reason = cbEndOfSimulation; cb.cb_rtn = cleanup_mdata; cb.user_data = NULL; cb.obj = NULL; vpi_register_cb(&cb); } void (*vlog_startup_routines[])(void) = { vhdl_register, 0 }; iverilog-10_1/vpi/vpi_config.h.in000066400000000000000000000022241265551621300170410ustar00rootroot00000000000000#ifndef IVL_vpi_config_H #define IVL_vpi_config_H /* * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # undef HAVE_LIBIBERTY_H # undef HAVE_INTTYPES_H # undef HAVE_LIBZ # undef HAVE_LIBBZ2 # undef HAVE_FMIN # undef HAVE_FMAX # undef WORDS_BIGENDIAN # undef _LARGEFILE_SOURCE #ifdef _LARGEFILE_SOURCE # define _FILE_OFFSET_BITS 64 #endif #endif /* IVL_vpi_config_H */ iverilog-10_1/vpi/vpi_debug.c000066400000000000000000000064531265551621300162600ustar00rootroot00000000000000/* * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The $vpi_tree(...) system task dumps the scopes listed in the * arguments. If no arguments are given, then dump the scope that * contains the call to the $vpi_tree function. */ # include "vpi_user.h" # include # include static void dump_object(vpiHandle item) { PLI_INT32 item_type = vpi_get(vpiType, item); vpiHandle argv, cur; vpi_printf("%s: ", vpi_get_str(vpiFullName, item)); vpi_printf("vpiType=%s (%d)\n", vpi_get_str(vpiType, item), item_type); switch (item_type) { /* These types are themselves scopes and have objects within. */ case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: argv = vpi_iterate(vpiScope, item); for (cur = vpi_scan(argv) ; cur ; cur = vpi_scan(argv)) dump_object(cur); break; #if 0 case vpiRegArray: case vpiNetArray: vpi_printf("%s: ", vpi_get_str(vpiFullName, item)); vpi_printf("vpiType=%s (%d)\n", vpi_get_str(vpiType, item), item_type); argv = vpi_iterate(vpiMember, item); for (cur = vpi_scan(argv) ; cur ; cur = vpi_scan(argv)) dump_object(cur); break; #endif /* vpiMemory contains words. */ case vpiMemory: argv = vpi_iterate(vpiMemoryWord, item); for (cur = vpi_scan(argv) ; cur ; cur = vpi_scan(argv)) dump_object(cur); break; default: break; } } static PLI_INT32 vpi_tree_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 0; } static PLI_INT32 vpi_tree_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle item; (void)name; /* Parameter is not used. */ if (argv == 0) { item = vpi_handle(vpiScope, callh); dump_object(item); return 0; } for (item = vpi_scan(argv) ; item ; item = vpi_scan(argv)) dump_object(item); return 0; } void sys_register(void) { s_vpi_systf_data tf_data; vpiHandle res; tf_data.type = vpiSysTask; tf_data.tfname = "$vpi_tree"; tf_data.calltf = vpi_tree_calltf; tf_data.compiletf = vpi_tree_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$vpi_tree"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } void (*vlog_startup_routines[])(void) = { sys_register, 0 }; iverilog-10_1/vpi/wavealloca.h000066400000000000000000000012031265551621300164230ustar00rootroot00000000000000/* * Copyright (c) Tony Bybell 1999. * * 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. */ #ifndef WAVE_ALLOCA_H #define WAVE_ALLOCA_H #include #ifdef HAVE_ALLOCA_H #include #elif defined(__GNUC__) #ifndef __MINGW32__ #ifndef alloca #define alloca __builtin_alloca #endif #else #include #endif #elif defined(_MSC_VER) #include #define alloca _alloca #endif #define wave_alloca alloca #endif iverilog-10_1/vpi_user.h000066400000000000000000000522451265551621300153570ustar00rootroot00000000000000#ifndef VPI_USER_H #define VPI_USER_H /* * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined(__MINGW32__) || defined (__CYGWIN32__) # define DLLEXPORT __declspec(dllexport) #else # define DLLEXPORT #endif #ifdef __cplusplus # define EXTERN_C_START extern "C" { # define EXTERN_C_END } #else # define EXTERN_C_START # define EXTERN_C_END #endif #ifndef __GNUC__ # undef __attribute__ # define __attribute__(x) #endif EXTERN_C_START # include # include # include # include "_pli_types.h" #define ICARUS_VPI_CONST #ifdef __cplusplus typedef class __vpiHandle *vpiHandle; #else typedef struct __vpiHandle *vpiHandle; #endif /* * This structure is created by the VPI application to provide hooks * into the application that the compiler/simulator can access. */ typedef struct t_vpi_systf_data { PLI_INT32 type; PLI_INT32 sysfunctype; const char *tfname; PLI_INT32 (*calltf) (ICARUS_VPI_CONST PLI_BYTE8*); PLI_INT32 (*compiletf)(ICARUS_VPI_CONST PLI_BYTE8*); PLI_INT32 (*sizetf) (PLI_BYTE8*); ICARUS_VPI_CONST PLI_BYTE8 *user_data; } s_vpi_systf_data, *p_vpi_systf_data; /* The type in the above structure can have one of the following values: */ #define vpiSysTask 1 #define vpiSysFunc 2 typedef struct t_vpi_vlog_info { PLI_INT32 argc; char **argv; char *product; char *version; } s_vpi_vlog_info, *p_vpi_vlog_info; typedef struct t_vpi_time { /* Type can be : vpiScaledRealTime == 1 vpiSimTime == 2 vpiSuppressTime == 3 */ PLI_INT32 type; PLI_UINT32 high; PLI_UINT32 low; double real; } s_vpi_time, *p_vpi_time; #define vpiScaledRealTime 1 #define vpiSimTime 2 #define vpiSuppressTime 3 typedef struct t_vpi_vecval { PLI_INT32 aval, bval; /* ab encoding: 00=0, 10=1, 11=X, 01=Z */ } s_vpi_vecval, *p_vpi_vecval; typedef struct t_vpi_strengthval { PLI_INT32 logic; PLI_INT32 s0, s1; } s_vpi_strengthval, *p_vpi_strengthval; /* * This structure holds values that are passed back and forth between * the simulator and the application. */ typedef struct t_vpi_value { PLI_INT32 format; union { char *str; PLI_INT32 scalar; PLI_INT32 integer; double real; struct t_vpi_time *time; struct t_vpi_vecval *vector; struct t_vpi_strengthval *strength; char *misc; } value; } s_vpi_value, *p_vpi_value; /* Conform the IEEE 1364, We add the Standard vpi_delay structure to enable the modpath delay values Conform IEEE 1364, Pg 670 : The "da" field of the s_vpi_delay structure shall be a user allocated array of "s_vpi_time" structure The array shall store delay values returned by vpi_get_delay(). The number of elements in the array shall be determined by (1) The number of delays to be retrieved ( normally this is used in vpi_get_delays (..) ) { (1.1) Set by "no_of_delays" field (1.2) For the primitive_object, the no_of_delays shall be 2 or 3 (1.3) For path_delay object the no_of_delays shall be 1,2,3,6, 12 (1.4) For timing_check_object, the no_of_delays shall be match the number of limits existing in the Time Check (1.5) For intermodule_path object, the no_of_delays shall be 2 or 3 } (2) The "mtm_flag" && "pulsere_flag" Normally, if you set mtm = X, pulsere = Y then, you will need allocate (num * no_of_delay) s_vpi_time elements for 'da' array before calling the vpi_get/put_delays (..) --------------------------------------------------------------------------- | | | | | mtm_flag | No of s_vpi_time array | order in which delay | | pulsere_flag | element required by the | elements shall be filed | | | s_vpi_delay->da | | | | | | |----------------|-------------------------|------------------------------| | | | 1o delay da[0]--> 1o delay | | mtm = false | no_of_delay | 2o delay da[1]--> 2o delay | | pulere = false | | | | | | | |----------------|-------------------------|------------------------------| | | | 1o delay da[0]--> min delay | | mtm = true | | da[1]--> typ delay | | pulere = false | 3*no_of_delay | da[2]--> max delay | | | | 2o delay da[3]--> min delay | | | | da[4]--> typ delay | | | | .... | |----------------|-------------------------|------------------------------| | | | 1o delay da[0]--> delay | | mtm = false | | da[1]--> rej limit | | pulere = true | 3*no_of_delay | da[2]--> err limit | | | | 2o delay da[3]--> delay | | | | da[4]--> rej limit | | | | .... | |----------------|-------------------------|------------------------------| | | | 1o delay da[0]--> min delay | | mtm = true | | da[1]--> typ delay | | pulere = true | 9*no_of_delay | da[2]--> max delay | | | | da[3]--> min delay | | | | da[4]--> typ delay | | | | da[5]--> max delay | | | | da[6]--> min delay | | | | da[7]--> typ delay | | | | da[8]--> max delay | | | | 2o delay da[9]--> min delay | | | | .... | ------------------------------------------------------------------------- IMPORTANT : The delay Structure has to be allocated before passing a pointer to "vpi_get_delays ( )". */ typedef struct t_vpi_delay { struct t_vpi_time *da; /* Array of delay data */ PLI_INT32 no_of_delays ; PLI_INT32 time_type; /* vpiScaledRealTime, vpiSimTime */ PLI_INT32 mtm_flag; PLI_INT32 append_flag; PLI_INT32 plusere_flag; } s_vpi_delay, *p_vpi_delay; /* These are valid codes for the format of the t_vpi_value structure. */ #define vpiBinStrVal 1 #define vpiOctStrVal 2 #define vpiDecStrVal 3 #define vpiHexStrVal 4 #define vpiScalarVal 5 #define vpiIntVal 6 #define vpiRealVal 7 #define vpiStringVal 8 #define vpiVectorVal 9 #define vpiStrengthVal 10 #define vpiTimeVal 11 #define vpiObjTypeVal 12 #define vpiSuppressVal 13 /* SCALAR VALUES */ #define vpi0 0 #define vpi1 1 #define vpiZ 2 #define vpiX 3 #define vpiH 4 #define vpiL 5 #define vpiDontCare 6 /* STRENGTH VALUES */ #define vpiSupplyDrive 0x80 #define vpiStrongDrive 0x40 #define vpiPullDrive 0x20 #define vpiLargeCharge 0x10 #define vpiWeakDrive 0x08 #define vpiMediumCharge 0x04 #define vpiSmallCharge 0x02 #define vpiHiZ 0x01 /* OBJECT CODES */ #define vpiConstant 7 #define vpiFunction 20 #define vpiIntegerVar 25 #define vpiIterator 27 #define vpiMemory 29 #define vpiMemoryWord 30 #define vpiModPath 31 #define vpiModule 32 #define vpiNamedBegin 33 #define vpiNamedEvent 34 #define vpiNamedFork 35 #define vpiNet 36 #define vpiParameter 41 #define vpiPartSelect 42 #define vpiPathTerm 43 #define vpiPort 44 #define vpiRealVar 47 #define vpiReg 48 #define vpiSysFuncCall 56 #define vpiSysTaskCall 57 #define vpiTask 59 #define vpiTimeVar 63 #define vpiUdpDefn 66 #define vpiUserSystf 67 #define vpiNetArray 114 #define vpiIndex 78 #define vpiLeftRange 79 #define vpiParent 81 #define vpiRightRange 83 #define vpiScope 84 #define vpiSysTfCall 85 #define vpiArgument 89 #define vpiInternalScope 92 #define vpiModPathIn 95 #define vpiModPathOut 96 #define vpiVariables 100 #define vpiExpr 102 /********************** object types added with 1364-2001 *********************/ #define vpiCallback 107 #define vpiRegArray 116 /********************** object types added with 1364-2005 *********************/ #define vpiGenScope 134 /* PROPERTIES */ #define vpiUndefined (-1) #define vpiType 1 #define vpiName 2 #define vpiFullName 3 #define vpiSize 4 #define vpiFile 5 #define vpiLineNo 6 #define vpiTopModule 7 #define vpiCellInstance 8 #define vpiDefName 9 #define vpiTimeUnit 11 #define vpiTimePrecision 12 #define vpiDefFile 15 #define vpiDefLineNo 16 #define vpiScalar 17 #define vpiVector 18 #define vpiDirection 20 /* direction of port: */ # define vpiInput 1 # define vpiOutput 2 # define vpiInout 3 # define vpiMixedIO 4 /* Not currently output */ # define vpiNoDirection 5 #define vpiNetType 22 # define vpiWire 1 # define vpiWand 2 # define vpiWor 3 # define vpiTri 4 # define vpiTri0 5 # define vpiTri1 6 # define vpiTriReg 7 # define vpiTriAnd 8 # define vpiTriOr 9 # define vpiSupply1 10 # define vpiSupply0 11 #define vpiArray 28 #define vpiPortIndex 29 #define vpiEdge 36 # define vpiNoEdge 0x00 /* No edge */ # define vpiEdge01 0x01 /* 0 --> 1 */ # define vpiEdge10 0x02 /* 1 --> 0 */ # define vpiEdge0x 0x04 /* 0 --> x */ # define vpiEdgex1 0x08 /* x --> 1 */ # define vpiEdge1x 0x10 /* 1 --> x */ # define vpiEdgex0 0x20 /* x --> 0 */ # define vpiPosedge (vpiEdgex1|vpiEdge01|vpiEdge0x) # define vpiNegedge (vpiEdgex0|vpiEdge10|vpiEdge1x) # define vpiAnyEdge (vpiPosedge|vpiNegedge) #define vpiConstType 40 # define vpiDecConst 1 # define vpiRealConst 2 # define vpiBinaryConst 3 # define vpiOctConst 4 # define vpiHexConst 5 # define vpiStringConst 6 #define vpiFuncType 44 # define vpiIntFunc 1 # define vpiRealFunc 2 # define vpiTimeFunc 3 # define vpiSizedFunc 4 # define vpiSizedSignedFunc 5 #define vpiSysFuncType vpiFuncType # define vpiSysFuncInt vpiIntFunc # define vpiSysFuncReal vpiRealFunc # define vpiSysFuncTime vpiTimeFunc # define vpiSysFuncSized vpiSizedFunc #define vpiUserDefn 45 #define vpiAutomatic 50 #define vpiConstantSelect 53 #define vpiSigned 65 #define vpiLocalParam 70 /* IVL private properties, also see vvp/vpi_priv.h for other properties */ #define _vpiNexusId 0x1000000 /* used in vvp/vpi_priv.h 0x1000001 */ #define _vpiDelaySelection 0x1000002 # define _vpiDelaySelMinimum 1 # define _vpiDelaySelTypical 2 # define _vpiDelaySelMaximum 3 /* used in vvp/vpi_priv.h 0x1000003 */ /* used in vvp/vpi_priv.h 0x1000004 */ /* DELAY MODES */ #define vpiNoDelay 1 #define vpiInertialDelay 2 #define vpiTransportDelay 3 #define vpiPureTransportDelay 4 #define vpiForceFlag 5 #define vpiReleaseFlag 6 #define vpiReturnEvent 0x1000 /* VPI FUNCTIONS */ extern vpiHandle vpi_register_systf(const struct t_vpi_systf_data*ss); extern void vpi_get_systf_info(vpiHandle obj, p_vpi_systf_data data); /* I/O routines */ extern PLI_UINT32 vpi_mcd_open(char *name); extern PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd); extern char *vpi_mcd_name(PLI_UINT32 mcd); extern PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, const char*fmt, ...) #ifdef __MINGW32__ __attribute__((format (gnu_printf,2,3))); #else __attribute__((format (printf,2,3))); #endif extern PLI_INT32 vpi_printf(const char*fmt, ...) #ifdef __MINGW32__ __attribute__((format (gnu_printf,1,2))); #else __attribute__((format (printf,1,2))); #endif extern PLI_INT32 vpi_vprintf(const char*fmt, va_list ap); extern PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, const char*fmt, va_list ap); extern PLI_INT32 vpi_flush(void); extern PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd); /* proposed extensions */ /* * These functions are proposed extensions to Verilog, and * are described by the Verilog PLI task force as issue#347. * * The vpi_fopen() function is exactly the same as the $fopen system * function. That is, it takes a path string and a mode string, and * opens the file. The result is a 32bit value with bit 31 set, the * remaining bits made up a small integer to represent the file. * * The vpi_get_file(fd) function takes as input a descriptor as * returned by vpi_fopen or $fopen. Bit 31 must be set. The result * is the C FILE* that represents the file. */ extern PLI_INT32 vpi_fopen(const char*name, const char*mode); extern FILE *vpi_get_file(PLI_INT32 fd); /* * support for VPI callback functions. */ typedef struct t_cb_data { PLI_INT32 reason; PLI_INT32 (*cb_rtn)(struct t_cb_data*cb); vpiHandle obj; p_vpi_time time; p_vpi_value value; PLI_INT32 index; char *user_data; } s_cb_data, *p_cb_data; #define cbValueChange 1 #define cbStmt 2 #define cbForce 3 #define cbRelease 4 #define cbAtStartOfSimTime 5 #define cbReadWriteSynch 6 #define cbReadOnlySynch 7 #define cbNextSimTime 8 #define cbAfterDelay 9 #define cbEndOfCompile 10 #define cbStartOfSimulation 11 #define cbEndOfSimulation 12 #define cbError 13 #define cbTchkViolation 14 #define cbStartOfSave 15 #define cbEndOfSave 16 #define cbStartOfRestart 17 #define cbEndOfRestart 18 #define cbStartOfReset 19 #define cbEndOfReset 20 #define cbEnterInteractive 21 #define cbExitInteractive 22 #define cbInteractiveScopeChange 23 #define cbUnresolvedSystf 24 extern vpiHandle vpi_register_cb(p_cb_data data); extern PLI_INT32 vpi_remove_cb(vpiHandle ref); /* * This function allows a vpi application to control the simulation * engine. The operation parameter specifies the function to * perform. The remaining parameters (if any) are interpreted by the * operation. The vpi_sim_control definition (now named vpi_control) * was added to P1364-2000 14 July 1999. See PLI Task Force ID: PTF-161 * * vpiFinish - perform the $finish operation, as soon as the user * function returns. This operation takes a single * parameter, a diagnostic exit code. * * vpiStop - * vpiReset - * vpiSetInteractiveScope - */ extern void vpi_control(PLI_INT32 operation, ...); /************* vpi_control() constants (added with 1364-2000) *************/ #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ #define vpiReset 68 /* execute simulator's $reset */ #define vpiSetInteractiveScope 69 /* set simulator's interactive scope */ #define __ivl_legacy_vpiStop 1 #define __ivl_legacy_vpiFinish 2 /* vpi_sim_control is the incorrect name for vpi_control. */ extern void vpi_sim_control(PLI_INT32 operation, ...); extern vpiHandle vpi_handle(PLI_INT32 type, vpiHandle ref); extern vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle ref); extern vpiHandle vpi_scan(vpiHandle iter); extern vpiHandle vpi_handle_by_index(vpiHandle ref, PLI_INT32 idx); extern vpiHandle vpi_handle_by_name(const char*name, vpiHandle scope); extern void vpi_get_time(vpiHandle obj, s_vpi_time*t); extern PLI_INT32 vpi_get(int property, vpiHandle ref); extern char *vpi_get_str(PLI_INT32 property, vpiHandle ref); extern void vpi_get_value(vpiHandle expr, p_vpi_value value); /* * This function puts a value into the object referenced by the * handle. This assumes that the value supports having its value * written to. The time parameter specifies when the assignment is to * take place. This allows you to schedule an assignment to happen in * the future. * * The flags value specifies the delay model to use in assigning the * value. This specifies how the time value is to be used. * * vpiNoDelay -- Set the value immediately. The p_vpi_time parameter * may be NULL, in this case. This is like a blocking assignment * in behavioral code. * * vpiInertialDelay -- Set the value using the transport delay. The * p_vpi_time parameter is required and specifies when the * assignment is to take place. This is like a non-blocking * assignment in behavioral code. */ extern vpiHandle vpi_put_value(vpiHandle obj, p_vpi_value value, p_vpi_time when, PLI_INT32 flags); extern PLI_INT32 vpi_free_object(vpiHandle ref); extern PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p); /* These Routines will enable the modpath vpiHandle to read/write delay values */ extern void vpi_get_delays(vpiHandle expr, p_vpi_delay delays); extern void vpi_put_delays(vpiHandle expr, p_vpi_delay delays); /* * Check to see if two handles point to the same object. */ extern PLI_INT32 vpi_compare_objects(vpiHandle obj1, vpiHandle obj2); /* * These functions support attaching user data to an instance of a * system task or function. These functions only apply to * vpiSysTaskCall or vpiSysFuncCall handles. */ extern PLI_INT32 vpi_put_userdata(vpiHandle obj, void*data); extern void*vpi_get_userdata(vpiHandle obj); /* * Support for handling errors. */ typedef struct t_vpi_error_info { PLI_INT32 state; PLI_INT32 level; char *message; char *product; char *code; char *file; PLI_INT32 line; } s_vpi_error_info, *p_vpi_error_info; /* error_info states */ # define vpiCompile 1 # define vpiPLI 2 # define vpiRun 3 /* error_info levels */ # define vpiNotice 1 # define vpiWarning 2 # define vpiError 3 # define vpiSystem 4 # define vpiInternal 5 extern PLI_INT32 vpi_chk_error(p_vpi_error_info info); /* This is the table of startup routines included in each module. */ extern DLLEXPORT void (*vlog_startup_routines[])(void); /* * ICARUS VERILOG EXTENSIONS * * The vpip_* functions are Icarus Verilog extensions. They are not * standard VPI functions, so use these at your own risk. * * The vpip_format_* functions format values in string format in the * manner of the $display system task. */ /* Format a scalar a la %v. The str points to a 4byte character buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); extern void vpip_set_return_value(int value); extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg); extern void vpip_make_systf_system_defined(vpiHandle ref); /* Perform fwrite to mcd files. This is used to write raw data, which may include nulls. */ extern void vpip_mcd_rawwrite(PLI_UINT32 mcd, const char*buf, size_t count); /* Return driver information for a net bit. The information is returned in the 'counts' array as follows: counts[0] - number of drivers driving '0' onto the net counts[1] - number of drivers driving '1' onto the net counts[2] - number of drivers driving 'X' onto the net counts[3] - set to 1 if the net is forced, 0 otherwise The 'ref' argument should reference a net. The 'idx' argument selects which bit of the net is examined. */ extern void vpip_count_drivers(vpiHandle ref, unsigned idx, unsigned counts[4]); /* * Stopgap fix for br916. We need to reject any attempt to pass a thread * variable to $strobe or $monitor. To do this, we use some private VPI * properties that are normally only used by the VVP thread cleanup code. * Normally the following definitions are provided by vvp/vpi_priv.h, but * for the stopgap fix we need to make them more widely available. */ #define BR916_STOPGAP_FIX #ifdef BR916_STOPGAP_FIX #define _vpiFromThr 0x1000001 # define _vpiNoThr 0 # define _vpiString 1 # define _vpiVThr 2 # define _vpiWord 3 # define _vpi_at_PV 4 # define _vpi_at_A 5 # define _vpi_at_APV 6 #endif EXTERN_C_END #endif /* VPI_USER_H */ iverilog-10_1/vvp/000077500000000000000000000000001265551621300141555ustar00rootroot00000000000000iverilog-10_1/vvp/Makefile.in000066400000000000000000000156411265551621300162310ustar00rootroot00000000000000# # This source code is free software; you can redistribute it # and/or modify it in source code form under the terms of the GNU # Library 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 Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # SHELL = /bin/sh suffix = @install_suffix@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ datarootdir = @datarootdir@ VPATH = $(srcdir) bindir = @bindir@ libdir = @libdir@ mandir = @mandir@ includedir = @includedir@ # For a cross compile these defines will need to be set accordingly. HOSTCC = @CC@ HOSTCFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CC = @CC@ CXX = @CXX@ DLLTOOL = @DLLTOOL@ AR = @AR@ RANLIB = @RANLIB@ INSTALL = @INSTALL@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ YACC = @YACC@ MAN = @MAN@ PS2PDF = @PS2PDF@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @rdynamic@ @LDFLAGS@ LIBS = @LIBS@ @EXTRALIBS@ dllib=@DLLIB@ MDIR1 = -DMODULE_DIR1='"$(libdir)/ivl$(suffix)"' V = vpi_modules.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \ vpi_event.o vpi_iter.o vpi_mcd.o \ vpi_priv.o vpi_scope.o vpi_real.o vpi_signal.o vpi_string.o vpi_tasks.o vpi_time.o \ vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \ concat.o dff.o class_type.o enum_type.o extend.o file_line.o npmos.o part.o \ permaheap.o reduce.o resolv.o \ sfunc.o stop.o \ substitute.o \ symbols.o ufunc.o codes.o vthread.o schedule.o \ statistics.o tables.o udp.o vvp_island.o vvp_net.o vvp_net_sig.o \ vvp_object.o vvp_cobject.o vvp_darray.o event.o logic.o delay.o \ words.o island_tran.o $V all: dep vvp@EXEEXT@ libvpi.a vvp.man check: all ifeq (@WIN32@,yes) ifeq (@install_suffix@,) ./vvp -M../vpi $(srcdir)/examples/hello.vvp | grep 'Hello, World.' else # On Windows if we have a suffix we must run the vvp test with # a suffix since it was built/linked that way. ln vvp.exe vvp$(suffix).exe ./vvp$(suffix) -M../vpi $(srcdir)/examples/hello.vvp | grep 'Hello, World.' rm -f vvp$(suffix).exe endif else ./vvp -M../vpi $(srcdir)/examples/hello.vvp | grep 'Hello, World.' endif clean: rm -f *.o *~ parse.cc parse.h lexor.cc tables.cc rm -rf dep vvp@EXEEXT@ libvpi.a parse.output vvp.man vvp.ps vvp.pdf vvp.exp distclean: clean rm -f Makefile config.log rm -f stamp-config-h config.h cppcheck: $(O:.o=.cc) libvpi.c draw_tt.c cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ -UMODULE_DIR1 -UMODULE_DIR2 -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in cd ..; ./config.status --file=vvp/$@ dep: mkdir dep ifeq (@WIN32@,yes) # Under Windows (mingw) I need to make the vvp.exe in two steps. # The first step makes an vvp.exe that dlltool can use to make an # export and import library, and the last link makes a, vvp.exe # that really exports the things that the import library imports. # # To get this to work correctly we must use the suffixed version of the # executable to build the library. vvp@EXEEXT@ libvpi.a: $O $(srcdir)/vvp.def $(CXX) -o vvp$(suffix)@EXEEXT@ $(LDFLAGS) $O $(dllib) $(LIBS) $(DLLTOOL) --dllname vvp$(suffix)@EXEEXT@ --def $(srcdir)/vvp.def \ --output-lib libvpi.a --output-exp vvp.exp rm -f vvp$(suffix)@EXEEXT@ $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ vvp.exp $(LDFLAGS) $O $(dllib) $(LIBS) else libvpi.a: libvpi.c $(CC) $(CPPFLAGS) $(CFLAGS) -c $< rm -f libvpi.a $(AR) cqv libvpi.a libvpi.o $(RANLIB) libvpi.a vvp@EXEEXT@: $O $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib) endif %.o: %.cc config.h $(CXX) $(CPPFLAGS) -DIVL_SUFFIX='"$(suffix)"' $(MDIR1) $(MDIR2) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d %.o: %.c config.h $(CC) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d tables.cc: $(srcdir)/draw_tt.c $(HOSTCC) $(HOSTCFLAGS) -o draw_tt.exe $(srcdir)/draw_tt.c ./draw_tt.exe > tables.cc rm draw_tt.exe lexor.o: lexor.cc parse.h parse.o: parse.cc tables.o: tables.cc # Build this in two steps to avoid parallel build issues (see pr3462585) parse.cc: $(srcdir)/parse.y $(YACC) --verbose -t -d -o $@ $< parse.h: parse.cc mv parse.cc.h $@ 2>/dev/null || mv parse.hh $@ lexor.cc: $(srcdir)/lexor.lex $(LEX) -s -olexor.cc $(srcdir)/lexor.lex vvp.man: $(srcdir)/vvp.man.in ../version.exe ../version.exe `head -1 $(srcdir)/vvp.man.in`'\n' > $@ tail -n +2 $(srcdir)/vvp.man.in >> $@ vvp.ps: vvp.man $(MAN) -t ./vvp.man > vvp.ps vvp.pdf: vvp.ps $(PS2PDF) vvp.ps vvp.pdf ifeq (@MINGW32@,yes) ifeq ($(MAN),none) INSTALL_DOC = $(mandir)/man1/vvp$(suffix).1 else ifeq ($(PS2PDF),none) INSTALL_DOC = $(mandir)/man1/vvp$(suffix).1 else INSTALL_DOC = $(prefix)/vvp$(suffix).pdf $(mandir)/man1/vvp$(suffix).1 all: vvp.pdf endif endif INSTALL_DOCDIR = $(mandir)/man1 else INSTALL_DOC = $(mandir)/man1/vvp$(suffix).1 INSTALL_DOCDIR = $(mandir)/man1 endif stamp-config-h: $(srcdir)/config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=vvp/config.h config.h: stamp-config-h install: all installdirs $(bindir)/vvp$(suffix)@EXEEXT@ $(libdir)/libvpi$(suffix).a $(INSTALL_DOC) $(bindir)/vvp$(suffix)@EXEEXT@: ./vvp@EXEEXT@ $(INSTALL_PROGRAM) ./vvp@EXEEXT@ "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@" $(libdir)/libvpi$(suffix).a : ./libvpi.a $(INSTALL_DATA) libvpi.a "$(DESTDIR)$(libdir)/libvpi$(suffix).a" $(mandir)/man1/vvp$(suffix).1: vvp.man $(INSTALL_DATA) vvp.man "$(DESTDIR)$(mandir)/man1/vvp$(suffix).1" $(prefix)/vvp$(suffix).pdf: vvp.pdf $(INSTALL_DATA) vvp.pdf "$(DESTDIR)$(prefix)/vvp$(suffix).pdf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(INSTALL_DOCDIR)" uninstall: $(UNINSTALL32) rm -f "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@" rm -f "$(DESTDIR)$(libdir)/libvpi$(suffix).a" rm -f "$(DESTDIR)$(mandir)/man1/vvp$(suffix).1" "$(DESTDIR)$(prefix)/vvp$(suffix).pdf" -include $(patsubst %.o, dep/%.d, $O) iverilog-10_1/vvp/README.txt000066400000000000000000001312241265551621300156560ustar00rootroot00000000000000/* * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * */ VVP SIMULATION ENGINE The VVP simulator takes as input source code not unlike assembly language for a conventional processor. It is intended to be machine generated code emitted by other tools, including the Icarus Verilog compiler, so the syntax, though readable, is not necessarily convenient for humans. GENERAL FORMAT The source file is a collection of statements. Each statement may have a label, an opcode, and operands that depend on the opcode. For some opcodes, the label is optional (or meaningless) and for others it is required. Every statement is terminated by a semicolon. The semicolon is also the start of a comment line, so you can put comment text after the semicolon that terminates a statement. Like so: Label .functor and, 0x5a, x, y ; This is a comment. The semicolon is required, whether the comment is there or not. Statements may span multiple lines, as long as there is no text (other then the first character of a label) in the first column of the continuation line. HEADER SYNTAX Before any other non-commentary code starts, the source may contain some header statements. These are used for passing parameters or global details from the compiler to the vvp run-time. In all cases, the header statement starts with a left-justified keyword. * :module "name" ; This header statement names a vpi module that vvp should load before the rest of the program is compiled. The compiler looks in the standard VPI_MODULE_PATH for files named "name.vpi", and tries to dynamic load them. * :vpi_time_precision [+|-]; This header statement specifies the time precision of a single tick of the simulation clock. This is mostly used for display (and VPI) purposes, because the engine itself does not care about units. The compiler scales time values ahead of time. The value is the size of a simulation tick in seconds, and is expressed as a power of 10. For example, +0 is 1 second, and -9 is 1 nanosecond. If the record is left out, then the precision is taken to be +0. LABELS AND SYMBOLS Labels and symbols consist of the characters: a-z A-Z 0-9 .$_<> Labels and symbols may not start with a digit or a '.', so that they are easily distinguished from keywords and numbers. A Label is a symbol that starts a statement. If a label is present in a statement, it must start in the first text column. This is how the lexical analyzer distinguishes a label from a symbol. If a symbol is present in a statement, it is in the operand. Opcodes of statements must be a keyword. Symbols are references to labels. It is not necessary for a label to be declared before its use in a symbol, but it must be declared eventually. When symbols refer to functors, the symbol represents the vvp_ipoint_t pointer to the output. (Inputs cannot, and need not, be references symbolically.) If the functor is part of a vector, then the symbol is the vvp_ipoint_t for the first functor. The [] operator can then be used to reference a functor other than the first in the vector. There are some special symbols that in certain contexts have special meanings. As inputs to functors, the symbols "C<0>", "C<1>", "C" and "C" represent a constant driver of the given value. NUMBERS: decimal number tokens are limited to 64bits, and are unsigned. Some contexts may constrain the number size further. SCOPE STATEMENTS: The syntax of a scope statement is: ; foo[n] = ; The format of the substitute statement is: , ; , ; , ; , ; , ; In all cases, there are no width limits, so long as the width is fixed. NOTE: The .arith/mult inputs are not necessarily the width of the output. I have not decided how to handle this. These devices support .s and .r suffixes. The .s means the node is a signed vector device, the .r a real valued device. STRUCTURAL COMPARE STATEMENTS: The arithmetic statements handle various arithmetic operators that have wide outputs, but the comparators have single bit output, so they are implemented a bit differently. The syntax, however, is very similar: , ; , ; , ; , ; , ; , ; , ; , ; Whereas the arithmetic statements generate an output the width of , the comparisons produce a single bit vector result. The plain versions do unsigned comparison, but the ".s" versions to signed comparisons. (Equality doesn't need to care about sign.) STRUCTURAL SHIFTER STATEMENTS: Variable shifts in structural context are implemented with .shift statements: : ). The two source vectors are popped from the vec4 stack (and must have the same width) and the result pushed in their place. The truth table for each bit is: 1 1 --> 1 0 0 --> 0 z z --> z x x --> x .... --> x In other words, if the bits are identical, then take that value. Otherwise, the value is x. * %blend/wr This instruction blends real values for the ternary operator. If the values match return that otherwise return 0.0. Two values are popped from the stack, one is pushed back. * %breakpoint This instruction unconditionally breaks the simulator into the interactive debugger. The idea is to stop the simulator here and give the user a chance to display the state of the simulation using debugger commands. This may not work on all platforms. If run-time debugging is compiled out, then this function is a no-op. * %cassign/vec4 * %cassign/vec4/off , Perform a continuous assign of a constant value to the target variable. This is similar to %set, but it uses the cassign port (port-1) of the signal functor instead of the normal assign, so the signal responds differently. See "VARIABLE STATEMENTS" in the README.txt file. The %cassign/vec4/off instruction will check the flags[4] flag, and if it is 1, it will suppress the assignment. This is so that failed index calculations can report the failure by setting the flag. * %cassign/wr Perform a continuous assign of a constant real value to the target variable. See %cassign/v above. The value is popped from the real value stack. * %cast2 Pop a value from the vec4 stack, convert it using Verilog rules to a vector2 (binary) value, and push the result. * %cmp/s * %cmp/u * %cmp/e * %cmp/ne * %cmpi/s , , * %cmpi/u , , * %cmpi/e , , * %cmpi/ne , , These instructions perform a generic comparison of two vectors of equal size. Two values are pulled from the top of the stack, and not replaced. The results are written into flag bits 4,5,6. The expressions (a, * %cmp/wu , [compare signed/unsigned integer words.] * %cmp/z * %cmp/x These instructions are for implementing the casez and casex comparisons. These work similar to the %cmp/u instructions, except only an eq bit is calculated. These comparisons both treat z values in the left or right operand as don't care positions. The %cmp/x instruction will also treat x values in either operand as don't care. Only bit 4 is set by these instructions. * %cmp/str This instruction pops the top two strings from the string stack and compares them. The results of the comparison go into bits 4 and 5: 4: eq (equal) 5: lt (less than) For the purposes of calculating the lt bit, the top string is the right operand and the string underneath is the left operand. This instruction removes two strings from the stack. * %concat/str * %concati/str Pop the top string, and concatenate it to the new top string. Or think of it as passing the tail, then the head, concatenating them, and pushing the result. The stack starts with two strings in the stack, and ends with one string in the stack. The %concati/str form pops only one value from the stack. The right part comes from the immediate value. * %concat/vec4 * %concati/vec4 , , Pop two vec4 vectors, concatenate them, and push the combined result. The top of the vec4 stack is the LSB of the result, and the next in this stack is the MSB bits of the result. The %concati/vec4 form takes an immediate value and appends it (lsb) to the value on the top of the stack. See the %pushi/vec4 instruction for how to describe the immediate value. * %cvt/sr * %cvt/rs Copy a word from r to l, converting it from real to signed integer (sr) or signed integer to real (rs) in the process. The source and destination may be the same word address, leading to a convert in place. Precision may be lost in the conversion. The %cvt/sr gets the real value from the top of the real value stack (and pops the value) and writes it to the indexed register. * %cvt/ur * %cvt/ru Copy a word from r to l, converting it from real to unsigned integer (ur) or signed integer to real (ru) in the process. The source and destination may be the same word address, leading to a convert in place. Precision may be lost in the conversion. * %cvt/rv , * %cvt/rv/s , The %cvt/rv instruction converts a thread vector starting at and with the width to a real word. Push the result onto the real value stack. Precision may be lost in the conversion. The %cvt/rv/s instruction is the same as %cvt/rv, but treats the thread vector as a signed value. * %cvt/vr The %cvt/vr opcode converts a real word from the stack to a vec4 that is wide. Non-integer precision is lost in the conversion, and the real value is popped from the stack. The result is pushed to the vec4 stack. * %deassign , , Deactivate and disconnect a procedural continuous assignment to a variable. The identifies the affected variable. The and are used to determine what part of the signal will be deactivated. For a full deactivation the is 0 and is the entire signal width. * %deassign/wr The same as %deassign above except this is used for real variables. * %debug/thr These opcodes are aids for debugging the vvp engine. The vvp code generator should not generate these, and they should not alter code flow, data contents, etc. * %delay , This opcode pauses the thread, and causes it to be rescheduled for a time in the future. The amount is the number of the ticks in the future to reschedule, and is >= 0. If the %delay is zero, then the thread yields the processor for another thread, but will be resumed in the current time step. The delay amount is given as 2 32bit numbers, so that 64bit times may be represented. * %delayx This is similar to the %delay opcode, except that the parameter selects an index register, which contains the actual delay. This supports run-time calculated delays. * %delete/obj Arrange for the dynamic object at the target label to be deleted. This has no effect on the object or string stack. Note that this is the same as: %null ; %store/obj but that idiom is expected to be common enough that it warrants an optimized shorthand. * %disable This instruction terminates threads that are part of a specific scope. The label identifies the scope in question, and the threads are the threads that are currently within that scope. * %disable/fork This instruction terminates all the detached children for the current thread. There should not be any non-detached children. * %div , , * %div/s , , This instruction arithmetically divides the vector by the vector, and leaves the result in the vector. IF any of the bits in either vector are x or z, the entire result is x. The %div/s instruction is the same as %div, but does signed division. * %div/wr This opcode divides the left operand by the right operand. If the right operand is 0, then the result is NaN. * dup/real * dup/vec4 These opcodes duplicate the value on the top of the stack for the corresponding type. * %evctl * %evctl/c * %evctl/s * %evctl/i These instructions are used to put event and repetition information into the thread event control registers. These values are then used by the %assign/e instructions to do not blocking event control. The is the event to trigger on and the is an index register to read the repetition count from (signed or unsigned). %evctl/i sets the repetition to an immediate unsigned value. %evctl/c clears the event control information. This is needed if a %assign/e may be skipped since the %assign/e statements clear the event control information and the other %evctl statements assert that this information has been cleared. You can get an assert if this information is not managed correctly. * %event This instruction is used to send a pulse to an event object. The is an event variable. This instruction simply writes an arbitrary value to the event to trigger the event. * %file_line This command emits the provided file and line information along with the description when it is executed. The output is sent to stderr and the format of the output is: :: is the unsigned numeric file index. is the unsigned line number. is a string, if string is 0 then the following default message is used: "Procedural tracing.". * %flag_mov , This instruction copies the flag bit from to . * %flag_or , This instruction calculates the Verilog OR of the flag bits in and , and leaves the result in . * %flag_set/imm , This instruction sets an immediate value into a flag bit. This is a single bit, and the value is 0==0, 1==1, 2==z, 3==x. * %flag_get/vec4 * %flag_set/vec4 These instructions provide a means for accessing flag bits. The %flag_get/vec4 loads the numbered flag as a vec4 on top of the vec4 stack, and the %flag_set/vec4 pops the top of the vec4 stack and writes the LSB to the selected flag. * %force/vec4