diff --git a/BUGS.txt b/BUGS.txt index ee8469633..14e39b774 100644 --- a/BUGS.txt +++ b/BUGS.txt @@ -54,7 +54,7 @@ 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 then the latest snapshot. +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 @@ -82,9 +82,9 @@ clarification. 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 then a core dump, but are worthy of +likely to be more subjective than a core dump, but are worthy of consideration. However, realize that outright errors will get more -attention then missed optimizations. +attention than missed optimizations. THE MAKING OF A GOOD TEST PROGRAM diff --git a/COPYING.lesser b/COPYING.lesser new file mode 100644 index 000000000..4362b4915 --- /dev/null +++ b/COPYING.lesser @@ -0,0 +1,502 @@ + 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! diff --git a/Makefile.in b/Makefile.in index 33d264a54..8704bfc8f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -106,7 +106,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ netenum.o net_event.o net_expr.o net_func.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_types.o set_width.o \ + pform_disciplines.o pform_dump.o pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o target.o \ Attrib.o HName.o Module.o PDelays.o PEvent.o PExpr.o PGate.o \ PGenerate.o PScope.o PSpec.o PTask.o PUdp.o PFunction.o PWire.o \ diff --git a/Module.h b/Module.h index 76998f72f..307836edf 100644 --- a/Module.h +++ b/Module.h @@ -79,7 +79,7 @@ class Module : public PScope, public LineInfo { enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; UCDriveType uc_drive; - /* specparams are simpler then other params, in that they have + /* specparams are simpler than other params, in that they have no type information. They are merely constant expressions. */ mapspecparams; diff --git a/PDelays.cc b/PDelays.cc index 40ea8b262..8e9ea79fc 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -78,11 +78,7 @@ unsigned PDelays::delay_count() const static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) { - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - expr->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - NetExpr*dex = expr->elaborate_expr(des, scope, -1, false); - eval_expr(dex); + 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. */ diff --git a/PExpr.cc b/PExpr.cc index cefe23bc7..152e6ebf4 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-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 @@ -31,8 +31,10 @@ PExpr::PExpr() { - expr_width_ = 0; - expr_type_ = IVL_VT_NO_TYPE; + expr_type_ = IVL_VT_NO_TYPE; + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; } PExpr::~PExpr() @@ -69,6 +71,11 @@ NetNet* PExpr::elaborate_bi_net(Design*, NetScope*) const return 0; } +bool PExpr::is_collapsible_net(Design*, NetScope*) const +{ + return false; +} + PEBinary::PEBinary(char op, PExpr*l, PExpr*r) : op_(op), left_(l), right_(r) { @@ -94,8 +101,8 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const PEBComp::PEBComp(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { - left_width_ = 0; - right_width_ = 0; + l_width_ = 0; + r_width_ = 0; } PEBComp::~PEBComp() @@ -204,13 +211,16 @@ bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const } PEConcat::PEConcat(const list&p, PExpr*r) -: parms_(p.size()), tested_widths_(p.size()), repeat_(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() @@ -437,3 +447,11 @@ bool PEUnary::has_aa_term(Design*des, NetScope*scope) const assert(expr_); return expr_->has_aa_term(des, scope); } + +PEVoid::PEVoid() +{ +} + +PEVoid::~PEVoid() +{ +} diff --git a/PExpr.h b/PExpr.h index 15adfc7f4..337305e6b 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef __PExpr_H #define __PExpr_H /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-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 @@ -43,6 +43,8 @@ class NetScope; class PExpr : public LineInfo { public: + enum width_mode_t { SIZED, EXPAND, LOSSLESS, UNSIZED }; + PExpr(); virtual ~PExpr(); @@ -62,53 +64,65 @@ class PExpr : public LineInfo { // references to automatically allocated variables. virtual bool has_aa_term(Design*des, NetScope*scope) const; - // This method tests the width that the expression wants to - // be. It is used by elaboration of assignments to figure out - // the width of the expression. + // 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. // - // The "min" is the width of the local context, so is the - // minimum width that this function should return. Initially - // this is the same as the lval width. + // Normally mode should be initialised 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 contains an unsized literal, 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. + // If the expression both contains an unsized literal and contains + // an operation that coerces a vector operand to a different type + // (signed <-> unsigned), mode is changed to UNSIZED. UNSIZED 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 literals 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. // - // The "lval" is the width of the destination where this - // result is going to go. This can be used to constrain the - // amount that an expression can reasonably expand. For - // example, there is no point expanding an addition to beyond - // the lval. This extra bit of information allows the - // expression to optimize itself a bit. If the lval==0, then - // the subexpression should not make l-value related - // optimizations. + // If mode is initialised 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 UNSIZED as described + // above. This supports a non-standard mode of expression width + // calculation. // - // The expr_type is an output argument that gives the - // calculated type for the expression. - // - // The unsized_flag is set to true if the expression is - // unsized and therefore expandable. This happens if a - // sub-expression is an unsized literal. Some expressions make - // special use of that. + // When the final value of mode is UNSIZED, 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, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + 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; } // Procedural elaboration of the expression. The expr_width is - // the width of the context of the expression (i.e. the - // l-value width of an assignment), - // - // ... or -1 if the expression is self-determined. or - // ... or -2 if the expression is losslessly - // self-determined. This can happen in situations where the - // result is going to a pseudo-infinitely wide context. + // 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, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. @@ -134,15 +148,25 @@ class PExpr : public LineInfo { // 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&); @@ -165,25 +189,27 @@ class PEConcat : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + 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*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, 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::valarraytested_widths_; + std::valarraywidth_modes_; PExpr*repeat_; + NetScope*tested_scope_; + unsigned repeat_count_; }; /* @@ -232,11 +258,10 @@ class PEFNumber : public PExpr { virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual void dump(ostream&) const; @@ -262,9 +287,7 @@ class PEIdent : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); // Identifiers are allowed (with restrictions) is assign l-values. virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; @@ -277,7 +300,8 @@ class PEIdent : public PExpr { bool is_force) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. @@ -285,6 +309,8 @@ class PEIdent : public PExpr { 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: @@ -326,13 +352,14 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb, - int expr_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + unsigned expr_wid) const; NetExpr*elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, @@ -349,28 +376,31 @@ class PEIdent : public PExpr { NetScope*scope, NetNet*net, NetScope*found, + unsigned expr_wid, bool sys_task_arg) const; NetExpr*elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found, + unsigned expr_wid, bool sys_task_arg) const; NetExpr*elaborate_expr_net_part_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found, + unsigned expr_wid) const; NetExpr*elaborate_expr_net_idx_up_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; NetExpr*elaborate_expr_net_idx_do_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; NetExpr*elaborate_expr_net_bit_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, @@ -390,12 +420,10 @@ class PENumber : public PExpr { virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - int expr_width, bool) const; + unsigned expr_wid, bool) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -425,12 +453,10 @@ class PEString : public PExpr { virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - int expr_width, bool) const; + unsigned expr_wid, bool) const; verinum* eval_const(Design*, NetScope*) const; private: @@ -450,16 +476,15 @@ class PEUnary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: - NetExpr* elaborate_expr_bits_(NetExpr*operand, int expr_wid) const; + NetExpr* elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const; private: char op_; @@ -479,12 +504,11 @@ class PEBinary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; protected: @@ -493,22 +517,18 @@ class PEBinary : public PExpr { PExpr*right_; NetExpr*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_bits_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_div_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; - NetExpr*elaborate_expr_base_lshift_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; - NetExpr*elaborate_expr_base_rshift_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_add_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; }; @@ -523,16 +543,14 @@ class PEBComp : public PEBinary { ~PEBComp(); virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, bool sys_task_arg) const; private: - int left_width_; - int right_width_; + unsigned l_width_; + unsigned r_width_; }; /* @@ -545,12 +563,10 @@ class PEBLogic : public PEBinary { ~PEBLogic(); virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, bool sys_task_arg) const; }; /* @@ -565,17 +581,15 @@ class PEBLeftWidth : public PEBinary { ~PEBLeftWidth() =0; virtual NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const =0; + unsigned expr_wid) const =0; protected: virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; - + unsigned expr_wid, + bool sys_task_arg) const; }; class PEBPower : public PEBLeftWidth { @@ -585,7 +599,7 @@ class PEBPower : public PEBLeftWidth { ~PEBPower(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const; + unsigned expr_wid) const; }; class PEBShift : public PEBLeftWidth { @@ -595,7 +609,7 @@ class PEBShift : public PEBLeftWidth { ~PEBShift(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const; + unsigned expr_wid) const; }; /* @@ -615,17 +629,16 @@ class PETernary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, int use_wid) const; + PExpr*expr, unsigned expr_wid) const; private: PExpr*expr_; @@ -658,12 +671,11 @@ class PECallFunction : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); private: pform_name_t path_; @@ -671,14 +683,29 @@ class PECallFunction : public PExpr { bool check_call_matches_definition_(Design*des, NetScope*dscope) const; - NetExpr* cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const; + NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; - NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const; - NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t) const; + NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, + unsigned expr_wid) const; + NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t, + unsigned expr_wid) const; unsigned test_width_sfunc_(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); +}; + +/* + * 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, + bool sys_task_arg) const; }; #endif diff --git a/PUdp.h b/PUdp.h index 77226286d..848739875 100644 --- a/PUdp.h +++ b/PUdp.h @@ -29,7 +29,7 @@ class PExpr; /* * This class represents a parsed UDP. This is a much simpler object - * then a module or macromodule. + * than a module or macromodule. * * - all ports are scalar, * - pin 0 (the first port) is always output, diff --git a/README.txt b/README.txt index da18bb553..38f5b66fd 100644 --- a/README.txt +++ b/README.txt @@ -237,7 +237,7 @@ 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 then those +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 diff --git a/design_dump.cc b/design_dump.cc index 8d02de40d..afdfa5031 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1396,12 +1396,8 @@ void NetEBinary::dump(ostream&o) const void NetEConcat::dump(ostream&o) const { - if (repeat_calculated_) { - if (repeat_value_ != 1) - o << repeat_value_; - } else if (repeat_) { - o << "<" << *repeat_ << ">"; - } + if (repeat_ != 1) + o << repeat_; if (parms_[0]) o << "{" << *parms_[0]; diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index 5ec8bf596..3bd8482cf 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,4 +1,4 @@ -.TH iverilog 1 "January 8th, 2011" "" "Version %M.%m.%n %E" +.TH iverilog 1 "February 27th, 2011" "" "Version %M.%m.%n %E" .SH NAME iverilog - Icarus Verilog compiler @@ -258,7 +258,9 @@ checking the syntax of the Verilog source. .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. +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, diff --git a/dup_expr.cc b/dup_expr.cc index ca65810e8..d541161e9 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -32,19 +32,103 @@ NetEAccess* NetEAccess::dup_expr() const return tmp; } -NetEBComp* NetEBComp::dup_expr() const +NetEBinary* NetEBinary::dup_expr() const { - NetEBComp*tmp = new NetEBComp(op_, left_->dup_expr(), - right_->dup_expr()); - assert(tmp); + 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_.count(), repeat_); + ivl_assert(*this, dup); + dup->set_line(*this); + for (unsigned idx = 0 ; idx < parms_.count() ; 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_); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -52,7 +136,7 @@ NetEConst* NetEConst::dup_expr() const NetEConstEnum* NetEConstEnum::dup_expr() const { NetEConstEnum*tmp = new NetEConstEnum(scope_, name_, enum_set_, value()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -60,7 +144,15 @@ NetEConstEnum* NetEConstEnum::dup_expr() const NetEConstParam* NetEConstParam::dup_expr() const { NetEConstParam*tmp = new NetEConstParam(scope_, name_, value()); - assert(tmp); + 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; } @@ -68,26 +160,26 @@ NetEConstParam* NetEConstParam::dup_expr() const NetECRealParam* NetECRealParam::dup_expr() const { NetECRealParam*tmp = new NetECRealParam(scope_, name_, value()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEEvent* NetEEvent::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } NetENetenum* NetENetenum::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } NetEScope* NetEScope::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } @@ -95,8 +187,8 @@ NetESelect* NetESelect::dup_expr() const { NetESelect*tmp = new NetESelect(expr_->dup_expr(), base_? base_->dup_expr() : 0, - expr_width()); - assert(tmp); + expr_width(), sel_type_); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -104,11 +196,11 @@ NetESelect* NetESelect::dup_expr() const NetESFunc* NetESFunc::dup_expr() const { NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms()); - assert(tmp); + ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { - assert(parm(idx)); + ivl_assert(*this, parm(idx)); tmp->parm(idx, parm(idx)->dup_expr()); } @@ -119,7 +211,7 @@ NetESFunc* NetESFunc::dup_expr() const NetESignal* NetESignal::dup_expr() const { NetESignal*tmp = new NetESignal(net_, word_); - assert(tmp); + ivl_assert(*this, tmp); tmp->expr_width(expr_width()); tmp->set_line(*this); return tmp; @@ -129,8 +221,10 @@ NetETernary* NetETernary::dup_expr() const { NetETernary*tmp = new NetETernary(cond_->dup_expr(), true_val_->dup_expr(), - false_val_->dup_expr()); - assert(tmp); + false_val_->dup_expr(), + expr_width(), + has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -141,29 +235,29 @@ NetEUFunc* NetEUFunc::dup_expr() const svector tmp_parms (parms_.count()); for (unsigned idx = 0 ; idx < tmp_parms.count() ; idx += 1) { - assert(parms_[idx]); + ivl_assert(*this, parms_[idx]); tmp_parms[idx] = parms_[idx]->dup_expr(); } tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUBits* NetEUBits::dup_expr() const { - NetEUBits*tmp = new NetEUBits(op_, expr_->dup_expr()); - assert(tmp); + 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()); - assert(tmp); + NetEUnary*tmp = new NetEUnary(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -171,15 +265,15 @@ NetEUnary* NetEUnary::dup_expr() const NetEUReduce* NetEUReduce::dup_expr() const { NetEUReduce*tmp = new NetEUReduce(op_, expr_->dup_expr()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetECast* NetECast::dup_expr() const { - NetECast*tmp = new NetECast(op_, expr_->dup_expr()); - assert(tmp); + NetECast*tmp = new NetECast(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } diff --git a/elab_expr.cc b/elab_expr.cc index b47b38278..4fe9dadf9 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -81,163 +81,141 @@ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) } NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, - ivl_variable_type_t data_type_lv, int expr_wid_lv, + ivl_variable_type_t lv_type, unsigned lv_width, PExpr*expr) { - bool unsized_flag = type_is_vectorable(data_type_lv)? false : true; - unsigned use_lval_wid = type_is_vectorable(data_type_lv)? expr_wid_lv : 0; - unsigned use_min_wid = expr_wid_lv; - - /* Find out what the r-value width is going to be. We - guess it will be the l-value width, but it may turn - out to be something else based on self-determined - widths inside. */ - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = expr->test_width(des, scope, use_min_wid, use_lval_wid, rval_type, unsized_flag); - - if (debug_elaborate) { - cerr << expr->get_fileline() << ": debug: r-value tested " - << "type=" << rval_type - << ", width=" << expr_wid - << ", min=" << use_min_wid - << ", unsized_flag=" << (unsized_flag?"true":"false") << endl; - } - - switch (data_type_lv) { + int context_wid = -1; + switch (lv_type) { case IVL_VT_REAL: case IVL_VT_STRING: - unsized_flag = true; - expr_wid = -2; - expr_wid_lv = -1; 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); - expr_wid = -2; - expr_wid_lv = -1; break; } - NetExpr*result = elab_and_eval(des, scope, expr, expr_wid, expr_wid_lv); - return result; + return elab_and_eval(des, scope, expr, context_wid); } /* - * The default behavior for the test_width method is to just return the - * minimum width that is passed in. + * If the mode is UNSIZED, make sure the final expression width is at + * least integer_width, but return the calculated lossless width to + * the caller. */ -unsigned PExpr::test_width(Design*, NetScope*, - unsigned min, unsigned, - ivl_variable_type_t&, bool&) +unsigned PExpr::fix_width_(width_mode_t mode) { - if (debug_elaborate) { - cerr << get_fileline() << ": debug: test_width defaults to " - << min << ", ignoring unsized_flag. typeid=" - << typeid(*this).name() << endl; - } - return min; + unsigned width = expr_width_; + if ((mode == UNSIZED) && type_is_vectorable(expr_type_) + && (width < integer_width)) + expr_width_ = integer_width; + + return width; } -NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, int, bool) const +unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) { - cerr << get_fileline() << ": internal error: I do not know how to elaborate" - << " expression. " << endl; + 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*, unsigned, bool) 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; } -unsigned PEBinary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + +unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - ivl_variable_type_t expr_type_left = IVL_VT_NO_TYPE; - ivl_variable_type_t expr_type_right= IVL_VT_NO_TYPE; + ivl_assert(*this, left_); + ivl_assert(*this, right_); - bool flag = unsized_flag; + unsigned l_width = left_->test_width(des, scope, mode); - bool flag_left = flag; - unsigned wid_left = left_->test_width(des,scope, min, 0, expr_type_left, flag_left); + width_mode_t saved_mode = mode; - bool flag_right = flag; - unsigned wid_right = right_->test_width(des,scope, min, 0, expr_type_right, flag_right); + unsigned r_width = right_->test_width(des, scope, mode); - if (flag_right && !flag) { - unsized_flag = true; - wid_left = left_->test_width(des, scope, min, 0, expr_type_left, unsized_flag); - } + // If the width mode changed, retest the left operand, as it + // may choose a different width if it is in an unsized context. + if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) + l_width = left_->test_width(des, scope, mode); - if (flag_left && !flag) { - unsized_flag = true; - wid_right = right_->test_width(des, scope, min, 0, expr_type_right, unsized_flag); - } + ivl_variable_type_t l_type = left_->expr_type(); + ivl_variable_type_t r_type = right_->expr_type(); - if (expr_type_left == IVL_VT_REAL || expr_type_right == IVL_VT_REAL) + if (l_type == IVL_VT_REAL || r_type == IVL_VT_REAL) expr_type_ = IVL_VT_REAL; - else if (expr_type_left==IVL_VT_LOGIC || expr_type_right==IVL_VT_LOGIC) + else if (l_type == IVL_VT_LOGIC || r_type == IVL_VT_LOGIC) expr_type_ = IVL_VT_LOGIC; else expr_type_ = IVL_VT_BOOL; - switch (op_) { - case '+': - case '-': - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - if (unsized_flag && type_is_vectorable(expr_type_)) - min += 1; - break; + 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(); - case '*': - if (unsized_flag && type_is_vectorable(expr_type_)) { - unsigned use_wid = wid_left + wid_right; - if (use_wid > integer_width) - use_wid = integer_width; - if (use_wid > min) - min = use_wid; - } - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - break; + // 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 = UNSIZED; - case 'l': // << 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: - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - break; + switch (op_) { + case '+': + case '-': + if (mode != SIZED) + expr_width_ += 1; + break; + + case '*': + if (mode != SIZED) + expr_width_ = l_width + r_width; + break; + + case '%': + case '/': + min_width_ = max(min_width_, expr_width_); + 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; + } } - if (type_is_vectorable(expr_type_)) { - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - expr_width_ = min; - } else - expr_width_ = 1; - - expr_type__ = expr_type_; - return expr_width_; + return fix_width_(mode); } /* @@ -246,104 +224,66 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, * types. */ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The context passes in the width that this expression is - // expected to use. But if that width is <0, we are in a - // self-determined context and we must use the width that was - // calculated by a previous call to test_with. - int use_wid = expr_wid; - if (use_wid < 0 && expr_type_ != IVL_VT_REAL) { - ivl_assert(*this, expr_width_ > 0); - use_wid = expr_width_; + // 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, use_wid, false); - NetExpr*rp = right_->elaborate_expr(des, scope, use_wid, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } - // Handle the special case that one of the operands is a real - // value and the other is a vector type. In that case, - // re-elaborate the vectorable argument as self-determined - // lossless. - if (lp->expr_type()==IVL_VT_REAL - && type_is_vectorable(rp->expr_type()) - && expr_wid != -2) { - delete rp; - rp = right_->elaborate_expr(des, scope, -2, false); - } - - if (rp->expr_type()==IVL_VT_REAL - && type_is_vectorable(lp->expr_type()) - && expr_wid != -2) { - delete lp; - lp = left_->elaborate_expr(des, scope, -2, false); - } - - NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid); - return tmp; -} - -void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp) -{ - // If an argument is a non-vector type, then this type - // suppression does not apply. - if (! type_is_vectorable(lp->expr_type())) - return; - if (! type_is_vectorable(rp->expr_type())) - return; - - // If either operand is unsigned, then treat the whole - // expression as unsigned. This test needs to be done here - // instead of in *_expr_base_ because it needs to be done - // ahead of any subexpression evaluation (because they need to - // know their signedness to evaluate) and because there are - // exceptions to this rule. - if (! lp->has_sign()) - rp->cast_signed(false); - if (! rp->has_sign()) - lp->cast_signed(false); -} - -NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des, - NetExpr*lp, - NetExpr*rp, - int expr_wid) const -{ - /* If either expression can be evaluated ahead of time, then - do so. This can prove helpful later. */ - eval_expr(lp, expr_wid); - eval_expr(rp, expr_wid); + // If either expression can be evaluated ahead of time, then + // do so. This can prove helpful later. + eval_expr(lp, l_width); + eval_expr(rp, r_width); return elaborate_expr_base_(des, lp, rp, expr_wid); } /* - * This is common elaboration of the operator. It presumes that the + * 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, - int expr_wid) const + unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " - << *this << " expr_wid=" << expr_wid << endl; + << *this << " expr_width=" << expr_wid << endl; } NetExpr*tmp; switch (op_) { default: - tmp = new NetEBinary(op_, lp, rp); + tmp = new NetEBinary(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; @@ -357,9 +297,12 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, return 0; case 'p': - tmp = new NetEBPow(op_, lp, rp); - tmp->set_line(*this); - break; + 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); @@ -391,7 +334,8 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case '+': case '-': - tmp = elaborate_expr_base_add_(des, lp, rp, expr_wid); + tmp = new NetEBAdd(op_, lp, rp, expr_wid, signed_flag_); + tmp->set_line(*this); break; case 'E': /* === */ @@ -411,7 +355,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case 'm': // min(l,r) case 'M': // max(l,r) - tmp = new NetEBMinMax(op_, lp, rp); + tmp = new NetEBMinMax(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; } @@ -421,7 +365,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -431,20 +375,7 @@ NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, return 0; } - // If either of the arguments is unsigned, then process both - // of them as unsigned. This only impacts the padding that is - // done to get the operands to the expr_wid. - if (! lp->has_sign()) rp->cast_signed(false); - if (! rp->has_sign()) lp->cast_signed(false); - - if (expr_wid > 0) { - if (type_is_vectorable(lp->expr_type())) - lp = pad_to_width(lp, expr_wid, *this); - if (type_is_vectorable(rp->expr_type())) - rp = pad_to_width(rp, expr_wid, *this); - } - - NetEBBits*tmp = new NetEBBits(op_, lp, rp); + NetEBBits*tmp = new NetEBBits(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; @@ -452,7 +383,7 @@ NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { /* The % operator does not support real arguments in baseline Verilog. But we allow it in our extended @@ -466,296 +397,16 @@ NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, } } - // If either of the arguments is unsigned, then process both - // of them as unsigned. This only impacts the padding that is - // done to get the operands to the expr_wid. - if (! lp->has_sign()) rp->cast_signed(false); - if (! rp->has_sign()) lp->cast_signed(false); - - /* The original elaboration of the left and right expressions - already tried to elaborate to the expr_wid. If the - expressions are not that width by now, then they need to be - padded. The divide expression operands must be the width - of the output. */ - if (expr_wid > 0) { - lp = pad_to_width(lp, expr_wid, *this); - rp = pad_to_width(rp, expr_wid, *this); - } - - NetEBDiv*tmp = new NetEBDiv(op_, lp, rp); + NetEBDiv*tmp = new NetEBDiv(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } -NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, - NetExpr*lp, NetExpr*rp, - int 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; - } - - NetExpr*tmp; - - long use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (use_wid == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Oops, left expression width is not known, " - << "so expression width is not known. Punt." << endl; - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return 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. No matter - // what the shift, the result is going to be zero. - if (lpc->value().is_defined() && lpc->value().is_zero()) { - - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Shift of zero always returns zero." - << " Elaborate as constant zero." << endl; - - tmp = make_const_0(use_wid); - tmp->set_line(*this); - return tmp; - } - - if (NetEConst*rpc = dynamic_cast (rp)) { - // Handle the super-special case that both - // operands are constants. Precalculate the - // entire value here. - verinum lpval = lpc->value(); - unsigned shift = rpc->value().as_ulong(); - verinum result = lpc->value() << shift; - // If the l-value has explicit size, or - // there is a context determined size, use that. - if (lpval.has_len() || expr_wid > 0) { - int use_len = lpval.len(); - if (expr_wid > 0 && expr_wid > use_len) - use_len = expr_wid; - result = verinum(result, use_len); - } - - tmp = new NetEConst(result); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Precalculate " << *lpc << " << " << shift - << " to constant " << *tmp - << " (expr_wid=" << expr_wid << ")" << endl; - - } else { - // Handle the special case that the left - // operand is constant. If it is unsized, we - // may have to expand it to an integer width. - verinum lpval = lpc->value(); - if (lpval.len() < integer_width && !lpval.has_len()) { - lpval = verinum(lpval, integer_width); - lpc = new NetEConst(lpval); - lpc->set_line(*lp); - } - - tmp = new NetEBShift(op_, lpc, rp); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Adjust " << *this - << " to this " << *tmp - << " to allow for integer widths." << endl; - } - - } else if (NetEConst*rpc = dynamic_cast (rp)) { - long shift = rpc->value().as_long(); - use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (shift >= use_wid || (-shift) >= (long)lp->expr_width()) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Value left-shifted " << shift - << " beyond width of " << use_wid - << ". Elaborate as constant zero." << endl; - - tmp = make_const_0(use_wid); - - } else { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Left shift expression by constant " - << shift << " bits. (use_wid=" << use_wid << ")" << endl; - lp = pad_to_width(lp, use_wid, *this); - tmp = new NetEBShift(op_, lp, rp); - } - - } else { - // Left side is not constant, so handle it the - // default way. - if (expr_wid >= 0) - lp = pad_to_width(lp, expr_wid, *this); - tmp = new NetEBShift(op_, lp, rp); - } - - tmp->set_line(*this); - return tmp; -} - -NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, - NetExpr*lp, NetExpr*rp, - int 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; - } - - NetExpr*tmp; - - long use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (use_wid == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Oops, left expression width is not known, " - << "so expression width is not known. Punt." << endl; - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return tmp; - } - - if (NetEConst*lpc = dynamic_cast (lp)) { - - // Special case: The left expression is zero. No matter - // what the shift, the result is going to be zero. - if (lpc->value().is_defined() && lpc->value().is_zero()) { - - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Shift of zero always returns zero." - << " Elaborate as constant zero." << endl; - - tmp = make_const_0(use_wid); - tmp->set_line(*this); - return tmp; - } - } - - if (NetEConst*rpc = dynamic_cast (rp)) { - unsigned long shift = rpc->value().as_ulong(); - - // Special case: The shift is the size of the entire - // left operand, and the shift is unsigned. Elaborate as - // a constant-0. - if ((op_=='r' || (lp->has_sign()==false)) && - shift >= lp->expr_width()) { - - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Value right-shifted " << shift - << " beyond width of " << lp->expr_width() - << ". Elaborate as constant zero." << endl; - - tmp = make_const_0(use_wid); - tmp->set_line(*this); - return tmp; - - } - - // Special case: the shift is the size of the entire - // left operand, and the shift is signed. Elaborate as a - // replication of the top bit of the left expression. - // - // The above test assures us that op_ == 'R' && the left - // argument is signed when the shift is greater than the - // expression width. - if (shift >= lp->expr_width()) { - - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Value signed-right-shifted " << shift - << " beyond width of " << lp->expr_width() - << ". Elaborate as replicated top bit." << endl; - - ivl_assert(*this, lp->expr_width() > 0); - ivl_assert(*this, use_wid > 0); - - tmp = new NetEConst(verinum(lp->expr_width()-1)); - tmp->set_line(*this); - tmp = new NetESelect(lp, tmp, 1); - tmp->cast_signed(true); - tmp = pad_to_width(tmp, use_wid, *this); - return tmp; - } - - // If this is lossless, then pad the left expression - // enough to cover the right shift. - if (expr_wid == -2 && use_wid+shift > lp->expr_width()) { - lp->cast_signed(lp->has_sign() && op_=='R'); - lp = pad_to_width(lp, use_wid + shift, *this); - } - - tmp = new NetEConst(verinum(shift)); - tmp->set_line(*this); - long tmp_wid = lp->expr_width() - shift; - if (tmp_wid > use_wid) - tmp_wid = use_wid; - - ivl_assert(*this, tmp_wid > 0); - ivl_assert(*this, use_wid > 0); - - // Implement the right-shift by part-selecting the low - // bits out. Pad the result of the part select back out - // to the desired size. - tmp = new NetESelect(lp, tmp, tmp_wid); - tmp->set_line(*this); - tmp->cast_signed(lp->has_sign() && op_=='R'); - tmp = pad_to_width(tmp, use_wid, *this); - return tmp; - } - - // Fallback, handle the general case. - if (expr_wid > 0) - lp = pad_to_width(lp, expr_wid, *this); - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return tmp; -} - NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { - // First, Make sure that signed arguments are padded to the - // width of the output. This is necessary for 2s complement - // multiplication to come out right. - if (expr_wid > 0) { - if (lp->has_sign() && lp->expr_type() != IVL_VT_REAL) - lp = pad_to_width(lp, expr_wid, *this); - if (rp->has_sign() && rp->expr_type() != IVL_VT_REAL) - rp = pad_to_width(rp, expr_wid, *this); - } - // Keep constants on the right side. if (dynamic_cast(lp)) { NetExpr*tmp = lp; @@ -767,154 +418,115 @@ NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, if (NetEConst*rp_const = dynamic_cast (rp)) { verinum rp_val = rp_const->value(); - int use_wid = expr_wid; - if (use_wid < 0) - use_wid = max(rp->expr_width(), lp->expr_width()); - if (! rp_val.is_defined()) { - NetEConst*tmp = make_const_x(use_wid); + NetEConst*tmp = make_const_x(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; } if (rp_val.is_zero()) { - NetEConst*tmp = make_const_0(use_wid); + NetEConst*tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; } } - // If this expression is unsigned, then make sure the - // arguments are unsigned so that the padding below doesn't - // cause any sign extension to happen. - suppress_binary_operand_sign_if_needed(lp, rp); - - // Multiply will guess a width that is the sum of the - // widths of the operand. If that sum is too small, then - // pad one of the arguments enough that the sum is the - // desired width. - if (expr_wid > (long)(lp->expr_width() + rp->expr_width())) - lp = pad_to_width(lp, expr_wid - rp->expr_width(), *this); - - NetEBMult*tmp = new NetEBMult(op_, lp, rp); + NetEBMult*tmp = new NetEBMult(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); - if (expr_wid > 0 && type_is_vectorable(tmp->expr_type())) - tmp->set_width(expr_wid, false); - return tmp; } -NetExpr* PEBinary::elaborate_expr_base_add_(Design*, - NetExpr*lp, NetExpr*rp, - int expr_wid) const +unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) { - NetExpr*tmp; - bool use_lossless_flag = expr_wid == -2; + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // If this expression is not vectorable, then do NOT pass the - // lossless flag to the NetEBAdd constructor. For non- - // vectorable, lossless is implicit. - if (! type_is_vectorable(lp->expr_type())) - use_lossless_flag = false; - if (! type_is_vectorable(rp->expr_type())) - use_lossless_flag = false; + // 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; - // If the expression is unsigned, then force the operands to - // unsigned so that the set_width below doesn't cause them to - // be sign-extended. - suppress_binary_operand_sign_if_needed(lp, rp); + // The widths of the operands are semi-self-determined. They + // affect each other, but not the result. + width_mode_t mode = SIZED; - tmp = new NetEBAdd(op_, lp, rp, use_lossless_flag); - if (expr_wid > 0 && type_is_vectorable(tmp->expr_type())) - tmp->set_width(expr_wid); + unsigned l_width = left_->test_width(des, scope, mode); - tmp->set_line(*this); - return tmp; -} + width_mode_t saved_mode = mode; -unsigned PEBComp::test_width(Design*des, NetScope*scope, unsigned, unsigned, - ivl_variable_type_t&my_expr_type, - bool&) -{ - // The width and type of a comparison operator is fixed and - // well known. Set them now. - expr_type_ = IVL_VT_LOGIC; - my_expr_type = expr_type_; - expr_width_ = 1; + unsigned r_width = right_->test_width(des, scope, mode); - // The widths of operands are self-determined, but need to be - // figured out. - bool unsized_flag = false; - ivl_variable_type_t left_type = IVL_VT_NO_TYPE; - unsigned left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag); - bool left_unsized_flag = unsized_flag; - ivl_variable_type_t right_type = IVL_VT_NO_TYPE; - unsigned right_width = right_->test_width(des, scope, 0, 0, right_type, unsized_flag); + // If the width mode changed, retest the left operand, as it + // may choose a different width if it is in an unsized context. + if ((mode != SIZED) && (saved_mode == SIZED)) + l_width = left_->test_width(des, scope, mode); - if (left_unsized_flag != unsized_flag) { - left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag); - } + ivl_variable_type_t l_type = left_->expr_type(); + ivl_variable_type_t r_type = right_->expr_type(); - int try_wid_l = left_width; - if (type_is_vectorable(left_type) && (right_width > left_width)) - try_wid_l = right_width; + l_width_ = l_width; + if (type_is_vectorable(l_type) && (r_width > l_width)) + l_width_ = r_width; - int try_wid_r = right_width; - if (type_is_vectorable(right_type) && (left_width > right_width)) - try_wid_r = left_width; + r_width_ = r_width; + if (type_is_vectorable(r_type) && (l_width > r_width)) + r_width_ = l_width; - // If the expression is unsized and smaller then the integer + // If the expression is unsized 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 (type_is_vectorable(left_type) && unsized_flag && try_wid_l<(int)integer_width) - try_wid_l += 1; - if (type_is_vectorable(right_type) && unsized_flag && try_wid_r<(int)integer_width) - try_wid_r += 1; + if (mode != SIZED) { + 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 " - << left_width << " bits and " - << right_width << " bits. Resorting to " - << try_wid_l << " bits and " - << try_wid_r << " bits." << endl; + << l_type << " " << l_width << " bits and " + << r_type << " " << r_width << " bits. Resorting to " + << l_width_ << " bits and " + << r_width_ << " bits." << endl; } - left_width_ = try_wid_l; - right_width_ = try_wid_r; - - return 1; + return expr_width_; } NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, - int, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - NetExpr*lp = left_->elaborate_expr(des, scope, left_width_, false); - NetExpr*rp = right_->elaborate_expr(des, scope, right_width_, false); + // 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_, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, false); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } - suppress_binary_operand_sign_if_needed(lp, rp); - - // The arguments of a compare need to have matching widths, so - // pad the width here. This matters because if the arguments - // are signed, then this padding will do sign extension. - if (type_is_vectorable(lp->expr_type())) - lp = pad_to_width(lp, left_width_, *this); - if (type_is_vectorable(rp->expr_type())) - rp = pad_to_width(rp, right_width_, *this); - - eval_expr(lp, left_width_); - eval_expr(rp, right_width_); + eval_expr(lp, l_width_); + eval_expr(rp, r_width_); // Handle some operand-specific special cases... switch (op_) { @@ -933,49 +545,36 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, break; } - NetEBComp*tmp = new NetEBComp(op_, lp, rp); + NetExpr*tmp = new NetEBComp(op_, lp, rp); tmp->set_line(*this); - bool flag = tmp->set_width(1); - if (flag == false) { - cerr << get_fileline() << ": internal error: " - "expression bit width of comparison != 1." << endl; - des->errors += 1; - } + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); return tmp; } -unsigned PEBLogic::test_width(Design*, NetScope*, - unsigned, unsigned, - ivl_variable_type_t&expr_type_out, - bool&) +unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_LOGIC; - expr_width_ = 1; - expr_type_out = expr_type_; + // 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, - int, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The left and right expressions are self-determined and - // independent. Run their test_width methods independently. We - // don't need the widths here, but we do need the expressions - // to calculate their self-determined width and type. - - bool left_flag = false; - ivl_variable_type_t left_type = IVL_VT_NO_TYPE; - left_->test_width(des, scope, 0, 0, left_type, left_flag); - - bool right_flag = false; - ivl_variable_type_t right_type = IVL_VT_NO_TYPE; - right_->test_width(des, scope, 0, 0, right_type, right_flag); - - NetExpr*lp = elab_and_eval(des, scope, left_, -1); + NetExpr*lp = elab_and_eval(des, scope, left_, -1); NetExpr*rp = elab_and_eval(des, scope, right_, -1); if ((lp == 0) || (rp == 0)) { delete lp; @@ -986,114 +585,181 @@ NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, lp = condition_reduce(lp); rp = condition_reduce(rp); - NetEBLogic*tmp = new NetEBLogic(op_, lp, rp); + NetExpr*tmp = new NetEBLogic(op_, lp, rp); tmp->set_line(*this); - tmp->set_width(1); + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); return tmp; } -unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - unsigned wid_left = left_->test_width(des,scope,min, lval, expr_type__, unsized_flag); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The right expression is self-determined and has no impact - // on the expression size that is generated. + // 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); - if (wid_left < min) - wid_left = min; - if (wid_left < lval) - wid_left = lval; + expr_width_ = left_->test_width(des, scope, mode); + expr_type_ = left_->expr_type(); + signed_flag_ = left_->has_sign(); - if (unsized_flag - && type_is_vectorable(expr_type__) - && wid_left > 0 - && wid_left < integer_width) { - wid_left = integer_width; + if ((mode != SIZED) && type_is_vectorable(expr_type_)) { + // We need to make our best guess at the right operand + // value, to minimise the calculated width. This is + // particularly important for the power operator... - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of unsized " << human_readable_op(op_) - << " is padded to compiler integer width=" << wid_left - << endl; + // 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 (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, false); + 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); + if (rc && (r_width < sizeof(long)*8)) + r_val = rc->value().as_long(); + + // Clip to a sensible range to avoid underflow/overflow + // in the following calculations. 1024 bits should be + // enough for anyone... + if (r_val < 0) + r_val = 0; + if (r_val > 1024) + r_val = 1024; + + // 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': // << + 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 = UNSIZED; + break; + + case 'p': // ** + if (lc && rc) { + verinum result = pow(lc->value(), rc->value()); + 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; + + expr_width_ = use_width; } - expr_type_ = expr_type__; - expr_width_ = wid_left; + if (op_ == 'l') + min_width_ = left_->min_width(); + else + min_width_ = expr_width_; - // Run a test-width on the shift amount so that its types are - // worked out for elaboration later on. We don't need the - // value now. - ivl_variable_type_t rtype = IVL_VT_NO_TYPE; - bool rflag = false; - unsigned wid_right = right_->test_width(des, scope, 0, 0, rtype, rflag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of exponent of " << op_ << " expression " - << "returns wid=" << wid_right << ", type=" << rtype - << ", flag=" << rflag << endl; - - return expr_width_; + return fix_width_(mode); } NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); - NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); - if (expr_wid > 0 && lp->expr_width() < (unsigned)expr_wid) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Pad left operand of " << human_readable_op(op_) - << " to " << expr_wid << "." << endl; - lp = pad_to_width(lp, expr_wid, *this); - } + // The left operand is always context determined, so propagate + // down the expression type (signed/unsigned). + left_->cast_signed(signed_flag_); - NetExpr*rp = right_->elaborate_expr(des, scope, -1, false); - if ((lp == 0) || (rp == 0)) { + unsigned r_width = right_->expr_width(); + + NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + if (lp == 0 || rp == 0) { delete lp; delete rp; return 0; } - eval_expr(lp); - eval_expr(rp); + // 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, - int expr_wid) const + 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); + 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, - int expr_wid) const + unsigned expr_wid) const { - NetExpr*tmp = 0; - switch (op_) { - case 'l': - tmp = elaborate_expr_base_lshift_(des, lp, rp, expr_wid); - break; - + case 'l': // << case 'r': // >> case 'R': // >>> - tmp = elaborate_expr_base_rshift_(des, lp, rp, expr_wid); break; default: @@ -1101,91 +767,220 @@ NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, << "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, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + width_mode_t&mode) { perm_string name = peek_tail_name(path_); - if (name=="$signed"|| name=="$unsigned") { + if (name=="$signed" || name=="$unsigned") { PExpr*expr = parms_[0]; if (expr == 0) return 0; - // The argument width is self-determined. - expr_width_ = expr->test_width(des, scope, 0, 0, expr_type__, unsized_flag); - expr_type_ = expr_type__; + // 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'); - // The result width is context dependent. - if (expr_width_ > min) - min = expr_width_; + if ((arg_mode != SIZED) && 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: $signed/$unsigned" - << " argument width = " << expr_width_ - << ", result width = " << min << "." << endl; + cerr << get_fileline() << ": debug: " << name + << " argument width = " << expr_width_ << "." << endl; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - return min; - } - - // Run through the arguments of the system function and make - // sure their widths/types are calculated. They are all self- - // determined. - for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - PExpr*expr = parms_[idx]; - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - unsigned wid = expr->test_width(des,scope,0,0,sub_type,flag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: test_width" - << " of " << name << " argument " << idx+1 - << " returns type=" << sub_type - << ", wid=" << wid << endl; + return expr_width_; } if (name=="$sizeof" || name=="$bits") { + 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_ = integer_width; + min_width_ = integer_width; + signed_flag_ = false; + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" - << " of $sizeof/$bits returns test_width" + << " of " << name << " returns test_width" << " of compiler integer." << endl; - expr_type_ = IVL_VT_BOOL; - expr_width_= integer_width; - - expr_type__ = expr_type_; 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; - expr_type_ = IVL_VT_BOOL; - expr_width_ = 1; - expr_type__ = expr_type_; 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(peek_tail_name(path_)); + const struct sfunc_return_type*sfunc_info = lookup_sys_func(name); - expr_type_ = sfunc_info->type; - expr_width_ = sfunc_info->wid; - - expr_type__ = expr_type_; + 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 " @@ -1197,12 +992,10 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, } unsigned PECallFunction::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + width_mode_t&mode) { if (peek_tail_name(path_)[0] == '$') - return test_width_sfunc_(des, scope, min, lval, expr_type__, unsized_flag); + 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 @@ -1212,9 +1005,11 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, // 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; - expr_type__ = expr_type_; + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + return expr_width_; } @@ -1229,18 +1024,17 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, assert(dscope); if (NetNet*res = dscope->find_signal(dscope->basename())) { - expr_type_ = res->data_type(); + 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 " << res->vector_width() + << "of function returns width " << expr_width_ << ", type=" << expr_type_ << "." << endl; - if (! type_is_vectorable(expr_type__)) - unsized_flag = true; - - expr_width_ = res->vector_width(); - expr_type__ = expr_type_; return expr_width_; } @@ -1248,44 +1042,28 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, return 0; } -NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const +NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const { /* 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)) { - /* If this is an unsized signed constant then we may need - * to extend it up to integer_width. */ - if (! tmp->has_width() && tmp->has_sign() && (wid > 0)) { - unsigned pwidth = tmp->expr_width(); - if ((unsigned)wid > integer_width) { - if (integer_width > pwidth) pwidth = integer_width; - } else { - if ((unsigned)wid > pwidth) pwidth = wid; - } - tmp = dynamic_cast(pad_to_width(tmp, pwidth, - *expr)); - assert(tmp); - } - tmp->cast_signed(signed_flag); - if (wid > (int)(tmp->expr_width())) { - verinum oval = pad_to_width(tmp->value(), wid); - tmp = new NetEConst(oval); + 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; } - if (wid < 0) - wid = expr->expr_width(); - if (debug_elaborate) cerr << get_fileline() << ": debug: cast to " << wid - << " bits" << endl; + << " bits " << (signed_flag_?"signed":"unsigned") << endl; NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - tmp->cast_signed(signed_flag); + return tmp; } @@ -1295,16 +1073,18 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) * size_tf functions, make assumptions about widths based on some * known function names. */ -NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const +NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, + unsigned expr_wid) const { + perm_string name = peek_tail_name(path_); /* Catch the special case that the system function is the $signed function. Its argument will be evaluated as a self-determined expression. */ - if (strcmp(peek_tail_name(path_), "$signed") == 0) { + if (name=="$signed" || name=="$unsigned") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $signed() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } @@ -1312,93 +1092,54 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w PExpr*expr = parms_[0]; NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); - return cast_to_width_(sub, expr_wid, true); - } - /* As above, for the $unsigned function. */ - if (strcmp(peek_tail_name(path_), "$unsigned") == 0) { - if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $unsigned() function " - << "takes exactly one(1) argument." << endl; - des->errors += 1; - return 0; - } - - PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); - - return cast_to_width_(sub, expr_wid, false); + 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 ((strcmp(peek_tail_name(path_), "$sizeof") == 0) - || (strcmp(peek_tail_name(path_), "$bits") == 0)) { + if (name=="$sizeof" || name=="$bits") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $bits() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } - if (strcmp(peek_tail_name(path_), "$sizeof") == 0) + if (name=="$sizeof") cerr << get_fileline() << ": warning: $sizeof is deprecated." << " Use $bits() instead." << endl; PExpr*expr = parms_[0]; - ivl_assert(*this, expr); - /* Elaborate the sub-expression to get its - self-determined width, and save that width. Then - delete the expression because we don't really want - the expression itself. */ - long sub_expr_width = 0; - if (NetExpr*tmp = expr->elaborate_expr(des, scope, -1, true)) { - sub_expr_width = tmp->expr_width(); - delete tmp; - } - - verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned)); + verinum val ( (uint64_t)expr->expr_width(), integer_width); NetEConst*sub = new NetEConst(val); sub->set_line(*this); - return sub; + 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. The subexpression is elaborated but not - evaluated. */ - if (strcmp(peek_tail_name(path_), "$is_signed") == 0) { + otherwise. */ + if (name=="$is_signed") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $is_signed() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, -1, true); - verinum val (sub->has_sign()? verinum::V1 : verinum::V0, 1); - delete sub; - - sub = new NetEConst(val); + verinum val (expr->has_sign()? verinum::V1 : verinum::V0, 1); + NetEConst*sub = new NetEConst(val); sub->set_line(*this); - return sub; + return cast_to_width_(sub, expr_wid); } - /* 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(peek_tail_name(path_)); - - ivl_variable_type_t sfunc_type = sfunc_info->type; - unsigned wid = sfunc_info->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 @@ -1411,11 +1152,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w if ((nparms == 1) && (parms_[0] == 0)) nparms = 0; - NetESFunc*fun = new NetESFunc(peek_tail_name(path_), sfunc_type, - wid, nparms); + NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms); fun->set_line(*this); - if (sfunc_info->signed_flag) - fun->cast_signed(true); /* Now run through the expected parameters. If we find that there are missing parameters, print an error message. @@ -1428,9 +1166,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { - NetExpr*tmp1 = expr->elaborate_expr(des, scope, -1, true); - eval_expr(tmp1); - fun->parm(idx, tmp1); + NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr); + fun->parm(idx, tmp); } else { missing_parms += 1; @@ -1439,19 +1176,22 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w } if (missing_parms > 0) { - cerr << get_fileline() << ": error: The function " - << peek_tail_name(path_) + cerr << get_fileline() << ": error: The function " << name << " has been called with empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; des->errors += 1; } - return fun; + 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) const + 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); @@ -1500,14 +1240,17 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, ivl_assert(*this, 0); } - NetEAccess*tmp = new NetEAccess(branch, nature); + 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; } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { if (peek_tail_name(path_)[0] == '$') return elaborate_sfunc_(des, scope, expr_wid); @@ -1519,7 +1262,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, // way. ivl_nature_t access_nature = find_access_function(path_); if (access_nature) - return elaborate_access_func_(des, scope, access_nature); + return elaborate_access_func_(des, scope, access_nature, + expr_wid); // We do not currently support constant user function and // this is where things fail because of that, though we @@ -1546,8 +1290,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if ((parms_count == 1) && (parms_[0] == 0)) parms_count = 0; - - svector parms (parms_count); /* Elaborate the input expressions for the function. This is @@ -1561,7 +1303,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if (tmp) { parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->data_type(), - def->port(idx)->vector_width(), + (unsigned)def->port(idx)->vector_width(), tmp); if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" @@ -1603,8 +1345,11 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, NetESignal*eres = new NetESignal(res); NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms); func->set_line(*this); - func->cast_signed(res->get_signed()); - 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 " @@ -1614,76 +1359,23 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return 0; } -unsigned PEConcat::test_width(Design*des, NetScope*scope, - unsigned, unsigned, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) { - expr_type_ = IVL_VT_LOGIC; - - unsigned count_width = 0; + expr_width_ = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - tested_widths_[idx] = parms_[idx]->test_width(des, scope, 0, 0, expr_type__, unsized_flag); - count_width += tested_widths_[idx]; + expr_width_ += parms_[idx]->test_width(des, scope, width_modes_[idx]); } - if (repeat_) { - unsigned repeat_count = 1; - - // The repeat expression is self-determined and - // its own type. - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - repeat_->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - - // Try to evaluate the repeat expression now, so - // that we can give the caller an accurate - // expression width. - NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); - if (NetEConst*tmp_c = dynamic_cast (tmp)) { - repeat_count = tmp_c->value().as_ulong(); - - } else { - // Gack! Can't evaluate expression yet! - // Unfortunately, it is possible that this - // expression may turn out to be constant later in - // elaboration, so we can't really get away with - // reporting an error. - repeat_count = 1; - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "CONCAT MISSING TEST_WIDTH WHEN REPEAT IS PRESENT!" - << endl; - } - count_width *= repeat_count; - } - - expr_type__ = expr_type_; - unsized_flag = false; - return count_width; -} - -// Keep track of the concatenation/repeat depth. -static int concat_depth = 0; - -NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const -{ - concat_depth += 1; - NetExpr* repeat = 0; - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: Elaborate expr=" << *this - << ", expr_wid=" << expr_wid << endl; - } + expr_type_ = IVL_VT_LOGIC; + signed_flag_ = false; /* If there is a repeat expression, then evaluate the constant value and set the repeat count. */ - if (repeat_) { + if (repeat_ && (scope != tested_scope_)) { need_constant_expr = true; NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); need_constant_expr = false; - assert(tmp); + if (tmp == 0) return 0; if (tmp->expr_type() == IVL_VT_REAL) { cerr << tmp->get_fileline() << ": error: Concatenation " @@ -1696,7 +1388,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, if (rep == 0) { cerr << get_fileline() << ": error: " - "Concatenation repeat expression cannot be evaluated." + "Concatenation repeat expression is not constant." << endl; cerr << get_fileline() << ": : The expression is: " << *tmp << endl; @@ -1709,7 +1401,6 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, << "may not be undefined (" << rep->value() << ")." << endl; des->errors += 1; - concat_depth -= 1; return 0; } @@ -1718,23 +1409,43 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, << "may not be negative (" << rep->value().as_long() << ")." << endl; des->errors += 1; - concat_depth -= 1; return 0; } - if (rep->value().is_zero() && 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; - } + repeat_count_ = rep->value().as_ulong(); - repeat = rep; + 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*des, NetScope*scope, + unsigned expr_wid, bool) const +{ + 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. */ @@ -1747,25 +1458,29 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } assert(parms_[idx]); - NetExpr*ex = elab_and_eval(des, scope, parms_[idx], - tested_widths_[idx], 0); + unsigned wid = parms_[idx]->expr_width(); + NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, false); 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 (! ex->has_width()) { + 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; } @@ -1778,29 +1493,34 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } wid_sum += ex->expr_width(); } + if (parm_errors) { + concat_depth -= 1; + return 0; + } /* Make the empty concat expression. */ - NetEConcat*tmp = new NetEConcat(parm_cnt, repeat); - tmp->set_line(*this); + NetEConcat*concat = new NetEConcat(parm_cnt, repeat_count_); + 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; - tmp->set(idx, parms[off+idx]); + concat->set(idx, parms[off+idx]); } - tmp->set_width(wid_sum * tmp->repeat()); - if (wid_sum == 0 && concat_depth < 2) { cerr << get_fileline() << ": error: Concatenation may not " << "have zero width in this context." << endl; des->errors += 1; concat_depth -= 1; - delete tmp; + delete concat; return 0; } + NetExpr*tmp = pad_to_width(concat, expr_wid, *this); + tmp->cast_signed(signed_flag_); + concat_depth -= 1; return tmp; } @@ -1812,24 +1532,20 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, * * Absent any better idea, we call all real valued results a width of 1. */ -unsigned PEFNumber::test_width(Design*, NetScope*, - unsigned, unsigned, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEFNumber::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_REAL; - expr_width_ = 1; - unsized_flag = false; + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; - expr_type__ = expr_type_; - return 1; + return expr_width_; } -NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, int, bool) const +NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, bool) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); - tmp->set_width(1U, false); return tmp; } @@ -1849,20 +1565,12 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.sel == index_component_t::SEL_PART); ivl_assert(*this, index_tail.msb && index_tail.lsb); - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - int msb_wid = index_tail.msb->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - - tmp_type = IVL_VT_NO_TYPE; - tmp_flag = false; - int lsb_wid = index_tail.lsb->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - /* 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. */ need_constant_expr = true; - NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, lsb_wid); + NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1); need_constant_expr = false; NetEConst*lsb_c = dynamic_cast(lsb_ex); if (lsb_c == 0) { @@ -1882,7 +1590,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, } need_constant_expr = true; - NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, msb_wid); + NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1); need_constant_expr = false; NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { @@ -1920,7 +1628,6 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, /* 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. */ - probe_expr_width(des, scope, index_tail.lsb); need_constant_expr = true; NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1); need_constant_expr = false; @@ -1953,7 +1660,6 @@ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const ivl_assert(*this, index_tail.lsb != 0); ivl_assert(*this, index_tail.msb != 0); - probe_expr_width(des, scope, index_tail.msb); NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1); return tmp; } @@ -1985,23 +1691,7 @@ bool PEIdent::calculate_param_range_(Design*, NetScope*, return true; } -static void probe_index_expr_width(Design*des, NetScope*scope, - const name_component_t&name) -{ - for (list::const_iterator cur = name.index.begin() - ; cur != name.index.end() ; ++ cur ) { - - if (cur->msb) - probe_expr_width(des, scope, cur->msb); - if (cur->lsb) - probe_expr_width(des, scope, cur->lsb); - } -} - -unsigned PEIdent::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) { NetNet* net = 0; const NetExpr*par = 0; @@ -2018,7 +1708,6 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, 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()) { - probe_index_expr_width(des, scope, name_tail); const index_component_t&index_tail = name_tail.index.back(); // Skip full array word net selects. if (!net || (name_tail.index.size() > net->array_dimensions())) { @@ -2059,17 +1748,21 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, } if (use_width != UINT_MAX) { - expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic - expr_width_ = max(use_width, min); - expr_type__ = expr_type_; + 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) { - expr_type_ = net->data_type(); - expr_width_= max(net->vector_width(), (unsigned long)min); - expr_type__ = expr_type_; + expr_type_ = net->data_type(); + expr_width_ = net->vector_width(); + min_width_ = expr_width_; + signed_flag_ = net->get_signed(); + return expr_width_; } @@ -2079,35 +1772,51 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, netenum_t*use_enum = par_enum->enumeration(); ivl_assert(*this, use_enum != 0); - expr_type_ = use_enum->base_type(); - unsized_flag = false; - expr_width_ = max(use_enum->base_width(), min); - expr_type__ = expr_type_; + expr_type_ = use_enum->base_type(); + expr_width_ = use_enum->base_width(); + min_width_ = expr_width_; + signed_flag_ = false; + 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(); - expr_type__ = expr_type_; - if (!par->has_width()) - unsized_flag = true; + expr_type_ = par->expr_type(); + expr_width_ = par->expr_width(); + min_width_ = expr_width_; + signed_flag_ = par->has_sign(); + + if ((mode < LOSSLESS) && !par->has_width()) + 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 (mode < LOSSLESS) + mode = LOSSLESS; + + return expr_width_; + } + // Not a net, and not a parameter? Give up on the type, but - // set the width that we collected. - expr_type_ = IVL_VT_NO_TYPE; - expr_width_ = min; + // set the width to 0. + expr_type_ = IVL_VT_NO_TYPE; + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - - expr_type__ = expr_type_; - return min; + return expr_width_; } /* @@ -2122,7 +1831,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, * The signal name may be escaped, but that affects nothing here. */ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool sys_task_arg) const + unsigned expr_wid, bool sys_task_arg) const { assert(scope); @@ -2145,9 +1854,18 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, ex1, ex2); // If the identifier name is a parameter name, then return - // a reference to the parameter expression. - if (par != 0) - return elaborate_expr_param_(des, scope, par, found_in, ex1, ex2, expr_wid); + // the parameter value. + if (par != 0) { + NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, + ex1, ex2, expr_wid); + + if (!tmp) return 0; + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; + } // If this is a parameter expression, no other identifiers are valid. if (is_param_expr) { @@ -2160,8 +1878,17 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. - if (net != 0) - return elaborate_expr_net(des, scope, net, found_in, sys_task_arg); + if (net != 0) { + NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, + expr_wid, sys_task_arg); + + if (!tmp) return 0; + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; + } // If the identifier is a named event. // is a variable reference. @@ -2181,7 +1908,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: " << path_ << " is genvar with value " << scope->genvar_tmp_val << "." << endl; - verinum val (scope->genvar_tmp_val); + verinum val (scope->genvar_tmp_val, expr_wid); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; @@ -2259,7 +1986,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, false); + NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, + expr_wid, false); NetESFunc*sys_expr = 0; if (method_name == "name") { @@ -2278,7 +2006,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << "Unknown method name `" << method_name << "'" << " attached to " << use_path << "." << endl; des->errors += 1; - return elaborate_expr_net(des, scope, net, found_in, false); + return elaborate_expr_net(des, scope, net, found_in, + expr_wid, false); } sys_expr->set_line(*this); @@ -2393,7 +2122,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; @@ -2422,7 +2152,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, "Replacing select with a constant 'bx." << endl; } - NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false)); + verinum val(verinum::Vx, expr_wid, true); + NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; } @@ -2587,7 +2318,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, base = normalize_variable_base(base, par_msv, par_lsv, wid, true); NetExpr*tmp = par->dup_expr(); - tmp = new NetESelect(tmp, base, wid); + tmp = new NetESelect(tmp, base, wid, IVL_SEL_IDX_UP); tmp->set_line(*this); return tmp; } @@ -2667,7 +2398,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, base = normalize_variable_base(base, par_msv, par_lsv, wid, false); NetExpr*tmp = par->dup_expr(); - tmp = new NetESelect(tmp, base, wid); + tmp = new NetESelect(tmp, base, wid, IVL_SEL_IDX_DOWN); tmp->set_line(*this); return tmp; } @@ -2683,7 +2414,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, - int expr_wid) const + unsigned expr_wid) const { const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; @@ -2705,7 +2436,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, // selects to different methods. if (use_sel == index_component_t::SEL_PART) return elaborate_expr_param_part_(des, scope, par, found_in, - par_msb, par_lsb); + 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, @@ -2719,6 +2450,8 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, // rewritten in the above format, as I get to it. NetExpr*tmp = par->dup_expr(); + if (!tmp) + return 0; if (use_sel == index_component_t::SEL_BIT) { ivl_assert(*this, !name_tail.index.empty()); @@ -2736,8 +2469,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, /* Handle the case where a parameter has a bit select attached to it. Generate a NetESelect object to select the bit as desired. */ - NetExpr*mtmp = index_tail.msb->elaborate_expr(des, scope, -1,false); - eval_expr(mtmp); + NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1); /* Let's first try to get constant values for both the parameter and the bit select. If they are @@ -2841,21 +2573,21 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate parameter <" << path_ - << "> as enumeration constant." << endl; + << "> as enumeration constant." << *etmp << endl; tmp = etmp->dup_expr(); + tmp = pad_to_width(tmp, expr_wid, *this); } else { - + /* No bit or part select. Make the constant into a - NetEConstParam if possible. */ + NetEConstParam or NetECRealParam as appropriate. */ NetEConst*ctmp = dynamic_cast(tmp); if (ctmp != 0) { + verinum cvalue = cast_to_width(ctmp->value(), expr_wid); + perm_string name = peek_tail_name(path_); NetEConstParam*ptmp - = new NetEConstParam(found_in, name, ctmp->value()); - - if (expr_wid > 0) - ptmp->set_width((unsigned)expr_wid); + = new NetEConstParam(found_in, name, cvalue); if (debug_elaborate) cerr << get_fileline() << ": debug: " @@ -2889,6 +2621,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, */ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, + unsigned expr_wid, bool sys_task_arg) const { const name_component_t&name_tail = path_.back(); @@ -2978,7 +2711,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, } if (word_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, res, found_in); + 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); @@ -2998,7 +2731,8 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, * Handle part selects of NetNet identifiers. */ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; @@ -3028,7 +2762,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, "Replacing select with a constant 'bx." << endl; } - NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false)); + NetEConst*tmp = new NetEConst(verinum(verinum::Vx, expr_wid, true)); tmp->set_line(*this); return tmp; } @@ -3103,7 +2837,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, * Part select indexed up, i.e. net[ +: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*) const { NetExpr*base = calculate_up_do_base_(des, scope); @@ -3176,7 +2910,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, base = normalize_variable_base(base, net->msi(), net->lsi(), wid, true); - NetESelect*ss = new NetESelect(net, base, wid); + NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_UP); ss->set_line(*this); if (debug_elaborate) { @@ -3191,7 +2925,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, * Part select indexed down, i.e. net[ -: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, - NetESignal*net, NetScope*)const + NetESignal*net, NetScope*)const { NetExpr*base = calculate_up_do_base_(des, scope); @@ -3263,7 +2997,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, base = normalize_variable_base(base, net->msi(), net->lsi(), wid, false); - NetESelect*ss = new NetESelect(net, base, wid); + NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_DOWN); ss->set_line(*this); if (debug_elaborate) { @@ -3275,7 +3009,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, } NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -3376,10 +3110,12 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, + unsigned expr_wid, bool sys_task_arg) const { if (net->array_dimensions() > 0) - return elaborate_expr_net_word_(des, scope, net, found_in, sys_task_arg); + return elaborate_expr_net_word_(des, scope, net, found_in, + expr_wid, sys_task_arg); NetESignal*node = new NetESignal(net); node->set_line(*this); @@ -3403,7 +3139,8 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, // 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); + 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); @@ -3422,104 +3159,72 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, return node; } -unsigned PENumber::test_width(Design*, NetScope*, - unsigned min, unsigned lval, - ivl_variable_type_t&use_expr_type, - bool&unsized_flag) +unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) { - expr_type_ = IVL_VT_LOGIC; - unsigned use_wid = value_->len(); - if (min > use_wid) - use_wid = min; + expr_type_ = IVL_VT_LOGIC; + expr_width_ = value_->len(); + min_width_ = 1; + signed_flag_ = value_->has_sign(); - if (! value_->has_len()) - unsized_flag = true; + if ((mode < LOSSLESS) && !value_->has_len()) + mode = LOSSLESS; - if (lval > 0 && lval < use_wid) - use_wid = lval; - - use_expr_type = expr_type_; - expr_width_ = use_wid; - return use_wid; + return expr_width_; } NetEConst* PENumber::elaborate_expr(Design*, NetScope*, - int expr_width__, bool) const + unsigned expr_wid, bool) const { assert(value_); - verinum tvalue = *value_; - - // If the expr_width is >0, then the context is requesting a - // specific size (for example this is part of the r-values of - // an assignment) so we pad to the desired width and ignore - // the self-determined size. - if (expr_width__ > 0) { - tvalue = pad_to_width(tvalue, expr_width__); - if ( (tvalue.len() > (unsigned)expr_width__) || - (is_param_expr && !tvalue.has_len()) ) { - verinum tmp (tvalue, expr_width__); - tmp.has_sign(tvalue.has_sign()); - tvalue = tmp; - } - } - - NetEConst*tmp = new NetEConst(tvalue); + 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*, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&) +unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_BOOL; - expr_width_ = text_? 8*strlen(text_) : 0; - if (min > expr_width_) - expr_width_ = min; + expr_type_ = IVL_VT_BOOL; + expr_width_ = text_? verinum(text_).len() : 0; + min_width_ = expr_width_; + signed_flag_ = false; - expr_type__ = expr_type_; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(expr_width_ >= lval); return expr_width_; } NetEConst* PEString::elaborate_expr(Design*, NetScope*, - int, bool) const + unsigned expr_wid, bool) const { - NetEConst*tmp = new NetEConst(value()); + 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, - unsigned min, unsigned lval, - ivl_variable_type_t&use_expr_type, - bool&flag) +unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - // The condition of the ternary is self-determined, but we - // test its width to force its type to be calculated. - ivl_variable_type_t con_type = IVL_VT_NO_TYPE; - bool con_flag = false; - expr_->test_width(des, scope, 0, 0, con_type, con_flag); + // The condition of the ternary is self-determined, so + // we will test its width when we elaborate it. - ivl_variable_type_t tru_type = IVL_VT_NO_TYPE; - unsigned tru_wid = tru_->test_width(des, scope, min, lval, tru_type,flag); + // Test the width of the true and false clauses. + unsigned tru_width = tru_->test_width(des, scope, mode); - bool initial_flag = flag; - ivl_variable_type_t fal_type = IVL_VT_NO_TYPE; - unsigned fal_wid = fal_->test_width(des, scope, min, lval, fal_type,flag); + width_mode_t saved_mode = mode; - // If the false clause is unsized, then try again with the - // true clause, because it might choose a different width if - // it is in an unsized context. - if (initial_flag == false && flag == true) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "False clause is unsized, so retest width of true clause." - << endl; - tru_wid = tru_->test_width(des, scope, max(min,fal_wid), lval, tru_type, flag); + 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 an unsized 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 @@ -3527,28 +3232,42 @@ unsigned PETernary::test_width(Design*des, NetScope*scope, // 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; - expr_width_ = 1; } else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) { expr_type_ = IVL_VT_LOGIC; - expr_width_ = max(tru_wid,fal_wid); } else { ivl_assert(*this, tru_type == fal_type); expr_type_ = tru_type; - expr_width_ = max(tru_wid,fal_wid); + } + 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 = UNSIZED; } if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Ternary expression type=" << expr_type_ << ", width=" << expr_width_ - << ", unsized_flag=" << flag << " (tru_type=" << tru_type << ", fal_type=" << fal_type << ")" << endl; - use_expr_type = expr_type_; - return expr_width_; + return fix_width_(mode); } bool NetETernary::test_operand_compat(ivl_variable_type_t l, @@ -3576,26 +3295,12 @@ bool NetETernary::test_operand_compat(ivl_variable_type_t l, * methods. If any elaboration fails, then give up and return 0. */ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { assert(expr_); assert(tru_); assert(fal_); - int use_wid = expr_wid >= 0? expr_wid : 0; - - if (expr_wid < 0) { - use_wid = expr_width(); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Self-sized ternary chooses wid="<< use_wid - << ", type=" << expr_type() - << ", expr=" << *this - << endl; - - ivl_assert(*this, use_wid > 0); - } - // Elaborate and evaluate the condition expression. Note that // it is always self-determined. NetExpr*con = elab_and_eval(des, scope, expr_, -1); @@ -3620,16 +3325,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: Short-circuit " "elaborate TRUE clause of ternary." << endl; - if (use_wid <= 0) { - cerr << get_fileline() << ": internal error: " - << "Unexpected use_wid=" << use_wid - << " processing short-circuit TRUE clause" - << " of expression: " << *this << endl; - } - ivl_assert(*this, use_wid > 0); - NetExpr*texpr = elab_and_eval_alternative_(des, scope, tru_, - use_wid); - return pad_to_width(texpr, use_wid, *this); + + return elab_and_eval_alternative_(des, scope, tru_, expr_wid); } // Condition is constant FALSE, so we only need the @@ -3639,29 +3336,21 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: Short-circuit " "elaborate FALSE clause of ternary." << endl; - if (use_wid <= 0) { - cerr << get_fileline() << ": internal error: " - << "Unexpected use_wid=" << use_wid - << " processing short-circuit FALSE clause" - << " of expression: " << *this << endl; - } - ivl_assert(*this, use_wid > 0); - NetExpr*texpr = elab_and_eval_alternative_(des, scope, fal_, - use_wid); - return pad_to_width(texpr, use_wid, *this); + + return elab_and_eval_alternative_(des, scope, fal_, expr_wid); } // X and Z conditions need to blend both results, so we // can't short-circuit. } - NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, use_wid); + NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid); if (tru == 0) { delete con; return 0; } - NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, use_wid); + NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid); if (fal == 0) { delete con; delete tru; @@ -3677,15 +3366,7 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, return 0; } - suppress_binary_operand_sign_if_needed(tru, fal); - - /* Whatever the width we choose for the ternary operator, we - need to make sure the operands match. */ - tru = pad_to_width(tru, use_wid, *this); - fal = pad_to_width(fal, use_wid, *this); - - NetETernary*res = new NetETernary(con, tru, fal); - res->cast_signed(tru->has_sign() && fal->has_sign()); + NetETernary*res = new NetETernary(con, tru, fal, expr_wid, signed_flag_); res->set_line(*this); return res; } @@ -3697,19 +3378,24 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, * self-determined. */ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, int use_wid) const + PExpr*expr, unsigned expr_wid) const { + int context_wid = expr_wid; if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) { - return elab_and_eval(des, scope, expr, -1); + expr_wid = expr->expr_width(); + context_wid = -1; + } else { + expr->cast_signed(signed_flag_); } + NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, false); + if (tmp == 0) return 0; - return elab_and_eval(des, scope, expr, use_wid); + eval_expr(tmp, context_wid); + + return tmp; } -unsigned PEUnary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEUnary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { switch (op_) { case '&': // Reduction AND @@ -3718,60 +3404,43 @@ unsigned PEUnary::test_width(Design*des, NetScope*scope, case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) - { - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - unsigned swid = expr_->test_width(des, scope, 0, 0, sub_type, flag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of sub-expression of " << op_ - << " returns " << swid << "." << endl; - - expr_type_ = sub_type; - } - expr_width_ = 1; - - expr_type__ = expr_type_; - return expr_width_; - case '!': - { - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - expr_->test_width(des, scope, 0, 0, sub_type, flag); - expr_type_ = (sub_type==IVL_VT_BOOL)? IVL_VT_BOOL : IVL_VT_LOGIC; - } - // Logical ! always returns a single-bit LOGIC or BOOL value. - expr_width_ = 1; - expr_type__ = expr_type_; - return expr_width_; + { + 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_; } - unsigned test_wid = expr_->test_width(des, scope, min, lval, expr_type__, unsized_flag); - switch (op_) { - // For these operators, the act of padding to the - // minimum width can have an important impact on the - // calculation. So don't let the tested width be less - // then the minimum width. - case '-': - case '+': - case '~': - if (test_wid < min) - test_wid = min; - break; - } + expr_width_ = expr_->test_width(des, scope, mode); + expr_type_ = expr_->expr_type(); + min_width_ = expr_->min_width(); + signed_flag_ = expr_->has_sign(); - expr_type_ = expr_type__; - expr_width_ = test_wid; - return test_wid; + return fix_width_(mode); } NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - /* Reduction operators and ! always have a self determined width. */ + unsigned sub_width = expr_wid; switch (op_) { + // Reduction operators and ! always have a self determined width. case '!': case '&': // Reduction AND case '|': // Reduction OR @@ -3779,12 +3448,16 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) - expr_wid = -1; + 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, expr_wid, false); + NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, false); if (ip == 0) return 0; ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE); @@ -3792,7 +3465,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, NetExpr*tmp; switch (op_) { default: - tmp = new NetEUnary(op_, ip); + tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); break; @@ -3800,31 +3473,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, if (NetEConst*ipc = dynamic_cast(ip)) { verinum val = ipc->value(); - if (expr_wid > 0) - val = pad_to_width(val, expr_wid); - - /* When taking the - of a number, extend it one - bit to accommodate a possible sign bit. - - NOTE: This may not be correct! The test_width - is supposed to detect the special case that we - want to do lossless self-determined - expressions, and the function that calls - elaborate_expr should account for that in the - expr_wid argument. */ - unsigned use_len = val.len(); - if (expr_wid < 0) - use_len += 1; /* Calculate unary minus as 0-val */ - verinum zero (verinum::V0, use_len, val.has_len()); + verinum zero (verinum::V0, expr_wid); zero.has_sign(val.has_sign()); - verinum nval = zero - val; - - if (val.has_len()) - nval = verinum(nval, val.len()); - nval.has_sign(val.has_sign()); + verinum nval = verinum(zero - val, expr_wid); tmp = new NetEConst(nval); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); delete ip; @@ -3838,9 +3493,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, delete ip; } else { - if (expr_wid > 0) - ip = pad_to_width(ip, expr_wid, *this); - tmp = new NetEUnary(op_, ip); + tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); } break; @@ -3898,6 +3551,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp->set_line(*this); } + tmp = pad_to_width(tmp, expr_wid, *this); break; case '&': // Reduction AND @@ -3915,6 +3569,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp = new NetEUReduce(op_, ip); tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); break; case '~': @@ -3925,15 +3580,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, return tmp; } -NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const +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(); - if (expr_wid > (int)value.len()) - value = pad_to_width(value, expr_wid); // The only operand that I know can get here is the // unary not (~). @@ -3946,14 +3599,16 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const return ctmp; } - if (expr_wid > (int)operand->expr_width()) - operand = pad_to_width(operand, expr_wid, *this); - - NetEUBits*tmp = new NetEUBits(op_, operand); + NetEUBits*tmp = new NetEUBits(op_, operand, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } +NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, bool) const +{ + return 0; +} + NetNet* Design::find_discipline_reference(ivl_discipline_t dis, NetScope*scope) { NetNet*gnd = discipline_references_[dis->name()]; diff --git a/elab_lval.cc b/elab_lval.cc index bc785a299..443578d9a 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -56,7 +56,7 @@ * l-value. * * This last case can turn up in statements like: {a, b[1]} = c; - * rather then create a NetAssign_ for each item in the concatenation, + * rather than create a NetAssign_ for each item in the concatenation, * elaboration makes a single NetAssign_ and connects it up properly. */ @@ -255,12 +255,6 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, ivl_assert(*this, index_head.msb != 0); ivl_assert(*this, index_head.lsb == 0); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_head.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - NetExpr*word = elab_and_eval(des, scope, index_head.msb, -1); // If there is a non-zero base to the memory, then build an @@ -335,12 +329,6 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, NetNet*reg = lv->sig(); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - // Bit selects have a single select expression. Evaluate the // constant value and treat it as a part select with a bit // width of 1. @@ -463,13 +451,8 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, unsigned long wid; calculate_up_do_width_(des, scope, wid); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - 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. @@ -532,10 +515,12 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, if (use_sel == index_component_t::SEL_IDX_UP) { base = normalize_variable_base(base, reg->msb(), reg->lsb(), wid, true); + sel_type = IVL_SEL_IDX_UP; } else { // This is assumed to be a SEL_IDX_DO. base = normalize_variable_base(base, reg->msb(), reg->lsb(), wid, false); + sel_type = IVL_SEL_IDX_DOWN; } } @@ -543,7 +528,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, cerr << get_fileline() << ": debug: Set part select width=" << wid << ", base=" << *base << endl; - lv->set_part(base, wid); + lv->set_part(base, wid, sel_type); return true; } diff --git a/elab_net.cc b/elab_net.cc index 452ef8e6a..88b5088c9 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -73,23 +73,20 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, nets[idx] = parms_[idx]->elaborate_lnet(des, scope); } - if (nets[idx] == 0) errors += 1; - else if (nets[idx]->data_type() == IVL_VT_REAL) { + 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(); - + } else { + width += nets[idx]->vector_width(); + } } - /* 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 += errors; return 0; } @@ -170,6 +167,28 @@ 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 @@ -203,12 +222,6 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, case index_component_t::SEL_IDX_DO: case index_component_t::SEL_IDX_UP: { - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, - integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); need_constant_expr = true; NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1); need_constant_expr = false; @@ -439,21 +452,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, return 0; } - if (sig->port_type() == NetNet::PINPUT) { - sig->port_type(NetNet::PINOUT); - // This map mask prevents an error message being - // repeated endlessly. - static map mask_map; - bool&flag = mask_map[sig->get_fileline() + ":" + string(sig->name())]; - if (! flag) { - cerr << get_fileline() << ": warning: L-value ``" - << sig->name() << "'' is also an input port." << endl; - cerr << sig->get_fileline() << ": warning: input " - << sig->name() << "; is coerced to inout." << endl; - flag = true; - } - } - // Default part select is the entire word. unsigned midx = sig->vector_width()-1, lidx = 0; // The default word select is the first. @@ -482,12 +480,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, } ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_head.msb->test_width(des, scope, - integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); need_constant_expr = true; NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1); need_constant_expr = false; @@ -582,10 +574,10 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, sig = tmp; } - /* If the desired l-value vector is narrower then the + /* 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 with matches the + can be skipped if the desired width matches the original vector. */ if (subnet_wid != sig->vector_width()) { @@ -655,6 +647,7 @@ NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const */ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const { + assert(scope->type() == NetScope::MODULE); NetNet*sig = des->find_signal(scope, path_); if (sig == 0) { cerr << get_fileline() << ": error: no wire/reg " << path_ @@ -707,8 +700,10 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const /* 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)) + if ((lidx == 0) && (midx == (long)sig->vector_width()-1)) { + scope->add_module_port(sig); return sig; + } unsigned swid = abs(midx - lidx) + 1; ivl_assert(*this, swid > 0 && swid < sig->vector_width()); @@ -718,26 +713,25 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const tmp->port_type(sig->port_type()); tmp->data_type(sig->data_type()); tmp->set_line(*this); + tmp->local_flag(true); NetNode*ps = 0; switch (sig->port_type()) { case NetNet::PINPUT: - ps = new NetPartSelect(sig, sig->sb_to_idx(lidx), swid, - NetPartSelect::PV); + 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, sig->sb_to_idx(lidx), swid, - NetPartSelect::VP); + ps = new NetPartSelect(sig, lidx, swid, NetPartSelect::VP); connect(tmp->pin(0), ps->pin(0)); sig = tmp; break; case NetNet::PINOUT: ps = new NetTran(scope, scope->local_symbol(), sig->vector_width(), - swid, sig->sb_to_idx(lidx)); + swid, lidx); connect(sig->pin(0), ps->pin(0)); connect(tmp->pin(0), ps->pin(1)); sig = tmp; @@ -751,5 +745,42 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const ps->set_line(*this); des->add_node(ps); + scope->add_module_port(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; +} diff --git a/elab_scope.cc b/elab_scope.cc index 98b271267..4bf184661 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -50,7 +50,7 @@ typedef map::const_iterator mparm_it_t; -static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, +static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, const LexicalScope::param_expr_t&cur) { NetScope::range_t*range_list = 0; @@ -61,7 +61,6 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, tmp->high_open_flag = range->high_open_flag; if (range->low_expr) { - probe_expr_width(des, scope, range->low_expr); tmp->low_expr = elab_and_eval(des, scope, range->low_expr, -1); ivl_assert(*range->low_expr, tmp->low_expr); } else { @@ -78,7 +77,6 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, tmp->high_expr = tmp->low_expr; } else if (range->high_expr) { - probe_expr_width(des, scope, range->high_expr); tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1); ivl_assert(*range->high_expr, tmp->high_expr); } else { @@ -136,8 +134,8 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, { bool rc_flag; assert(enum_type->range->size() == 2); - NetExpr*msb_ex = enum_type->range->front()->elaborate_expr(des, scope, -2, false); - NetExpr*lsb_ex = enum_type->range->back() ->elaborate_expr(des, scope, -2, false); + NetExpr*msb_ex = elab_and_eval(des, scope, enum_type->range->front(), -1); + NetExpr*lsb_ex = elab_and_eval(des, scope, enum_type->range->back(), -1); long msb = 0; rc_flag = eval_as_long(msb, msb_ex); @@ -162,7 +160,6 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, // 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, - use_enum->base_width(), use_enum->base_width()); NetEConst*val_const = dynamic_cast (val); if (val_const == 0) { @@ -427,7 +424,7 @@ bool Module::elaborate_scope(Design*des, NetScope*scope, // 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 ) { @@ -557,7 +554,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) // 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. - probe_expr_width(des, container, loop_init); need_constant_expr = true; NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1); need_constant_expr = false; @@ -624,7 +620,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; container->genvar_tmp_val = genvar; - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -673,7 +668,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) elaborate_subscope_(des, scope); // Calculate the step for the loop variable. - probe_expr_width(des, container, loop_step); need_constant_expr = true; NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); need_constant_expr = false; @@ -692,7 +686,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) container->genvar_tmp_val = genvar; delete step; delete test_ex; - probe_expr_width(des, container, loop_test); test_ex = elab_and_eval(des, container, loop_test, -1); test = dynamic_cast(test_ex); assert(test); @@ -707,7 +700,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else_flag) { - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -800,7 +792,6 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else bool PGenerate::generate_scope_case_(Design*des, NetScope*container) { - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -833,7 +824,6 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool match_flag = false; for (unsigned idx = 0 ; idx < item->item_test.size() && !match_flag ; idx +=1 ) { - probe_expr_width(des, container, item->item_test[idx]); need_constant_expr = true; NetExpr*item_value_ex = elab_and_eval(des, container, item->item_test[idx], -1); need_constant_expr = false; @@ -1206,8 +1196,6 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const */ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const { - if (msb_) probe_expr_width(des, sc, msb_); - if (lsb_) probe_expr_width(des, sc, lsb_); need_constant_expr = true; NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1) : 0; NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1) : 0; @@ -1387,7 +1375,7 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const assert(scope->type() == NetScope::FUNC); // Scan the parameters in the function, and store the information - // needed to evaluate the parameter expressions. + // needed to evaluate the parameter expressions. collect_scope_parameters_(des, scope, parameters); @@ -1405,7 +1393,7 @@ 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. + // needed to evaluate the parameter expressions. collect_scope_parameters_(des, scope, parameters); @@ -1482,7 +1470,7 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const my_scope->is_auto(scope->is_auto()); // Scan the parameters in the scope, and store the information - // needed to evaluate the parameter expressions. + // needed to evaluate the parameter expressions. collect_scope_parameters_(des, my_scope, parameters); diff --git a/elab_sig.cc b/elab_sig.cc index 2e70cb4a4..35312e442 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -471,8 +471,6 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const case PTF_REG_S: if (return_type_.range) { ivl_assert(*this, return_type_.range->size() == 2); - probe_expr_width(des, scope, return_type_.range->at(0)); - probe_expr_width(des, scope, return_type_.range->at(1)); need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, @@ -545,8 +543,6 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const case PTF_ATOM2: case PTF_ATOM2_S: ivl_assert(*this, return_type_.range != 0); - probe_expr_width(des, scope, (*return_type_.range)[0]); - probe_expr_width(des, scope, (*return_type_.range)[1]); long use_wid; { need_constant_expr = true; @@ -852,7 +848,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const bool bad_lsb = false, bad_msb = false; /* If they exist get the port definition MSB and LSB */ if (port_set_ && port_msb_ != 0) { - probe_expr_width(des, scope, port_msb_); /* We do not currently support constant user function. */ need_constant_expr = true; NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1); @@ -870,7 +865,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - probe_expr_width(des, scope, port_lsb_); /* We do not currently support constant user function. */ need_constant_expr = true; texpr = elab_and_eval(des, scope, port_lsb_, -1); @@ -898,7 +892,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) { - probe_expr_width(des, scope, net_msb_); /* We do not currently support constant user function. */ need_constant_expr = true; NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1); @@ -916,7 +909,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - probe_expr_width(des, scope, net_lsb_); /* We do not currently support constant user function. */ need_constant_expr = true; texpr = elab_and_eval(des, scope, net_lsb_, -1); @@ -1011,9 +1003,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (lidx_ || ridx_) { assert(lidx_ && ridx_); - probe_expr_width(des, scope, lidx_); - probe_expr_width(des, scope, ridx_); - need_constant_expr = true; NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1); NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1); @@ -1053,7 +1042,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const cerr << get_fileline() << ": error: real "; if (wtype == NetNet::REG) cerr << "variable"; else cerr << "net"; - cerr << " '" << name_ + cerr << " '" << name_ << "' cannot be declared as a vector, found a range [" << msb << ":" << lsb << "]." << endl; des->errors += 1; diff --git a/elaborate.cc b/elaborate.cc index 64dcdd7b0..dc3ccb862 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -115,6 +115,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const cerr << get_fileline() << ": debug: PGAssign: elaborated r-value" << " width="<< rval->vector_width() << ", type="<< rval->data_type() + << ", signed="<< rval->get_signed() << ", expr=" << *rval_expr << endl; } @@ -143,7 +144,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const need_driver_flag = false; } - /* If the r-value insists on being smaller then the l-value + /* If the r-value insists on being smaller than the l-value (perhaps it is explicitly sized) the pad it out to be the right width so that something is connected to all the bits of the l-value. */ @@ -156,7 +157,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const } /* If, on the other hand, the r-value insists on being - LARGER then the l-value, use a part select to chop it down + 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(), @@ -227,11 +228,6 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, gate. Figure out how many are desired. */ if (msb_) { need_constant_expr = true; - ivl_variable_type_t use_type; - bool flag = false; - msb_->test_width(des, scope, 0, 0, use_type, flag); - flag = false; - lsb_->test_width(des, scope, 0, 0, use_type, flag); NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); need_constant_expr = false; @@ -820,12 +816,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const sig = lval_sigs[idx]; } else { - unsigned use_width = array_count * instance_width; - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool flag = false; - ex->test_width(des, scope, 0, use_width, tmp_type, flag); - NetExpr*tmp = elab_and_eval(des, scope, ex, - use_width, use_width); + NetExpr*tmp = elab_and_eval(des, scope, ex, -1); sig = tmp->synthesize(des, scope, tmp); delete tmp; } @@ -1315,6 +1306,15 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if (instance.size() != 1) desired_vector_width = 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. @@ -1332,13 +1332,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const port is actually empty on the inside. We assume in that case that the port is input. */ - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool flag = false; - pins[idx]->test_width(des, scope, 0, desired_vector_width, - tmp_type, flag); - NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], - desired_vector_width, - desired_vector_width); + NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], -1); if (tmp_expr == 0) { cerr << pins[idx]->get_fileline() << ": internal error: Port expression " @@ -1411,8 +1405,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << "Inout port expression must support " << "continuous assignment." << endl; cerr << pins[idx]->get_fileline() << ": : " - << "Port of " << rmod->mod_name() - << " is " << rmod->ports[idx]->name << endl; + << "Port " << rmod->ports[idx]->name << " of " + << rmod->mod_name() << " is connected to " + << *pins[idx] << endl; des->errors += 1; continue; } @@ -1457,8 +1452,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << "Output port expression must support " << "continuous assignment." << endl; cerr << pins[idx]->get_fileline() << ": : " - << "Port of " << rmod->mod_name() - << " is " << rmod->ports[idx]->name << endl; + << "Port " << rmod->ports[idx]->name << " of " + << rmod->mod_name() << " is connected to " + << *pins[idx] << endl; des->errors += 1; continue; } @@ -1608,7 +1604,11 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << " high bits of the port." << endl; } else { - cerr << get_fileline() << ": : Padding "; + 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." @@ -1889,7 +1889,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const if (pins[idx] == 0) continue; - NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1, 1); + 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; @@ -2044,7 +2044,6 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, */ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) { - probe_expr_width(des, scope, expr); NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based @@ -2099,12 +2098,11 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) NetExpr*scal_val = new NetECReal(verireal(round)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); + dex = new NetEBMult('*', dex, scal_val, 1, true); dex->set_line(*expr); // Cast this part of the expression to an integer. - dex = new NetECast('i', dex); - dex->set_width(64); + dex = new NetECast('i', dex, 64, false); dex->set_line(*expr); // Now scale the integer value. @@ -2115,8 +2113,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); - dex->set_width(64); + dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } else { int shift = scope->time_unit() - des->get_precision(); @@ -2126,7 +2123,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) NetExpr*scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); + dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } @@ -2174,17 +2171,6 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const if (delay || event_) { unsigned wid = count_lval_width(lv); - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - - if (wid > rv->expr_width()) { - cerr << get_fileline() << ": error: Unable to match " - "expression width of " << rv->expr_width() << - " to l-value width of " << wid << "." << endl; - //XXXX delete rv; - return 0; - } - NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, wid); tmp->local_flag(true); @@ -2219,6 +2205,7 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return 0; } st = event_->elaborate(des, scope); + st->set_line(*this); if (st == 0) { cerr << event_->get_fileline() << ": error: " "unable to elaborate event expression." @@ -2244,12 +2231,15 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const // 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); + st->set_line(*this); if (st == 0) { cerr << event_->get_fileline() << ": error: " "unable to elaborate event expression." @@ -2274,22 +2264,6 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return bl; } - /* Based on the specific type of the l-value, do cleanup - processing on the r-value. */ - if (rv->expr_type() == IVL_VT_REAL) { - - // The r-value is a real. Casting will happen in the - // code generator, so leave it. - - } else { - unsigned wid = count_lval_width(lv); - if (wid > rv->expr_width()) { - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - ivl_assert(*this, rv->expr_width() >= wid); - } - if (lv->expr_type() == IVL_VT_BOOL && rv->expr_type() != IVL_VT_BOOL) { if (debug_elaborate) cerr << get_fileline() << ": debug: Cast expression to int2" << endl; @@ -2301,7 +2275,7 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const << "Enumeration type mismatch in assignment." << endl; des->errors += 1; } - + NetAssign*cur = new NetAssign(lv, rv); cur->set_line(*this); @@ -2340,20 +2314,6 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type()); if (rv == 0) return 0; - /* Handle the (common) case that the r-value is a vector. This - includes just about everything but reals. In this case, we - need to pad the r-value to match the width of the l-value. - - If in this case the l-val is a variable (i.e., real) then - the width to pad to will be 0, so this code is harmless. */ - if (rv->expr_type() == IVL_VT_REAL) { - - } else { - unsigned wid = count_lval_width(lv); - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - NetExpr*delay = 0; if (delay_ != 0) { assert(count_ == 0 && event_ == 0); @@ -2505,7 +2465,6 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const { assert(scope); - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate this case" @@ -2560,7 +2519,6 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const NetExpr*gu = 0; NetProc*st = 0; assert(cur_expr); - probe_expr_width(des, scope, cur_expr); gu = elab_and_eval(des, scope, cur_expr, -1); if (cur->stat) @@ -2583,7 +2541,6 @@ NetProc* PCondit::elaborate(Design*des, NetScope*scope) const << " with conditional: " << *expr_ << endl; // Elaborate and try to evaluate the conditional expression. - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" @@ -2699,29 +2656,12 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const svectoreparms (parm_count); + perm_string name = peek_tail_name(path_); + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { PExpr*ex = parm(idx); if (ex != 0) { - ivl_variable_type_t use_type; - bool flag = false; - int use_wid = ex->test_width(des,scope,0,0, use_type, flag); - if (debug_elaborate) - cerr << ex->get_fileline() << ": debug: " - << "Argument " << (idx+1) - << " of system task tests its width as " << use_wid - << ", type=" << use_type - << ", unsized_flag=" << flag << endl; - - // If the argument expression is unsized, then - // elaborate as self-determined *lossless* instead - // of sized. - if (flag==true) - use_wid = -2; - - eparms[idx] = ex->elaborate_expr(des, scope, use_wid, true); - if (eparms[idx]) - eval_expr(eparms[idx]); - + eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); } else { eparms[idx] = 0; } @@ -2731,8 +2671,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const // $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 - && peek_tail_name(path_) == "$sdf_annotate") { + if (gn_specify_blocks_flag == false && name == "$sdf_annotate") { cerr << get_fileline() << ": warning: Omitting $sdf_annotate() " << "since specify blocks are being omitted." << endl; @@ -2741,8 +2680,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const return noop; } - NetSTask*cur = new NetSTask(peek_tail_name(path_), def_sfunc_as_task, - eparms); + NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms); cur->set_line(*this); return cur; } @@ -2869,11 +2807,6 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const des->errors += 1; continue; } - if (wid > rv->expr_width()) { - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - ivl_assert(*this, rv->expr_width() >= wid); NetAssign*pr = new NetAssign(lv, rv); pr->set_line(*this); @@ -2986,9 +2919,6 @@ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const if (rexp == 0) return 0; - rexp->set_width(lwid); - rexp = pad_to_width(rexp, lwid, *this); - dev = new NetCAssign(lval, rexp); if (debug_elaborate) { @@ -3304,8 +3234,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, } } - probe_expr_width(des, scope, expr_[idx]->expr()); - NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), 0); + NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), -1); if (tmp == 0) { expr_[idx]->dump(cerr); cerr << endl; @@ -3407,8 +3336,9 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, /* Elaborate wait expression. Don't eval yet, we will do that shortly, after we apply a reduction or. */ - probe_expr_width(des, scope, pe); - NetExpr*expr = pe->elaborate_expr(des, scope, -1, false); + PExpr::width_mode_t mode; + pe->test_width(des, scope, mode); + NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), false); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " wait condition expression." << endl; @@ -3570,7 +3500,7 @@ NetProc* PForever::elaborate(Design*des, NetScope*scope) const * force = * * The can be anything that a normal behavioral assignment can - * take, plus net signals. This is a little bit more lax then the + * take, plus net signals. This is a little bit more lax than the * other procedural assignments. */ NetForce* PForce::elaborate(Design*des, NetScope*scope) const @@ -3605,9 +3535,6 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const if (rexp == 0) return 0; - rexp->set_width(lwid, true); - rexp = pad_to_width(rexp, lwid, *this); - if (ltype==IVL_VT_BOOL && rexp->expr_type()!=IVL_VT_BOOL) { if (debug_elaborate) { cerr << get_fileline() << ": debug: " @@ -3668,32 +3595,14 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const assert(sig); NetAssign_*lv = new NetAssign_(sig); - /* Calculate the width of the initialization as if this were - any other assignment statement. */ - unsigned use_width = lv->lwidth(); - bool unsized_flag = false; - ivl_variable_type_t expr1_type = IVL_VT_NO_TYPE; - use_width = expr1_->test_width(des, scope, use_width, use_width, expr1_type, unsized_flag); - /* Make the r-value of the initial assignment, and size it properly. Then use it to build the assignment statement. */ - etmp = elab_and_eval(des, scope, expr1_, use_width); - etmp->set_width(use_width); - etmp = pad_to_width(etmp, use_width, *this); + etmp = elaborate_rval_expr(des, scope, lv->expr_type(), lv->lwidth(), + expr1_); if (debug_elaborate) { cerr << get_fileline() << ": debug: FOR initial assign: " << sig->name() << " = " << *etmp << endl; - assert(etmp->expr_width() >= lv->lwidth()); - } - - /* Based on the specific type of the l-value, do cleanup - processing on the r-value. */ - if (etmp->expr_type() != IVL_VT_REAL) { - unsigned wid = count_lval_width(lv); - etmp->set_width(wid); - etmp = pad_to_width(etmp, wid, *this); - assert(etmp->expr_width() >= wid); } NetAssign*init = new NetAssign(lv, etmp); @@ -3727,9 +3636,16 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const assert(sig); lv = new NetAssign_(sig); - /* Make the rvalue of the increment expression, and size it - for the lvalue. */ - etmp = elab_and_eval(des, scope, expr2_, lv->lwidth()); + /* Make the r-value of the increment assignment, and size it + properly. Then use it to build the assignment statement. */ + etmp = elaborate_rval_expr(des, scope, lv->expr_type(), lv->lwidth(), + expr2_); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: FOR increment assign: " + << sig->name() << " = " << *etmp << endl; + } + NetAssign*step = new NetAssign(lv, etmp); step->set_line(*this); @@ -3739,7 +3655,6 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const /* Elaborate the condition expression. Try to evaluate it too, in case it is a constant. This is an interesting case worthy of a warning. */ - probe_expr_width(des, scope, cond_); NetExpr*ce = elab_and_eval(des, scope, cond_, -1); if (ce == 0) { delete top; @@ -3830,7 +3745,6 @@ NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const { assert(scope); - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": Unable to elaborate" @@ -3954,9 +3868,10 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const */ NetProc* PWhile::elaborate(Design*des, NetScope*scope) const { - probe_expr_width(des, scope, cond_); NetExpr*tmp = elab_and_eval(des, scope, cond_, -1); + tmp->set_line(*this); NetWhile*loop = new NetWhile(tmp, statement_->elaborate(des, scope)); + loop->set_line(*this); return loop; } @@ -4057,8 +3972,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const them for the timescale/precision of the scope. */ for (unsigned idx = 0 ; idx < ndelays ; idx += 1) { PExpr*exp = delays[idx]; - probe_expr_width(des, scope, exp); - NetExpr*cur = elab_and_eval(des, scope, exp, 0); + NetExpr*cur = elab_and_eval(des, scope, exp, -1); if (NetEConst*con = dynamic_cast (cur)) { verinum fn = con->value(); @@ -4096,7 +4010,6 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const NetNet*condit_sig = 0; if (conditional && condition) { - probe_expr_width(des, scope, condition); NetExpr*tmp = elab_and_eval(des, scope, condition, -1); ivl_assert(*condition, tmp); @@ -4253,7 +4166,6 @@ bool Module::elaborate(Design*des, NetScope*scope) const for (specparam_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { - probe_expr_width(des, scope, (*cur).second); need_constant_expr = true; NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1); need_constant_expr = false; diff --git a/elaborate_analog.cc b/elaborate_analog.cc index 5d538b98d..25cbdd4e6 100644 --- a/elaborate_analog.cc +++ b/elaborate_analog.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008,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 @@ -28,8 +28,6 @@ NetProc* AContrib::elaborate(Design*des, NetScope*scope) const { - probe_expr_width(des, scope, lval_); - probe_expr_width(des, scope, rval_); NetExpr*lval = elab_and_eval(des, scope, lval_, -1); NetExpr*rval = elab_and_eval(des, scope, rval_, -1); diff --git a/emit.cc b/emit.cc index 8148dc1dd..8411489a8 100644 --- a/emit.cc +++ b/emit.cc @@ -409,6 +409,7 @@ void NetScope::emit_scope(struct target_t*tgt) const tgt->signal_paths(cur->second); } + if (type_ == MODULE) tgt->convert_module_ports(this); } bool NetScope::emit_defs(struct target_t*tgt) const diff --git a/eval_tree.cc b/eval_tree.cc index bb37e1401..5fc9831c2 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -29,7 +29,7 @@ # include "ivl_assert.h" # include "netmisc.h" -NetExpr* NetExpr::eval_tree(int) +NetExpr* NetExpr::eval_tree() { return 0; } @@ -114,12 +114,12 @@ NetECReal* NetEBAdd::eval_tree_real_() return res; } -NetExpr* NetEBAdd::eval_tree(int prune_to_width) +NetExpr* NetEBAdd::eval_tree() { - eval_expr(left_, prune_to_width); - eval_expr(right_, prune_to_width); + eval_expr(left_); + eval_expr(right_); - if (left_->expr_type() == IVL_VT_REAL || right_->expr_type()==IVL_VT_REAL) + if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); NetEConst*lc = dynamic_cast(left_); @@ -131,33 +131,23 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) 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 = lval + rval; + val = verinum(lval + rval, wid); break; case '-': - val = lval - rval; + val = verinum(lval - rval, wid); break; default: return 0; } - /* Result might have known width. */ - if (has_width()) { - unsigned lwid = lc->expr_width(); - unsigned rwid = rc->expr_width(); - unsigned wid = (rwid > lwid) ? rwid : lwid; - if (prune_to_width < 0) - wid += 1; - verinum val2=verinum(val,wid); - val=val2; - } else { - /* No fixed width, so trim the bits losslessly. */ - verinum val2 = trim_vnum(val); - val = val2; - } - NetEConst *res = new NetEConst(val); ivl_assert(*this, res); res->set_line(*this); @@ -188,37 +178,20 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - if (lval.len() < expr_width()) - lval = pad_to_width(lval, expr_width()); - if (rval.len() < expr_width()) - rval = pad_to_width(rval, expr_width()); - - if (se->expr_width() > this->expr_width()) { - cerr << get_fileline() << ": internal error: " - << "expr_width()=" << expr_width() - << ", sub expr_width()=" << se->expr_width() - << ", sub expression=" << *se << endl; - } - ivl_assert(*this, se->expr_width() <= this->expr_width()); + 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 = rval + lval; + val = verinum(rval + lval, wid); } else { /* (a - lval) + rval --> a + (rval-lval) */ /* (a + lval) - rval --> a - (rval-lval) */ - val = rval - lval; - } - - // Since we padded the operands above to be the minimum - // width, the val should also be at least expr_width(). - ivl_assert(*this, val.len() >= expr_width()); - if (val.len() > expr_width()) { - verinum tmp (val, expr_width()); - tmp.has_sign(val.has_sign()); - val = tmp; + val = verinum(rval - lval, wid); } NetEConst*tmp = new NetEConst(val); @@ -236,15 +209,15 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) return 0; } -NetEConst* NetEBBits::eval_tree(int prune_to_width) +NetEConst* NetEBBits::eval_tree() { if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } - eval_expr(left_, prune_to_width); - eval_expr(right_, prune_to_width); + eval_expr(left_); + eval_expr(right_); NetEConst*lc = dynamic_cast(left_); NetEConst*rc = dynamic_cast(right_); @@ -266,79 +239,33 @@ NetEConst* NetEBBits::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - unsigned lwid = lc->expr_width(); - if (lwid == 0) lwid = lval.len(); - - unsigned rwid = rc->expr_width(); - if (rwid == 0) rwid = rval.len(); - unsigned wid = expr_width(); - if (wid == 0) - wid = (rwid > lwid)? rwid : lwid; + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); verinum res (verinum::V0, wid); - if (lwid > wid) - lwid = wid; - if (rwid > wid) - rwid = wid; - - // Sub-expressions of bitwise operators need to be the same - // width. Pad them out if necessary. - if (lwid < wid) { - lval = pad_to_width(lval, wid); - lwid = wid; - } - if (rwid < wid) { - rval = pad_to_width(rval, wid); - rwid = wid; - } - switch (op()) { case '|': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) | rval.get(idx)); - if (lwid < rwid) - for (unsigned idx = lwid ; idx < rwid ; idx += 1) - res.set(idx, rval.get(idx)); - - if (rwid < lwid) - for (unsigned idx = rwid ; idx < lwid ; idx += 1) - res.set(idx, lval.get(idx)); - break; } case '&': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) & rval.get(idx)); break; } case '^': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) ^ rval.get(idx)); - if (lwid < rwid) - for (unsigned idx = lwid ; idx < rwid ; idx += 1) - res.set(idx, rval.get(idx)); - - if (rwid < lwid) - for (unsigned idx = rwid ; idx < lwid ; idx += 1) - res.set(idx, lval.get(idx)); - break; } @@ -761,7 +688,7 @@ NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag) return result; } -NetEConst* NetEBComp::eval_tree(int) +NetEConst* NetEBComp::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -850,11 +777,8 @@ NetExpr* NetEBDiv::eval_tree_real_() * The NetEBDiv operator includes the / and % operators. First evaluate * the sub-expressions, then perform the required operation. */ -NetExpr* NetEBDiv::eval_tree(int prune_to_width) +NetExpr* NetEBDiv::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -865,20 +789,26 @@ NetExpr* NetEBDiv::eval_tree(int prune_to_width) NetEConst*rc = dynamic_cast(right_); if (lc == 0 || rc == 0) return 0; - // Make sure the expression is evaluated at the - // expression width. - verinum lval = pad_to_width(lc->value(), expr_width()); - verinum rval = pad_to_width(rc->value(), expr_width()); + verinum lval = lc->value(); + verinum rval = rc->value(); - NetExpr*tmp = 0; + 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 '/': - tmp = new NetEConst(lval / rval); + val = verinum(lval / rval, wid); break; case '%': - tmp = new NetEConst(lval % rval); + val = verinum(lval % rval, wid); break; + default: + return 0; } + NetExpr*tmp = new NetEConst(val); ivl_assert(*this, tmp); tmp->set_line(*this); @@ -928,7 +858,7 @@ NetEConst* NetEBLogic::eval_tree_real_() return tmp; } -NetEConst* NetEBLogic::eval_tree(int) +NetEConst* NetEBLogic::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -1024,11 +954,8 @@ NetExpr* NetEBMult::eval_tree_real_() return res; } -NetExpr* NetEBMult::eval_tree(int prune_to_width) +NetExpr* NetEBMult::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -1042,7 +969,13 @@ NetExpr* NetEBMult::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - NetEConst*tmp = new NetEConst(lval * rval); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + + verinum val(lval * rval, wid); + NetEConst*tmp = new NetEConst(val); ivl_assert(*this, tmp); tmp->set_line(*this); @@ -1072,11 +1005,8 @@ NetExpr* NetEBPow::eval_tree_real_() return res; } -NetExpr* NetEBPow::eval_tree(int prune_to_width) +NetExpr* NetEBPow::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -1090,7 +1020,12 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - NetEConst*res = new NetEConst( pow(lval,rval) ); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + + verinum val(pow(lval, rval), wid); + NetEConst*res = new NetEConst(val); ivl_assert(*this, res); res->set_line(*this); @@ -1105,7 +1040,7 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) * Evaluate the shift operator if possible. For this to work, both * operands must be constant. */ -NetEConst* NetEBShift::eval_tree(int prune_to_width) +NetEConst* NetEBShift::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -1116,87 +1051,44 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) NetEConst*res; - verinum rv = re->value(); verinum lv = le->value(); + verinum rv = re->value(); - /* Make an early estimate of the expression width. */ 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_ulong(); - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: " - << "Evaluate " << lv << "<<" << op() << ">> " - << rv << ", wid=" << wid << ", shift=" << shift - << ", lv.has_len()=" << lv.has_len() << endl; + switch (op_) { + case 'l': + val = verinum(lv << shift, wid); + break; + case 'r': + lv.has_sign(false); + case 'R': + val = verinum(lv >> shift, wid); + break; + default: + return 0; } - - if ((wid == 0) || ! lv.has_len()) { - /* If the caller doesn't care what the width is, - then calculate a width from the trimmed left - expression, plus the shift. This avoids - data loss. */ - lv = trim_vnum(lv); - wid = lv.len(); - if (op() == 'l') - wid = lv.len() + shift; - } - - if (prune_to_width > 0 && wid > (unsigned)prune_to_width) - wid = prune_to_width; - - assert(wid > 0); - verinum::V pad = verinum::V0; - if (op() == 'R' && has_sign()) { - pad = lv[lv.len()-1]; - } - verinum nv (pad, wid, lv.has_len()); - - if (op() == 'r' || op() == 'R') { - unsigned cnt = wid; - if (cnt > nv.len()) - cnt = nv.len(); - if (shift >= lv.len()) - cnt = 0; - else if (cnt > (lv.len()-shift)) - cnt = (lv.len()-shift); - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - nv.set(idx, lv[idx+shift]); - - } else { - unsigned cnt = wid; - if (cnt > lv.len()) - cnt = lv.len(); - if (shift >= nv.len()) - cnt = 0; - else if (cnt > (nv.len()-shift)) - cnt = nv.len() - shift; - - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - nv.set(idx+shift, lv[idx]); - } - - res = new NetEConst(nv); - } else { - if (wid == 0) wid = left_->expr_width(); - - if (prune_to_width > 0 && wid > (unsigned)prune_to_width) - wid = prune_to_width; - - assert(wid > 0); - verinum nv (verinum::Vx, wid); - res = new NetEConst(nv); + val = verinum(verinum::Vx, wid); } - + val.has_sign(has_sign()); + res = new NetEConst(val); res->set_line(*this); + if (debug_eval_tree) + cerr << get_fileline() << ": debug: Evaluated: " << *this + << " --> " << *res << endl; + return res; } -NetEConst* NetEConcat::eval_tree(int prune_to_width) +NetEConst* NetEConcat::eval_tree() { // HERE unsigned repeat_val = repeat(); @@ -1204,7 +1096,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } unsigned gap = 0; @@ -1225,7 +1117,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) // that is here. If I succeed, reset the parameter to // the evaluated value. assert(parms_[idx]); - NetExpr*expr = parms_[idx]->eval_tree(0); + NetExpr*expr = parms_[idx]->eval_tree(); if (expr) { expr->set_line(*parms_[idx]); delete parms_[idx]; @@ -1285,16 +1177,15 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) val.has_sign( this->has_sign() ); NetEConst*res = new NetEConst(val); - res->set_width(val.len()); return res; } -NetEConst* NetESelect::eval_tree(int prune_to_width) +NetEConst* NetESelect::eval_tree() { // HERE if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } eval_expr(expr_); @@ -1318,14 +1209,12 @@ NetEConst* NetESelect::eval_tree(int prune_to_width) verinum::V pad_bit = verinum::Vx; if (base_ == 0) { - /* If the base is NULL (different from 0) the this - select is here for sign extension. So calculate a - proper pad bit. Extend x or z or 0, and sign extend 1 - if this is signed. */ - unsigned top = expr->expr_width()-1; - - pad_bit = eval.get(top); - if (pad_bit==verinum::V1 && !has_sign()) + /* 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; } @@ -1365,12 +1254,12 @@ static void print_ternary_cond(NetExpr*expr) * evaluates to x or z, then merge the constant bits of the true and * false expressions. */ -NetExpr* NetETernary::eval_tree(int prune_to_width) +NetExpr* NetETernary::eval_tree() { eval_expr(cond_); switch (const_logical(cond_)) { case C_0: - eval_expr(false_val_, prune_to_width); + eval_expr(false_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " @@ -1393,7 +1282,7 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) return false_val_->dup_expr(); case C_1: - eval_expr(true_val_, prune_to_width); + eval_expr(true_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; @@ -1425,8 +1314,8 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) expressions down to constants then compare the values to build up a constant result. */ - eval_expr(true_val_, prune_to_width); - eval_expr(false_val_, prune_to_width); + eval_expr(true_val_); + eval_expr(false_val_); NetEConst*t = dynamic_cast(true_val_); NetEConst*f = dynamic_cast(false_val_); @@ -1509,7 +1398,7 @@ NetExpr* NetEUnary::eval_tree_real_() return res; } -NetExpr* NetEUnary::eval_tree(int prune_to_width) +NetExpr* NetEUnary::eval_tree() { eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -1529,7 +1418,7 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) if (val.is_defined()) { verinum tmp (verinum::V0, val.len()); tmp.has_sign(val.has_sign()); - val = tmp - val; + val = verinum(tmp - val, val.len()); } else { for (unsigned idx = 0 ; idx < val.len() ; idx += 1) val.set(idx, verinum::Vx); @@ -1537,7 +1426,7 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) break; case '~': - /* Bitwise not is even simpler then logical + /* 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) @@ -1572,9 +1461,9 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) } -NetExpr* NetEUBits::eval_tree(int prune_to_width) +NetExpr* NetEUBits::eval_tree() { - return NetEUnary::eval_tree(prune_to_width); + return NetEUnary::eval_tree(); } NetEConst* NetEUReduce::eval_tree_real_() @@ -1598,7 +1487,7 @@ NetEConst* NetEUReduce::eval_tree_real_() return tmp; } -NetEConst* NetEUReduce::eval_tree(int) +NetEConst* NetEUReduce::eval_tree() { eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -1955,9 +1844,8 @@ static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, return res; } -NetExpr* NetESFunc::eval_tree(int prune_to_width) +NetExpr* NetESFunc::eval_tree() { -// assert(prune_to_width <= 0); // HERE /* If we are not targeting at least Verilog-2005, Verilog-AMS * or using the Icarus misc flag then we do not support these @@ -2056,7 +1944,7 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width) return rtn; } -NetExpr* NetEUFunc::eval_tree(int) +NetExpr* NetEUFunc::eval_tree() { if (need_constant_expr) { cerr << get_fileline() << ": sorry: constant user " diff --git a/ieee1364-notes.txt b/ieee1364-notes.txt index 1f7c5f254..72b21405b 100644 --- a/ieee1364-notes.txt +++ b/ieee1364-notes.txt @@ -285,12 +285,12 @@ One might note that the quote from section 4.1.14 says "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 then (16) +intent, and accepting (15+1) as having a more defined size taen (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 then (16). Therefore, Icarus Verilog takes the +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 diff --git a/ivl.def b/ivl.def index 801c966e4..d44386270 100644 --- a/ivl.def +++ b/ivl.def @@ -68,6 +68,7 @@ ivl_expr_parm ivl_expr_parms ivl_expr_repeat ivl_expr_scope +ivl_expr_sel_type ivl_expr_signal ivl_expr_signed ivl_expr_sized @@ -134,6 +135,7 @@ ivl_lpm_width ivl_lval_idx ivl_lval_mux ivl_lval_part_off +ivl_lval_sel_type ivl_lval_sig ivl_lval_width @@ -187,6 +189,7 @@ ivl_scope_logs ivl_scope_log ivl_scope_lpms ivl_scope_lpm +ivl_scope_mod_port ivl_scope_name ivl_scope_param ivl_scope_params @@ -288,6 +291,7 @@ ivl_udp_file ivl_udp_lineno ivl_udp_name ivl_udp_nin +ivl_udp_port ivl_udp_row ivl_udp_rows ivl_udp_sequ diff --git a/ivl_target.h b/ivl_target.h index deb046635..cba9aa05a 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -229,6 +229,12 @@ typedef enum ivl_expr_type_e { IVL_EX_UNARY = 14 } ivl_expr_type_t; +typedef enum ivl_select_type_e { + 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, @@ -844,6 +850,8 @@ extern ivl_expr_t ivl_expr_parm(ivl_expr_t net, unsigned idx); 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_SCOPE */ @@ -1040,6 +1048,7 @@ 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); @@ -1408,6 +1417,7 @@ extern unsigned ivl_lval_width(ivl_lval_t net); extern ivl_expr_t ivl_lval_mux(ivl_lval_t net); /* 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 ivl_signal_t ivl_lval_sig(ivl_lval_t net); @@ -1702,6 +1712,7 @@ 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_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); diff --git a/lexor.lex b/lexor.lex index 59f5b7289..56791672c 100644 --- a/lexor.lex +++ b/lexor.lex @@ -979,9 +979,9 @@ verinum* make_undef_highz_dec(const char* ptr) } /* - * Making a decimal number is much easier then the other base numbers + * 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 then other base numbers because the width needed in bits is + * harder than other base numbers because the width needed in bits is * hard to calculate. */ diff --git a/macosx.txt b/macosx.txt index 8fad41c76..5e58f253e 100644 --- a/macosx.txt +++ b/macosx.txt @@ -34,7 +34,7 @@ quite similar. 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 then + then just make sure the distributed lexor_keyword.cc is newer than lexor_keyword.gperf, and use that. 3) If working with a CVS snapshot, you must run autoconf in several diff --git a/net_assign.cc b/net_assign.cc index 48e3adc39..0ea1a2b2e 100644 --- a/net_assign.cc +++ b/net_assign.cc @@ -77,6 +77,11 @@ const NetExpr* NetAssign_::get_base() const return base_; } +ivl_select_type_t NetAssign_::select_type() const +{ + return sel_type_; +} + unsigned NetAssign_::lwidth() const { return lwid_; @@ -120,10 +125,12 @@ NetNet* NetAssign_::sig() const return sig_; } -void NetAssign_::set_part(NetExpr*base, unsigned wid) +void NetAssign_::set_part(NetExpr*base, unsigned wid, + ivl_select_type_t sel_type) { base_ = base; lwid_ = wid; + sel_type_ = sel_type; } /* diff --git a/net_design.cc b/net_design.cc index 7dab97b6f..31296aa63 100644 --- a/net_design.cc +++ b/net_design.cc @@ -309,7 +309,7 @@ void NetScope::run_defparams_later(Design*des) (*cur)->evaluate_parameters(des); // If there are some scopes that still have missing scopes, - // then sav them back into the defparams_later list for a + // then save them back into the defparams_later list for a // later pass. defparams_later = defparams_even_later; if (! defparams_later.empty()) @@ -332,8 +332,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the msb expression, if it is present. */ PExpr*msb_expr = (*cur).second.msb_expr; if (msb_expr) { - probe_expr_width(des, this, msb_expr); - (*cur).second.msb = elab_and_eval(des, this, msb_expr, -2); + (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1); if (! eval_as_long(msb, (*cur).second.msb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate msb expression " @@ -349,8 +348,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the lsb expression, if it is present. */ PExpr*lsb_expr = (*cur).second.lsb_expr; if (lsb_expr) { - probe_expr_width(des, this, lsb_expr); - (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -2); + (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1); if (! eval_as_long(lsb, (*cur).second.lsb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate lsb expression " @@ -367,26 +365,12 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - unsigned lval_wid = 0; + int lv_width = -2; if (range_flag) - lval_wid = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; + lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; - bool unsized_flag = false; - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = val_expr->test_width(des, val_scope, lval_wid, lval_wid, - rval_type, unsized_flag); - - if (unsized_flag && !range_flag) - expr_wid = -1; - - int prune_wid = -1; - if (gn_strict_expr_width_flag) - prune_wid = 0; - if (range_flag) - prune_wid = lval_wid; - - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, expr_wid, prune_wid); - if (! expr) + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width); + if (! expr) return; switch (expr->expr_type()) { @@ -427,7 +411,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) if (range_flag) { /* If we have a real value convert it to an integer. */ if(NetECReal*tmp = dynamic_cast(expr)) { - verinum nval(tmp->value().as_long64(), lval_wid); + verinum nval(tmp->value().as_long64(), (unsigned)lv_width); expr = new NetEConst(nval); expr->set_line(*((*cur).second.val)); (*cur).second.val = expr; @@ -504,19 +488,8 @@ 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; - bool unsized_flag = false; - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = val_expr->test_width(des, val_scope, 0, 0, - rval_type, unsized_flag); - if (unsized_flag) - expr_wid = -1; - - int prune_wid = -1; - if (gn_strict_expr_width_flag) - prune_wid = 0; - - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, expr_wid, prune_wid); - if (! expr) + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1); + if (! expr) return; NetECReal*res = 0; diff --git a/net_expr.cc b/net_expr.cc index b31bf1c45..b632693fa 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -38,92 +38,17 @@ netenum_t*NetExpr::enumeration() const } /* - * Create an add/sub node from the two operands. Make a best guess of - * the + * Create an add/sub node from the two operands. */ -NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, bool lossless_flag) -: NetEBinary(op__, l, r) +NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - NetEConst* tmp; - - /* Catch the special case that one of the operands is an - unsized constant number. If so, then we should set the - width of that number to the size of the other operand, plus - one. This expands the expression to account for the largest - possible result. - - Remember to handle the special case of an unsized constant, - which we define to be at least "integer_width" bits. - - The set_width applied to a constant value will only - truncate the constant so far as it can still hold its - logical value, so this is safe to do. */ - if ( (tmp = dynamic_cast(r)) - && (! tmp->has_width()) - && (tmp->expr_width() > l->expr_width() || integer_width > l->expr_width()) ) { - - verinum tmp_v = trim_vnum(tmp->value()); - unsigned target_width = l->expr_width(); - if (target_width < tmp_v.len()) - target_width = tmp_v.len(); - if (lossless_flag) - target_width += 1; - if (target_width < integer_width) - target_width = integer_width; - - r->set_width(target_width); - - /* Note: This constant value will not gain a defined - width from this. Make sure. */ - assert(! r->has_width() ); - - expr_width(target_width); - - } else if ( (tmp = dynamic_cast(l)) - && (! tmp->has_width()) - && (tmp->expr_width() > r->expr_width() || integer_width > r->expr_width()) ) { - - verinum tmp_v = trim_vnum(tmp->value()); - unsigned target_width = r->expr_width(); - if (target_width < tmp_v.len()) - target_width = tmp_v.len(); - if (lossless_flag) - target_width += 1; - if (target_width < integer_width) - target_width = integer_width; - - l->set_width(target_width); - - /* Note: This constant value will not gain a defined - width from this. Make sure. */ - assert(! l->has_width() ); - - expr_width(target_width); - - } else if (r->expr_width() > l->expr_width()) { - unsigned loss_pad = lossless_flag? 1 : 0; - expr_width(r->expr_width() + loss_pad); - - } else { - unsigned loss_pad = lossless_flag? 1 : 0; - expr_width(l->expr_width() + loss_pad); - } - - cast_signed(l->has_sign() && r->has_sign()); } NetEBAdd::~NetEBAdd() { } -NetEBAdd* NetEBAdd::dup_expr() const -{ - NetEBAdd*result = new NetEBAdd(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBAdd::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -137,50 +62,10 @@ ivl_variable_type_t NetEBAdd::expr_type() const /* * Create a comparison operator with two sub-expressions. - * - * Handle the special case of an unsized constant on the left or right - * side by resizing the number to match the other - * expression. Otherwise, the netlist will have to allow the - * expressions to have different widths. */ NetEBComp::NetEBComp(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +: NetEBinary(op__, l, r, 1, false) { - // The output of compare is always unsigned. - cast_signed_base_(false); - - if (NetEConst*tmp = dynamic_cast(r)) do { - - if (tmp->has_width()) - break; - - if (l->expr_width() == 0) - break; - - if (tmp->expr_width() == l->expr_width()) - break; - - tmp->set_width(l->expr_width()); - - } while (0); - - if (NetEConst*tmp = dynamic_cast(l)) do { - - if (tmp->has_width()) - break; - - if (r->expr_width() == 0) - break; - - if (tmp->expr_width() == r->expr_width()) - break; - - tmp->set_width(r->expr_width()); - - } while (0); - - - expr_width(1); } NetEBComp::~NetEBComp() @@ -207,29 +92,15 @@ ivl_variable_type_t NetEBComp::expr_type() const return IVL_VT_BOOL; } -NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - unsigned w = l->expr_width(); - if (r->expr_width() > w) - w = r->expr_width(); - - expr_width(w); - cast_signed(l->has_sign() && r->has_sign()); } NetEBDiv::~NetEBDiv() { } -NetEBDiv* NetEBDiv::dup_expr() const -{ - NetEBDiv*result = new NetEBDiv(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBDiv::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -241,11 +112,9 @@ ivl_variable_type_t NetEBDiv::expr_type() const return IVL_VT_LOGIC; } -NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - expr_width( max(l->expr_width(), r->expr_width()) ); - cast_signed(l->has_sign() || r->has_sign()); } NetEBMinMax::~NetEBMinMax() @@ -262,30 +131,15 @@ ivl_variable_type_t NetEBMinMax::expr_type() const return IVL_VT_LOGIC; } -NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - if (expr_type() == IVL_VT_REAL) { - expr_width(1); - cast_signed(true); - } else { - expr_width(l->expr_width() + r->expr_width()); - cast_signed(l->has_sign() && r->has_sign()); - } } NetEBMult::~NetEBMult() { } -NetEBMult* NetEBMult::dup_expr() const -{ - NetEBMult*result = new NetEBMult(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBMult::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -297,27 +151,15 @@ ivl_variable_type_t NetEBMult::expr_type() const return IVL_VT_LOGIC; } -NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - assert(op__ == 'p'); - /* You could need up to a * (2^b - 1) bits. */ - expr_width(l->expr_width()); - cast_signed(l->has_sign() || r->has_sign()); } NetEBPow::~NetEBPow() { } -NetEBPow* NetEBPow::dup_expr() const -{ - NetEBPow*result = new NetEBPow(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBPow::expr_type() const { if (right_->expr_type() == IVL_VT_REAL) @@ -328,13 +170,9 @@ ivl_variable_type_t NetEBPow::expr_type() const return IVL_VT_LOGIC; } -NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - expr_width(l->expr_width()); - - // The >>> is signed if the left operand is signed. - if (op__ == 'R') cast_signed(l->has_sign()); } NetEBShift::~NetEBShift() @@ -346,24 +184,9 @@ bool NetEBShift::has_width() const return left_->has_width(); } -NetEBShift* NetEBShift::dup_expr() const -{ - NetEBShift*result = new NetEBShift(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - -NetEConcat::NetEConcat(unsigned cnt, NetExpr* r) +NetEConcat::NetEConcat(unsigned cnt, unsigned r) : parms_(cnt), repeat_(r) { - if (repeat_ == 0) { - repeat_calculated_ = true; - repeat_value_ = 1; - } else { - repeat_calculated_ = false; - } - expr_width(0); } @@ -383,63 +206,7 @@ void NetEConcat::set(unsigned idx, NetExpr*e) assert(idx < parms_.count()); assert(parms_[idx] == 0); parms_[idx] = e; - expr_width( expr_width() + e->expr_width() ); -} - -NetEConcat* NetEConcat::dup_expr() const -{ - NetEConcat*dup = new NetEConcat(parms_.count(), 0); - dup->set_line(*this); - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) - if (parms_[idx]) { - NetExpr*tmp = parms_[idx]->dup_expr(); - assert(tmp); - dup->parms_[idx] = tmp; - } - - - dup->repeat_ = repeat_? repeat_->dup_expr() : 0; - dup->repeat_value_ = repeat_value_; - dup->repeat_calculated_ = repeat_calculated_; - dup->expr_width(expr_width()); - - return dup; -} - -unsigned NetEConcat::repeat() -{ - if (repeat_calculated_) - return repeat_value_; - - eval_expr(repeat_); - - NetEConst*repeat_const = dynamic_cast(repeat_); - - /* This should not be possible, as it was checked earlier to - assure that this is a constant expression. */ - if (repeat_const == 0) { - cerr << get_fileline() << ": internal error: repeat expression " - << "is not a compile time constant." << endl; - cerr << get_fileline() << ": : Expression is: " - << *repeat_ << endl; - repeat_calculated_ = true; - repeat_value_ = 1; - return 1; - } - - repeat_calculated_ = true; - repeat_value_ = repeat_const->value().as_ulong(); - - delete repeat_; - repeat_ = 0; - - return repeat_value_; -} - -unsigned NetEConcat::repeat() const -{ - assert(repeat_calculated_); - return repeat_value_; + expr_width( expr_width() + repeat_ * e->expr_width() ); } NetEConstEnum::NetEConstEnum(NetScope*s, perm_string n, netenum_t*eset, const verinum&v) @@ -461,7 +228,7 @@ NetECReal::NetECReal(const verireal&val) : value_(val) { expr_width(1); - cast_signed(true); + cast_signed_base_(true); } NetECReal::~NetECReal() @@ -475,14 +242,7 @@ const verireal& NetECReal::value() const bool NetECReal::has_width() const { - return false; -} - -NetECReal* NetECReal::dup_expr() const -{ - NetECReal*tmp = new NetECReal(value_); - tmp->set_line(*this); - return tmp; + return true; } ivl_variable_type_t NetECReal::expr_type() const @@ -524,8 +284,9 @@ netenum_t* NetENetenum::netenum() const return netenum_; } -NetESelect::NetESelect(NetExpr*exp, NetExpr*base, unsigned wid) -: expr_(exp), base_(base) +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); } @@ -546,6 +307,11 @@ const NetExpr*NetESelect::select() const return base_; } +ivl_select_type_t NetESelect::select_type() const +{ + return sel_type_; +} + bool NetESelect::has_width() const { return true; diff --git a/net_proc.cc b/net_proc.cc index 103deeb7e..83487dd91 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -100,8 +100,6 @@ void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p) assert(idx < nitems_); items_[idx].guard = e; items_[idx].statement = p; - if (items_[idx].guard) - items_[idx].guard->set_width(expr_->expr_width()); } NetDisable::NetDisable(NetScope*tgt) @@ -169,4 +167,3 @@ const NetExpr* NetRepeat::expr() const { return expr_; } - diff --git a/net_scope.cc b/net_scope.cc index f040b038a..50f2d646f 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -359,6 +359,25 @@ perm_string NetScope::module_name() const return module_name_; } +void NetScope::add_module_port(NetNet*port) +{ + assert(type_ == MODULE); + ports_.push_back(port); +} + +unsigned NetScope::module_ports() const +{ + assert(type_ == MODULE); + return ports_.size(); +} + +NetNet* NetScope::module_port(unsigned idx) const +{ + assert(type_ == MODULE); + assert(idx < ports_.size()); + return ports_[idx]; +} + void NetScope::time_unit(int val) { time_unit_ = val; diff --git a/net_udp.cc b/net_udp.cc index a2d7aa3d0..e9ca4e0bd 100644 --- a/net_udp.cc +++ b/net_udp.cc @@ -79,3 +79,14 @@ char NetUDP::get_initial() const 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]; +} diff --git a/netlist.cc b/netlist.cc index 05e3899a0..6af72c71d 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2095,21 +2095,9 @@ NetExpr::~NetExpr() { } -bool NetExpr::has_sign() const -{ - return signed_flag_; -} - void NetExpr::cast_signed(bool flag) { - signed_flag_ = flag; -} - -void NetExpr::expr_width(unsigned w) -{ - // Catch underflow wrap errors. - ivl_assert(*this, w < (UINT_MAX - 256)); - width_ = w; + cast_signed_base_(flag); } bool NetExpr::has_width() const @@ -2119,37 +2107,22 @@ bool NetExpr::has_width() const /* * Create a bitwise operator node from the opcode and the left and - * right expressions. Don't worry about the width of the expression - * yet, we'll get that from the l-value, whatever that turns out to - * be. However, if we don't, our default will be the width of the - * largest operand. + * right expressions. */ -NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - if (r->expr_width() > l->expr_width()) - expr_width(r->expr_width()); - else - expr_width(l->expr_width()); } NetEBBits::~NetEBBits() { } -NetEBBits* NetEBBits::dup_expr() const -{ - NetEBBits*result = new NetEBBits(op_, left_->dup_expr(), - right_->dup_expr()); - return result; -} - -NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r) +NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : op_(op__), left_(l), right_(r) { - // Binary expressions of all sorts are signed if both the - // arguments are signed. - cast_signed_base_( left_->has_sign() && right_->has_sign()); + expr_width(wid); + cast_signed_base_(signed_flag); } NetEBinary::~NetEBinary() @@ -2163,29 +2136,15 @@ bool NetEBinary::has_width() const return left_->has_width() && right_->has_width(); } -NetEBinary* NetEBinary::dup_expr() const -{ - assert(0); - return 0; -} - NetEBLogic::NetEBLogic(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +: NetEBinary(op__, l, r, 1, false) { - expr_width(1); } NetEBLogic::~NetEBLogic() { } -NetEBLogic* NetEBLogic::dup_expr() const -{ - NetEBLogic*result = new NetEBLogic(op_, left_->dup_expr(), - right_->dup_expr()); - return result; -} - NetEConst::NetEConst(const verinum&val) : NetExpr(val.len()), value_(val) { @@ -2196,6 +2155,12 @@ NetEConst::~NetEConst() { } +void NetEConst::cast_signed(bool flag) +{ + cast_signed_base_(flag); + value_.has_sign(flag); +} + const verinum& NetEConst::value() const { return value_; @@ -2218,6 +2183,17 @@ ivl_variable_type_t NetEConst::expr_type() const 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) { @@ -2272,7 +2248,7 @@ NetESignal::NetESignal(NetNet*n) { net_->incr_eref(); set_line(*n); - cast_signed(net_->get_signed()); + cast_signed_base_(net_->get_signed()); } NetESignal::NetESignal(NetNet*n, NetExpr*w) @@ -2280,7 +2256,7 @@ NetESignal::NetESignal(NetNet*n, NetExpr*w) { net_->incr_eref(); set_line(*n); - cast_signed(net_->get_signed()); + cast_signed_base_(net_->get_signed()); } NetESignal::~NetESignal() @@ -2334,20 +2310,12 @@ ivl_variable_type_t NetESignal::expr_type() const * 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) +NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, + unsigned wid, bool signed_flag) : cond_(c), true_val_(t), false_val_(f) { - if (type_is_vectorable(expr_type())) { - // use widest result - if (true_val_->expr_width() > false_val_->expr_width()) - expr_width(true_val_->expr_width()); - else - expr_width(false_val_->expr_width()); - } else { - expr_width(1); - } - - cast_signed(c->has_sign() && t->has_sign() && f->has_sign()); + expr_width(wid); + cast_signed_base_(signed_flag); } NetETernary::~NetETernary() @@ -2399,23 +2367,10 @@ ivl_variable_type_t NetETernary::expr_type() const return tru; } -NetEUnary::NetEUnary(char op__, NetExpr*ex) -: NetExpr(ex->expr_width()), op_(op__), expr_(ex) +NetEUnary::NetEUnary(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetExpr(wid), op_(op__), expr_(ex) { - switch (op_) { - case '!': - expr_width(1); - break; - } - switch (op_) { - case '-': - case '+': - case 'm': // abs() - cast_signed(ex->has_sign()); - break; - default: - ; - } + cast_signed_base_(signed_flag); } NetEUnary::~NetEUnary() @@ -2428,8 +2383,8 @@ ivl_variable_type_t NetEUnary::expr_type() const return expr_->expr_type(); } -NetEUBits::NetEUBits(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +NetEUBits::NetEUBits(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetEUnary(op__, ex, wid, signed_flag) { } @@ -2443,9 +2398,8 @@ ivl_variable_type_t NetEUBits::expr_type() const } NetEUReduce::NetEUReduce(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +: NetEUnary(op__, ex, 1, false) { - expr_width(1); } NetEUReduce::~NetEUReduce() @@ -2457,8 +2411,8 @@ ivl_variable_type_t NetEUReduce::expr_type() const return expr_->expr_type(); } -NetECast::NetECast(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +NetECast::NetECast(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetEUnary(op__, ex, wid, signed_flag) { } diff --git a/netlist.h b/netlist.h index 403ad6a4a..a182f6bbd 100644 --- a/netlist.h +++ b/netlist.h @@ -42,6 +42,7 @@ # include "LineInfo.h" # include "svector.h" # include "Attrib.h" +# include "PUdp.h" #ifdef HAVE_IOSFWD # include @@ -803,6 +804,11 @@ class NetScope : public Attrib { /* 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 add_module_port(NetNet*port); + unsigned module_ports() const; + NetNet*module_port(unsigned idx) const; /* Scopes have their own time units and time precision. The unit and precision are given as power of 10, i.e., -3 is @@ -943,6 +949,7 @@ class NetScope : public Attrib { typedef std::map::const_iterator signals_map_iter_t; std::map signals_map_; perm_string module_name_; + vectorports_; union { NetTaskDef*task_; NetFuncDef*func_; @@ -1137,7 +1144,7 @@ class NetCLShift : public NetNode { * 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 then the other, it is up to the generator to + * 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 @@ -1600,12 +1607,8 @@ class NetTran : public NetNode, public IslandBranch { * 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, that accounts - * for the widths of the sub-expressions I might have. It is up to the - * derived classes to properly set the expr width, if need be. The - * set_width() method is used to compel an expression to have a - * certain width, and is used particularly when the expression is an - * rvalue in an assignment statement. + * The expr_width() is the width of the expression, which is calculated + * before the expression is elaborated. */ class NetExpr : public LineInfo { public: @@ -1621,27 +1624,9 @@ class NetExpr : public LineInfo { // How wide am I? unsigned expr_width() const { return width_; } - // Coerce the expression to have a specific width. If the - // coercion works, then return true. Otherwise, return false. - // A coercion will work or not depending on the implementation - // in the derived class. Normally, the width will be set if - // the expression is: - // - already the requested size, OR - // - otherwise unsized. - // Normally, the resize will not allow a width size that loses - // data. For example, it will not reduce a constant expression - // to the point where significant bits are lost. But if the - // last_chance flag is true, then the method assumes that high - // bits will be lost anyhow, so try harder. Loss will be - // allowed, but it still won't resize fixed size expressions - // such as vector signals. This flag is meant to be used by - // elaboration of procedural assignment to set the expression - // width to the l-value width, if possible. - virtual bool set_width(unsigned wid, bool last_chance =false); - // This method returns true if the expression is // signed. Unsigned expressions return false. - bool has_sign() const; + bool has_sign() const { return signed_flag_; } virtual void cast_signed(bool flag); // This returns true if the expression has a definite @@ -1663,14 +1648,7 @@ class NetExpr : public LineInfo { // equivalent expression that is reduced as far as compile // time knows how. Essentially, this is designed to fold // constants. - // - // The prune_to_width is the maximum width that the result - // should be. If it is 0 or -1, then do not prune the - // result. If it is -1, go through special efforts to preserve - // values that may expand. A width of 0 corresponds to a - // self-determined context, and a width of -1 corresponds to - // an infinitely wide context. - virtual NetExpr*eval_tree(int prune_to_width = -1); + virtual NetExpr*eval_tree(); // Make a duplicate of myself, and subexpressions if I have // any. This is a deep copy operation. @@ -1695,10 +1673,9 @@ class NetExpr : public LineInfo { // the expression output. virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); - protected: - void expr_width(unsigned w); - void cast_signed_base_(bool flag) {signed_flag_ = flag; } + void expr_width(unsigned wid) { width_ = wid; } + void cast_signed_base_(bool flag) { signed_flag_ = flag; } private: unsigned width_; @@ -1722,11 +1699,15 @@ class NetEConst : public NetExpr { const verinum&value() const; - virtual bool set_width(unsigned w, bool last_chance =false); - virtual void cast_signed(bool sign_flag); + 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. */ + virtual void trim(); + virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1749,7 +1730,6 @@ class NetEConstEnum : public NetEConst { const NetScope*scope() const; netenum_t*enumeration() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1771,7 +1751,6 @@ class NetEConstParam : public NetEConst { perm_string name() const; const NetScope*scope() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1793,10 +1772,6 @@ class NetECReal : public NetExpr { const verireal&value() const; - // Reals can be used in vector expressions. Conversions will - // be done at the right time. - virtual bool set_width(unsigned w, bool last_chance); - // This type has no self-determined width. This is false. virtual bool has_width() const; @@ -2144,7 +2119,6 @@ class NetUReduce : public NetNode { * 1 are listed. * */ -#include "PUdp.h" class NetUDP : public NetNode { @@ -2169,6 +2143,9 @@ class NetUDP : public NetNode { 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; @@ -2298,9 +2275,11 @@ class NetAssign_ { // 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*); - void set_part(NetExpr* loff, unsigned wid); + void set_part(NetExpr* loff, unsigned wid, + ivl_select_type_t = IVL_SEL_OTHER); // Get the width of the r-value that this node expects. This // method accounts for the presence of the mux, so it is not @@ -2342,6 +2321,7 @@ class NetAssign_ { // indexed part select base NetExpr*base_; unsigned lwid_; + ivl_select_type_t sel_type_; }; class NetAssignBase : public NetProc { @@ -3123,14 +3103,13 @@ class NetEUFunc : public NetExpr { const NetScope* func() const; - virtual bool set_width(unsigned, bool last_chance); 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(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); private: @@ -3326,7 +3305,7 @@ class NetAnalogTop : public LineInfo, public Attrib { class NetEBinary : public NetExpr { public: - NetEBinary(char op, NetExpr*l, NetExpr*r); + NetEBinary(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBinary(); const NetExpr*left() const { return left_; } @@ -3334,8 +3313,6 @@ class NetEBinary : public NetExpr { char op() const { return op_; } - virtual bool set_width(unsigned w, bool last_chance =false); - // A binary expression node only has a definite // self-determinable width if the operands both have definite // widths. @@ -3365,15 +3342,13 @@ class NetEBinary : public NetExpr { class NetEBAdd : public NetEBinary { public: - NetEBAdd(char op, NetExpr*l, NetExpr*r, bool lossless_flag =false); + NetEBAdd(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBAdd(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBAdd* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3388,15 +3363,13 @@ class NetEBAdd : public NetEBinary { class NetEBDiv : public NetEBinary { public: - NetEBDiv(char op, NetExpr*l, NetExpr*r); + NetEBDiv(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBDiv(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBDiv* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3420,12 +3393,11 @@ class NetEBDiv : public NetEBinary { class NetEBBits : public NetEBinary { public: - NetEBBits(char op, NetExpr*l, NetExpr*r); + NetEBBits(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBBits(); - virtual bool set_width(unsigned w, bool last_chance); virtual NetEBBits* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); }; @@ -3450,13 +3422,11 @@ class NetEBComp : public NetEBinary { NetEBComp(char op, NetExpr*l, NetExpr*r); ~NetEBComp(); - virtual bool set_width(unsigned w, bool last_chance =false); - /* 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 NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3486,9 +3456,8 @@ class NetEBLogic : public NetEBinary { NetEBLogic(char op, NetExpr*l, NetExpr*r); ~NetEBLogic(); - virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEBLogic* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3505,7 +3474,7 @@ class NetEBLogic : public NetEBinary { class NetEBMinMax : public NetEBinary { public: - NetEBMinMax(char op, NetExpr*l, NetExpr*r); + NetEBMinMax(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMinMax(); virtual ivl_variable_type_t expr_type() const; @@ -3519,15 +3488,13 @@ class NetEBMinMax : public NetEBinary { class NetEBMult : public NetEBinary { public: - NetEBMult(char op, NetExpr*l, NetExpr*r); + NetEBMult(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMult(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBMult* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3540,14 +3507,13 @@ class NetEBMult : public NetEBinary { class NetEBPow : public NetEBinary { public: - NetEBPow(char op, NetExpr*l, NetExpr*r); + NetEBPow(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBPow(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); virtual NetEBPow* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3566,17 +3532,15 @@ class NetEBPow : public NetEBinary { class NetEBShift : public NetEBinary { public: - NetEBShift(char op, NetExpr*l, NetExpr*r); + NetEBShift(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBShift(); - virtual bool set_width(unsigned w, bool last_chance); - // 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 NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3596,31 +3560,27 @@ class NetEBShift : public NetEBinary { class NetEConcat : public NetExpr { public: - NetEConcat(unsigned cnt, NetExpr* repeat =0); + NetEConcat(unsigned cnt, unsigned repeat =1); ~NetEConcat(); // Manipulate the parameters. void set(unsigned idx, NetExpr*e); - unsigned repeat(); - unsigned repeat() const; + unsigned repeat() const { return repeat_; } unsigned nparms() const { return parms_.count() ; } NetExpr* parm(unsigned idx) const { return parms_[idx]; } virtual NexusSet* nex_input(bool rem_out = true); virtual bool has_width() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEConcat* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; private: svectorparms_; - NetExpr* repeat_; - unsigned repeat_value_; - bool repeat_calculated_; + unsigned repeat_; }; @@ -3641,17 +3601,18 @@ class NetEConcat : public NetExpr { class NetESelect : public NetExpr { public: - NetESelect(NetExpr*exp, NetExpr*base, unsigned wid); + 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; virtual NexusSet* nex_input(bool rem_out = true); - virtual bool set_width(unsigned w, bool last_chance =false); virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetESelect* dup_expr() const; virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); virtual void dump(ostream&) const; @@ -3659,6 +3620,7 @@ class NetESelect : public NetExpr { private: NetExpr*expr_; NetExpr*base_; + ivl_select_type_t sel_type_; }; /* @@ -3748,11 +3710,10 @@ class NetESFunc : public NetExpr { NetExpr* parm(unsigned idx); const NetExpr* parm(unsigned idx) const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual bool set_width(unsigned, bool last_chance); virtual netenum_t* enumeration() const; virtual void dump(ostream&) const; @@ -3779,17 +3740,15 @@ class NetESFunc : public NetExpr { class NetETernary : public NetExpr { public: - NetETernary(NetExpr*c, NetExpr*t, NetExpr*f); + NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag); ~NetETernary(); - virtual bool set_width(unsigned w, bool last_chance); - const NetExpr*cond_expr() const; const NetExpr*true_expr() const; const NetExpr*false_expr() const; virtual NetETernary* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -3828,16 +3787,14 @@ class NetETernary : public NetExpr { class NetEUnary : public NetExpr { public: - NetEUnary(char op, NetExpr*ex); + NetEUnary(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUnary(); char op() const { return op_; } const NetExpr* expr() const { return expr_; } - virtual bool set_width(unsigned w, bool last_chance); - virtual NetEUnary* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; @@ -3856,13 +3813,13 @@ class NetEUnary : public NetExpr { class NetEUBits : public NetEUnary { public: - NetEUBits(char op, NetExpr*ex); + 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 NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; }; @@ -3872,10 +3829,9 @@ class NetEUReduce : public NetEUnary { NetEUReduce(char op, NetExpr*ex); ~NetEUReduce(); - virtual bool set_width(unsigned w, bool last_chance); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUReduce* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual ivl_variable_type_t expr_type() const; private: @@ -3885,7 +3841,7 @@ class NetEUReduce : public NetEUnary { class NetECast : public NetEUnary { public: - NetECast(char op, NetExpr*ex); + NetECast(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetECast(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3911,7 +3867,6 @@ class NetESignal : public NetExpr { ~NetESignal(); perm_string name() const; - virtual bool set_width(unsigned, bool last_chance); virtual NetESignal* dup_expr() const; NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); diff --git a/netmisc.cc b/netmisc.cc index 4d0ea5c06..0479092ff 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -184,9 +184,9 @@ NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) NetExpr* cast_to_int2(NetExpr*expr) { - NetECast*cast = new NetECast('2', expr); + NetECast*cast = new NetECast('2', expr, expr->expr_width(), + expr->has_sign()); cast->set_line(*expr); - cast->cast_signed(expr->has_sign()); return cast; } @@ -208,17 +208,14 @@ static NetExpr* make_add_expr(NetExpr*expr, long val) val = -val; } - verinum val_v (val); + verinum val_v (val, expr->expr_width()); val_v.has_sign(true); - if (expr->has_width()) { - val_v = verinum(val_v, expr->expr_width()); - } - NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); - NetEBAdd*res = new NetEBAdd(add_op, expr, val_c); + NetEBAdd*res = new NetEBAdd(add_op, expr, val_c, expr->expr_width(), + expr->has_sign()); res->set_line(*expr); return res; @@ -235,7 +232,8 @@ static NetExpr* make_sub_expr(long val, NetExpr*expr) NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); - NetEBAdd*res = new NetEBAdd('-', val_c, expr); + NetEBAdd*res = new NetEBAdd('-', val_c, expr, expr->expr_width(), + expr->has_sign()); res->set_line(*expr); return res; @@ -444,11 +442,10 @@ NetExpr* condition_reduce(NetExpr*expr) return expr; verinum zero (verinum::V0, expr->expr_width()); + zero.has_sign(expr->has_sign()); NetEConst*ezero = new NetEConst(zero); ezero->set_line(*expr); - ezero->cast_signed(expr->has_sign()); - ezero->set_width(expr->expr_width()); NetEBComp*cmp = new NetEBComp('n', expr, ezero); cmp->set_line(*expr); @@ -457,43 +454,132 @@ NetExpr* condition_reduce(NetExpr*expr) return cmp; } -void probe_expr_width(Design*des, NetScope*scope, PExpr*pe) +static const char*width_mode_name(PExpr::width_mode_t mode) { - ivl_variable_type_t expr_type = IVL_VT_NO_TYPE; - bool flag = false; - pe->test_width(des, scope, 0, 0, expr_type, flag); + switch (mode) { + case PExpr::SIZED: + return "sized"; + case PExpr::EXPAND: + return "expand"; + case PExpr::LOSSLESS: + return "lossless"; + case PExpr::UNSIZED: + return "unsized"; + default: + return "??"; + } } -NetExpr* elab_and_eval(Design*des, NetScope*scope, - const PExpr*pe, int expr_wid, int prune_width) +NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) { - NetExpr*tmp = pe->elaborate_expr(des, scope, expr_wid, false); + PExpr::width_mode_t mode = PExpr::SIZED; + if ((context_width == -2) && !gn_strict_expr_width_flag) + 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. + if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) + && (expr_width < (unsigned)context_width)) + expr_width = context_width; + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": debug: test_width of " + << *pe << endl; + cerr << pe->get_fileline() << ": " + << "returns type=" << pe->expr_type() + << ", width=" << expr_width + << ", signed=" << pe->has_sign() + << ", mode=" << width_mode_name(mode) << endl; + } + + // If we can get the same result using a smaller expression + // width, do so. + if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) + && (expr_width > (unsigned)context_width)) { + expr_width = max(pe->min_width(), (unsigned)context_width); + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": " + << "pruned to width=" << expr_width << endl; + } + } + + NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, false); if (tmp == 0) return 0; - eval_expr(tmp, prune_width); + eval_expr(tmp, context_width); + + if (NetEConst*ce = dynamic_cast(tmp)) { + if ((mode >= PExpr::LOSSLESS) && (context_width < 0)) + ce->trim(); + } return tmp; } -void eval_expr(NetExpr*&expr, int prune_width) +NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, + unsigned arg_idx, PExpr*pe) +{ + 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=" << width_mode_name(mode) << endl; + } + + NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), true); + if (tmp == 0) return 0; + + eval_expr(tmp, -1); + + if (NetEConst*ce = dynamic_cast(tmp)) { + if (mode != PExpr::SIZED) + ce->trim(); + } + + return tmp; +} + +void eval_expr(NetExpr*&expr, int context_width) { assert(expr); if (dynamic_cast(expr)) return; - /* Resize a constant if allowed and needed. */ - if (NetEConst *tmp = dynamic_cast(expr)) { - if (prune_width <= 0) return; - if (tmp->has_width()) return; - if ((unsigned)prune_width <= tmp->expr_width()) return; - expr = pad_to_width(expr, (unsigned)prune_width, *expr); - return; - } - NetExpr*tmp = expr->eval_tree(prune_width); + 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); + } + 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, NetExpr*expr) @@ -560,7 +646,6 @@ hname_t eval_path_component(Design*des, NetScope*scope, assert(index.sel == index_component_t::SEL_BIT); // Evaluate the bit select to get a number. - probe_expr_width(des, scope, index.msb); NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1); ivl_assert(*index.msb, tmp); diff --git a/netmisc.h b/netmisc.h index 14c6628df..6f98d30d4 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef __netmisc_H #define __netmisc_H /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -151,45 +151,38 @@ extern bool is_param_expr; * constant expression. If it cannot be evaluated, it returns whatever * it can. If the expression cannot be elaborated, return 0. * - * The expr_width is the width of the context where the expression is - * being elaborated, or -1 if the expression is self-determined width. - * - * The prune_width is the maximum width of the result, and is passed - * to the eval_tree method of the expression to limit constant - * results. The evaluation will prune any constant result down to the - * prune_width (if >0) so should only be used at the point where it is - * bound to the destination. + * 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). */ class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, - const PExpr*pe, int expr_wid, - int prune_width =-1); - -void probe_expr_width(Design*des, NetScope*scope, PExpr*pe); + PExpr*pe, int context_width); +/* + * 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); /* * This function elaborates an expression as if it is for the r-value - * of an assignment, The data_type_lv and expr_wid_lv are the type and - * with of the l-value, and the expr is the expression to - * elaborate. The result is the NetExpr elaborated and evaluated. - * (See elab_expr.cc) + * 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) */ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, - ivl_variable_type_t data_type_lv, - int expr_wid_lv, PExpr*expr); + ivl_variable_type_t lv_type, + unsigned lv_width, PExpr*expr); /* - * Used by elaboration to suppress the sign of an operand if the other - * is unsigned. - */ -extern void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp); - -/* - * This procedure elaborates an expression and if the elaboration is + * 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 prune_width =-1); +void eval_expr(NetExpr*&expr, int context_width =-1); /* * Get the long integer value for the passed in expression, if diff --git a/parse.y b/parse.y index 9ad2c3e4f..fe517aef2 100644 --- a/parse.y +++ b/parse.y @@ -426,7 +426,7 @@ static list* make_named_number(perm_string name, PExpr*val =0) %type port_name_list parameter_value_byname_list %type attribute -%type attribute_list attribute_list_opt +%type attribute_list attribute_instance_list attribute_list_opt %type case_item %type case_items @@ -522,11 +522,24 @@ real_or_realtime variety of different objects. The syntax inside the (* *) is a comma separated list of names or names with assigned values. */ attribute_list_opt - : K_PSTAR attribute_list K_STARP { $$ = $2; } - | K_PSTAR K_STARP { $$ = 0; } + : attribute_instance_list | { $$ = 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; @@ -2254,7 +2267,7 @@ atom2_type /* 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 then the general expr_primary + assignments. It is more limited than the general expr_primary rule to reflect the rules for assignment l-values. */ lpvalue : hierarchy_identifier @@ -3924,7 +3937,7 @@ statement /* assign and deassign statements are procedural code to do structural assignments, and to turn that structural assignment - off. This stronger then any other assign, but weaker then the + off. This is stronger than any other assign, but weaker than the force assignments. */ : K_assign lpvalue '=' expression ';' @@ -3957,7 +3970,7 @@ statement /* 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 + 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 statement_list K_end diff --git a/set_width.cc b/set_width.cc deleted file mode 100644 index 9e5dd4278..000000000 --- a/set_width.cc +++ /dev/null @@ -1,486 +0,0 @@ -/* - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -# include "config.h" - -# include - -/* - * This file contains set_width methods for the various NetExpr - * classes. The set_width method is used by elaboration to ask the - * expression to resize itself. If the expression can't, then the - * set_width method will return false and the caller will arrange for - * whatever is needed to deal with the size mismatch. - */ -# include "netlist.h" -# include "netmisc.h" -# include "compiler.h" -# include - - -bool NetExpr::set_width(unsigned w, bool) -{ - cerr << get_fileline() << ": internal warning: " - <>) - /* these operators are handled in the derived class. */ - assert(0); - break; - - /* The default rule is that the operands of the binary - operator might as well use the same width as the - output from the binary operation. */ - default: - expr_width(left_->expr_width() > right_->expr_width() - ? left_->expr_width() : right_->expr_width()); - cerr << "NetEBinary::set_width(): Using default for " << - op_ << "." << endl; - flag = false; - - case '%': - case '/': - flag = left_->set_width(w) && flag; - flag = right_->set_width(w) && flag; - expr_width(w); - break; - } - return flag; -} - -/* - * The bitwise logical operators have operands the same size as the - * result. Anything else is a mess. - */ -bool NetEBAdd::set_width(unsigned w, bool) -{ - - unsigned wid = w; - if (left_->expr_width() > wid) - wid = left_->expr_width(); - if (right_->expr_width() > wid) - wid = right_->expr_width(); - - left_->set_width(wid); - right_->set_width(wid); - - if (left_->expr_width() < wid) { - NetExpr*tmp = new NetESelect(left_, 0, wid); - tmp->cast_signed(left_->has_sign()); - left_ = tmp; - } - - if (right_->expr_width() < wid) { - NetExpr*tmp = new NetESelect(right_, 0, wid); - tmp->cast_signed(right_->has_sign()); - right_ = tmp; - } - - expr_width(wid); - return w == wid; -} - -void NetEBAdd::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - cast_signed_base_(sign_flag); -} - -/* - * The bitwise logical operators have operands the same size as the - * result. Anything else is a mess. I first try to get the operands to - * shrink to the desired size. I then expand operands that are too small. - */ -bool NetEBBits::set_width(unsigned w, bool) -{ - /* First, give the operands a chance to adjust themselves to - the requested width. */ - left_->set_width(w); - right_->set_width(w); - - - /* */ - - unsigned use_width = w; - if (left_->expr_width() > use_width) - use_width = left_->expr_width(); - if (right_->expr_width() > use_width) - use_width = right_->expr_width(); - - /* If the operands end up too small, then pad them to suit. */ - - if (left_->expr_width() < use_width) { - NetExpr*tmp = pad_to_width(left_, use_width, *this); - assert(tmp); - left_ = tmp; - } - - if (right_->expr_width() < w) { - NetExpr*tmp = pad_to_width(right_, use_width, *this); - assert(tmp); - right_ = tmp; - } - - - /* And here is the final width. If this is not the size the - caller requested, then return false. Otherwise, return - true. */ - expr_width(use_width); - return w == use_width; -} - -/* - * Comparison operators allow the subexpressions to have - * their own natural width, but the comparison operator result has a - * fixed width of 1. - */ -bool NetEBComp::set_width(unsigned w, bool) -{ - return w == 1; -} - -/* - * There is nothing we can do to the operands of a division to make it - * confirm to the requested width. Force the context to pad or truncate. - */ -bool NetEBDiv::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -void NetEBDiv::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - - cast_signed_base_(sign_flag); -} - -bool NetEBLogic::set_width(unsigned w, bool) -{ - bool flag; - flag = left_->set_width(right_->expr_width()); - if (!flag) - flag = right_->set_width(left_->expr_width()); - return w == 1; -} - -/* - * There is nothing we can do to the operands of a multiply to make it - * confirm to the requested width. Force the context to pad or truncate. - */ -bool NetEBMult::set_width(unsigned w, bool) -{ - if (w < left_->expr_width()) - left_->set_width(w); - if (w < right_->expr_width()) - right_->expr_width(); - - expr_width(w); - return true; -} - -void NetEBMult::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - - cast_signed_base_(sign_flag); -} - -bool NetEBPow::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * The shift operator allows the shift amount to have its own - * natural width. The width of the operator result is the width of the - * left operand, the value that is to be shifted. - */ -bool NetEBShift::set_width(unsigned w, bool) -{ - switch (op()) { - - case 'l': - left_->set_width(w); - if (left_->expr_width() < w) - left_ = pad_to_width(left_, w, *this); - break; - - case 'r': - case 'R': - if (left_->expr_width() < w) - left_ = pad_to_width(left_, w, *this); - break; - - default: - assert(0); - } - - expr_width(left_->expr_width()); - - return w == expr_width(); -} - -/* - * Add up the widths from all the expressions that are concatenated - * together. This is the width of the expression, tough luck if you - * want it otherwise. - * - * If during the course of elaboration one of the sub-expressions is - * broken, then don't count it in the width. This doesn't really - * matter because the null expression is indication of an error and - * the compiler will not go beyond elaboration. - */ -bool NetEConcat::set_width(unsigned w, bool) -{ - unsigned sum = 0; - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) - if (parms_[idx] != 0) - sum += parms_[idx]->expr_width(); - - sum *= repeat(); - expr_width(sum); - return w == sum; -} - -bool NetEConst::set_width(unsigned w, bool last_chance) -{ - /* Make the value signed if the NetEConst is signed. - * This happens when $signed() is called, so this - * sign information needs to be propagated. */ - value_.has_sign(has_sign()); - - if (w == value_.len()) { - expr_width(w); - return true; - } - - assert(w != 0); - - if (w > value_.len()) { - verinum::V pad = verinum::V0; - if (value_.has_sign()) { - pad = value_.get(value_.len()-1); - - /* It appears that you always have a defined length here, - * so this logic may be in error. */ - } else if (value_.len() != 0 && !value_.has_len()) - switch (value_.get(value_.len()-1)) { - case verinum::V1: - case verinum::V0: - break; - case verinum::Vx: - pad = verinum::Vx; - break; - case verinum::Vz: - pad = verinum::Vz; - break; - } - - verinum tmp (verinum::V0, w, has_width()); - for (unsigned idx = 0 ; idx < value_.len() ; idx += 1) - tmp.set(idx, value_[idx]); - for (unsigned idx = value_.len() ; idx < w ; idx += 1) - tmp.set(idx, pad); - - tmp.has_sign(value_.has_sign()); - value_ = tmp; - - expr_width(w); - return true; - - } else { - unsigned use_w = w; - - verinum::V pad_bit = value_.has_sign() - ? value_[value_.len() - 1] - : verinum::V0; - - if (! last_chance) { - // Don't reduce a number too small to hold all the - // significant bits. - for (unsigned idx = w ; idx < value_.len() ; idx += 1) - if (value_[idx] != pad_bit) - use_w = idx+1; - - // Correct for the special case of signed value. We - // cannot have the result change sign on us. - if (value_.has_sign() && (use_w < value_.len()) - && (value_[use_w-1] != pad_bit)) - use_w += 1; - } - - verinum tmp (verinum::V0, use_w, has_width()); - for (unsigned idx = 0 ; idx < use_w ; idx += 1) - tmp.set(idx, value_[idx]); - - tmp.has_sign(value_.has_sign()); - value_ = tmp; - expr_width(use_w); - return w == use_w; - } -} - -void NetEConst::cast_signed(bool sign_flag) -{ - value_.has_sign(sign_flag); - cast_signed_base_(sign_flag); -} - -/* - * Parameter vectors cannot be resized because they refer to a common - * value. Ditto for enumeration names. - */ -bool NetEConstParam::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -bool NetEConstEnum::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * Real constants can have whatever width the environment wants, - * because it isn't really a vector. The environment will convert this - * to a vector at the right time. - */ -bool NetECReal::set_width(unsigned w, bool) -{ - expr_width(w); - return true; -} -#if 0 -bool NetEMemory::set_width(unsigned w, bool) -{ - if (w != mem_->width()) - return false; - - expr_width(w); - return true; -} -#endif -bool NetESelect::set_width(unsigned, bool) -{ - return expr_width() == 1; -} - -bool NetESFunc::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * The signal should automatically pad with zeros to get to the desired - * width. Do not allow signal bits to be truncated, however. - */ -bool NetESignal::set_width(unsigned w, bool) -{ - return w == vector_width(); -} - -bool NetETernary::set_width(unsigned w, bool last_chance) -{ - bool flag = true; - flag = flag && true_val_->set_width(w, last_chance); - flag = flag && false_val_->set_width(w, last_chance); - - /* The ternary really insists that the true and false clauses - have the same width. Even if we fail to make the width that - the user requests, at least pad the smaller width to suit - the larger. */ - if (true_val_->expr_width() < false_val_->expr_width()) - true_val_ = pad_to_width(true_val_, false_val_->expr_width(), - *this); - if (false_val_->expr_width() < true_val_->expr_width()) - false_val_ = pad_to_width(false_val_, true_val_->expr_width(), - *this); - - expr_width(true_val_->expr_width()); - return flag; -} - -/* - * XXXX FIX ME: For now, just take whatever the caller says as my - * width. What I really need to do is note the width of the output - * parameter of the function definition and take that into account. - */ -bool NetEUFunc::set_width(unsigned w, bool) -{ - return w == result_sig_->expr_width(); -} - -bool NetEUnary::set_width(unsigned w, bool) -{ - bool flag = true; - switch (op_) { - case '~': - case '-': - case 'r': - case 'i': - flag = expr_->set_width(w); - expr_width(w); - break; - case '!': - return w == 1; - default: - flag = expr_width() == w; - break; - } - - return flag; -} - -/* - * Unary reduction operators allow its operand to have any width. The - * result is defined to be 1. - */ -bool NetEUReduce::set_width(unsigned w, bool) -{ - return w == 1; -} diff --git a/synth2.cc b/synth2.cc index 69d9a5943..3a4b5367c 100644 --- a/synth2.cc +++ b/synth2.cc @@ -483,7 +483,7 @@ bool NetBlock::synth_sync(Design*des, NetScope* /*scope*/, NetFF* /*ff*/, 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 then the r-value. */ + an assignment is smaller than the r-value. */ for (unsigned idx = 0 ; idx < tmp_out->pin_count() ; idx += 1) { unsigned ptr = find_nexus_in_set(nex_map, tmp_map->pin(idx).nexus()); diff --git a/sys_funcs.cc b/sys_funcs.cc index 08dfbdf3c..56e6ab523 100644 --- a/sys_funcs.cc +++ b/sys_funcs.cc @@ -58,7 +58,7 @@ void cleanup_sys_func_table() const struct sfunc_return_type* lookup_sys_func(const char*name) { - /* First, try to find then name in the function stack. */ + /* 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) diff --git a/t-dll-api.cc b/t-dll-api.cc index 37c0255c4..c04be3e66 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -436,9 +436,11 @@ extern "C" ivl_expr_t ivl_expr_oper1(ivl_expr_t net) assert(net); switch (net->type_) { case IVL_EX_BINARY: - case IVL_EX_SELECT: return net->u_.binary_.lef_; + case IVL_EX_SELECT: + return net->u_.select_.expr_; + case IVL_EX_UNARY: return net->u_.unary_.sub_; @@ -463,9 +465,11 @@ extern "C" ivl_expr_t ivl_expr_oper2(ivl_expr_t net) assert(net); switch (net->type_) { case IVL_EX_BINARY: - case IVL_EX_SELECT: return net->u_.binary_.rig_; + case IVL_EX_SELECT: + return net->u_.select_.base_; + case IVL_EX_TERNARY: return net->u_.ternary_.true_e; @@ -569,6 +573,13 @@ extern "C" ivl_scope_t ivl_expr_scope(ivl_expr_t net) 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); @@ -867,6 +878,14 @@ 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); @@ -1459,6 +1478,12 @@ extern "C" ivl_expr_t ivl_lval_part_off(ivl_lval_t 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); @@ -1903,7 +1928,8 @@ extern "C" ivl_scope_t ivl_scope_parent(ivl_scope_t net) extern "C" unsigned ivl_scope_ports(ivl_scope_t net) { assert(net); - if (net->type_ == IVL_SCT_FUNCTION || + if (net->type_ == IVL_SCT_MODULE || + net->type_ == IVL_SCT_FUNCTION || net->type_ == IVL_SCT_TASK) return net->ports; return 0; } @@ -1914,7 +1940,15 @@ extern "C" ivl_signal_t ivl_scope_port(ivl_scope_t net, unsigned idx) assert(net->type_ == IVL_SCT_FUNCTION || net->type_ == IVL_SCT_TASK); assert(idx < net->ports); - return net->port[idx]; + 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) diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 3c3b3030a..0db2ffd62 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -274,8 +274,8 @@ void dll_target::expr_param(const NetEConstParam*net) << ivl_scope_name(scop) << endl; } assert(par); - assert(par->value); - expr_ = par->value; + expr_const(net); + expr_->u_.string_.parameter = par; } void dll_target::expr_rparam(const NetECRealParam*net) @@ -359,13 +359,13 @@ void dll_target::expr_select(const NetESelect*net) assert(expr_ == 0); net->sub_expr()->expr_scan(this); - ivl_expr_t left = expr_; + ivl_expr_t expr = expr_; expr_ = 0; if (net->select()) net->select()->expr_scan(this); - ivl_expr_t rght = expr_; + ivl_expr_t base = expr_; expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); @@ -376,8 +376,9 @@ void dll_target::expr_select(const NetESelect*net) expr_->sized_= 1; FILE_NAME(expr_, net); - expr_->u_.binary_.lef_ = left; - expr_->u_.binary_.rig_ = rght; + 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) diff --git a/t-dll-proc.cc b/t-dll-proc.cc index 0f5fb8852..783ce6618 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -88,9 +88,9 @@ void dll_target::task_def(const NetScope*net) scop->ports = def->port_count(); if (scop->ports > 0) { - scop->port = new ivl_signal_t[scop->ports]; + scop->u_.port = new ivl_signal_t[scop->ports]; for (unsigned idx = 0 ; idx < scop->ports ; idx += 1) - scop->port[idx] = find_signal(des_, def->port(idx)); + scop->u_.port[idx] = find_signal(des_, def->port(idx)); } } @@ -110,9 +110,9 @@ bool dll_target::func_def(const NetScope*net) scop->ports = def->port_count() + 1; if (scop->ports > 0) { - scop->port = new ivl_signal_t[scop->ports]; + scop->u_.port = new ivl_signal_t[scop->ports]; for (unsigned idx = 1 ; idx < scop->ports ; idx += 1) - scop->port[idx] = find_signal(des_, def->port(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 @@ -121,7 +121,7 @@ bool dll_target::func_def(const NetScope*net) this, but that will break code generators that use this result. */ if (const NetNet*ret_sig = def->return_sig()) { - scop->port[0] = find_signal(des_, ret_sig); + scop->u_.port[0] = find_signal(des_, ret_sig); return true; } @@ -153,9 +153,11 @@ void dll_target::make_assign_lvals_(const NetAssignBase*net) 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; } diff --git a/t-dll.cc b/t-dll.cc index 1141626d8..1378539c8 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -563,6 +563,13 @@ void dll_target::add_root(ivl_design_s &des__, const NetScope *s) root_->attr = fill_in_attributes(s); root_->is_auto = 0; root_->is_cell = s->is_cell(); + root_->ports = s->module_ports(); + 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(idx); + } + } des__.nroots_++; if (des__.roots_) @@ -1270,38 +1277,36 @@ void dll_target::udp(const NetUDP*net) 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); + 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; - } + udps[net->udp_name()] = u; + } obj->udp = u; @@ -2275,6 +2280,7 @@ void dll_target::scope(const NetScope*net) FILE_NAME(scop, net); scop->parent = find_scope(des_, net->parent()); assert(scop->parent); + scop->parent->children[net->fullname()] = scop; scop->nsigs_ = 0; scop->sigs_ = 0; scop->nlog_ = 0; @@ -2296,6 +2302,13 @@ void dll_target::scope(const NetScope*net) case NetScope::MODULE: scop->type_ = IVL_SCT_MODULE; scop->tname_ = net->module_name(); + scop->ports = net->module_ports(); + 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(idx); + } + } break; case NetScope::TASK: { const NetTaskDef*def = net->task_def(); @@ -2326,10 +2339,20 @@ void dll_target::scope(const NetScope*net) scop->tname_ = scop->name_; break; } + } +} - assert(scop->parent != 0); - - scop->parent->children[net->fullname()] = scop; +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; } } diff --git a/t-dll.h b/t-dll.h index 56aae7b02..6bd4448d4 100644 --- a/t-dll.h +++ b/t-dll.h @@ -93,6 +93,7 @@ struct dll_target : public target_t, public expr_scan_t { 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_; @@ -231,6 +232,12 @@ struct ivl_expr_s { ivl_expr_t rig_; } binary_; + struct { + ivl_select_type_t sel_type_; + ivl_expr_t expr_; + ivl_expr_t base_; + } select_; + struct { ivl_branch_t branch; ivl_nature_t nature; @@ -427,6 +434,7 @@ enum ivl_lval_type_t { struct ivl_lval_s { ivl_expr_t loff; + ivl_select_type_t sel_type; ivl_expr_t idx; unsigned width_; unsigned type_ : 8; @@ -517,6 +525,7 @@ struct ivl_udp_s { ccharp_t*table; // zero terminated array of pointers perm_string file; unsigned lineno; + string*ports; }; /* @@ -599,7 +608,7 @@ struct ivl_process_s { /* * 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 + * by following its child pointer, then following sibling pointers from * there. */ struct ivl_scope_s { @@ -638,7 +647,11 @@ struct ivl_scope_s { unsigned is_cell; unsigned ports; - ivl_signal_t*port; + union { + ivl_signal_t*port; + ivl_nexus_t*nex; + NetNet**net; + } u_; std::vectorswitches; diff --git a/target.cc b/target.cc index 3df7a9c1b..7c744dd87 100644 --- a/target.cc +++ b/target.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-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 @@ -32,6 +32,10 @@ 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() diff --git a/target.h b/target.h index 87d338c24..1ff7fc522 100644 --- a/target.h +++ b/target.h @@ -1,7 +1,7 @@ #ifndef __target_H #define __target_H /* - * Copyright (c) 1998-2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-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 @@ -56,6 +56,10 @@ struct target_t { anything else is called. */ virtual void scope(const NetScope*); + /* 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*); diff --git a/tgt-fpga/d-virtex.c b/tgt-fpga/d-virtex.c index d208b4216..24e371741 100644 --- a/tgt-fpga/d-virtex.c +++ b/tgt-fpga/d-virtex.c @@ -206,7 +206,7 @@ static void virtex_or_wide(ivl_net_logic_t net) /* * Pick off the cases where there is a Virtex specific implementation - * that is better then the generic Xilinx implementation. Route the + * 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) diff --git a/tgt-vhdl/cast.cc b/tgt-vhdl/cast.cc index 8e34673a3..f54dacf94 100644 --- a/tgt-vhdl/cast.cc +++ b/tgt-vhdl/cast.cc @@ -122,7 +122,7 @@ 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())); diff --git a/tgt-vhdl/expr.cc b/tgt-vhdl/expr.cc index a382fb8f6..2b8322683 100644 --- a/tgt-vhdl/expr.cc +++ b/tgt-vhdl/expr.cc @@ -473,8 +473,18 @@ static vhdl_expr *translate_select(ivl_expr_t e) // 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 - return new vhdl_binop_expr(from, VHDL_BINOP_SR, base->to_integer(), - new vhdl_type(*from->get_type())); + + 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 diff --git a/tgt-vhdl/process.cc b/tgt-vhdl/process.cc index d19eac572..8ed03e0ca 100644 --- a/tgt-vhdl/process.cc +++ b/tgt-vhdl/process.cc @@ -59,7 +59,7 @@ static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc) // 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); diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index ff82c4e42..56d313933 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -945,7 +945,7 @@ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) assert(false); } } - + arch->set_comment(ss.str()); ent->set_comment(ss.str()); diff --git a/tgt-vhdl/state.cc b/tgt-vhdl/state.cc index 01262ab62..43e448828 100644 --- a/tgt-vhdl/state.cc +++ b/tgt-vhdl/state.cc @@ -187,8 +187,8 @@ vhdl_entity* find_entity(ivl_scope_t scope) return NULL; } else { - const char *tname = ivl_scope_tname(scope); - + 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) @@ -248,8 +248,8 @@ void set_active_entity(vhdl_entity *ent) 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; - + return false; + unsigned nparams_a = ivl_scope_params(a); unsigned nparams_b = ivl_scope_params(b); diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 57c1b055b..de27ac749 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -32,7 +32,7 @@ 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 @@ -79,7 +79,7 @@ static int draw_stask_display(vhdl_procedural *proc, { 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) { @@ -91,7 +91,7 @@ static int draw_stask_display(vhdl_procedural *proc, 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++) { @@ -110,7 +110,7 @@ static int draw_stask_display(vhdl_procedural *proc, // 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; @@ -125,13 +125,13 @@ static int draw_stask_display(vhdl_procedural *proc, 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())); } } @@ -141,16 +141,16 @@ static int draw_stask_display(vhdl_procedural *proc, } // Emit any non-empty string data left in the buffer - if (!ss.str().empty()) + if (!ss.str().empty()) text->add_expr(new vhdl_const_string(ss.str())); } else { vhdl_expr *base = translate_expr(net); - if (NULL == base) + if (NULL == base) return 1; emit_wait_for_0(proc, container, stmt, base); - + text->add_expr(base->cast(text->get_type())); } } @@ -158,7 +158,7 @@ static int draw_stask_display(vhdl_procedural *proc, if (count == 0) text->add_expr(new vhdl_const_string("")); - container->add_stmt(new vhdl_report_stmt(text)); + container->add_stmt(new vhdl_report_stmt(text)); return 0; } @@ -397,7 +397,7 @@ static void emit_wait_for_0(vhdl_procedural *proc, << ivl_stmt_file(stmt) << ":" << ivl_stmt_lineno(stmt) << ")"; wait->set_comment(ss.str()); - + container->add_stmt(wait); proc->added_wait_stmt(); } @@ -451,7 +451,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, assign_type = decl->assignment_type(); if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) - proc->add_blocking_target(lhs); + proc->add_blocking_target(lhs); // A small optimisation is to expand ternary RHSs into an // if statement (eliminates a function call and produces @@ -589,7 +589,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, width_so_far += lval_width; if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) - proc->add_blocking_target(*it); + proc->add_blocking_target(*it); } } } @@ -615,9 +615,9 @@ static int draw_assign(vhdl_procedural *proc, stmt_container *container, { 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; } @@ -645,7 +645,7 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container, 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); @@ -665,8 +665,8 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container, // 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); - + proc->get_scope()->set_initializing(false); + return 0; } @@ -939,7 +939,7 @@ static int draw_wait(vhdl_procedural *_proc, stmt_container *container, 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 + // 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 @@ -1022,10 +1022,10 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container, 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); @@ -1555,7 +1555,7 @@ int draw_while(vhdl_procedural *proc, stmt_container *container, 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; } diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index c485776e0..4f231e1ee 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -212,7 +212,7 @@ 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); @@ -283,7 +283,7 @@ void stmt_container::find_vars(vhdl_var_set_t& read, for (stmt_list_t::const_iterator it = stmts_.begin(); it != stmts_.end(); ++it) - (*it)->find_vars(read, write); + (*it)->find_vars(read, write); } void stmt_container::emit(std::ostream &of, int level, bool newline) const @@ -800,7 +800,7 @@ void vhdl_report_stmt::emit(ostream& of, int level) const const char *levels[] = { "note", "warning", "error", "failure" }; of << " severity " << levels[severity_]; } - + of << ";"; } @@ -812,7 +812,7 @@ void vhdl_report_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) 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 @@ -876,7 +876,7 @@ void vhdl_if_stmt::find_vars(vhdl_var_set_t& read, it != elsif_parts_.end(); ++it) { (*it).test->find_vars(read); (*it).container->find_vars(read, write); - } + } } int vhdl_expr::paren_levels(0); @@ -890,7 +890,7 @@ void vhdl_expr::open_parens(std::ostream& of) void vhdl_expr::close_parens(std::ostream& of) { assert(paren_levels > 0); - + if (--paren_levels > 0) of << ")"; } @@ -908,7 +908,7 @@ void vhdl_unaryop_expr::find_vars(vhdl_var_set_t& read) void vhdl_unaryop_expr::emit(std::ostream &of, int level) const { open_parens(of); - + switch (op_) { case VHDL_UNARYOP_NOT: of << "not "; @@ -923,7 +923,7 @@ void vhdl_unaryop_expr::emit(std::ostream &of, int level) const } vhdl_binop_expr::vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op, - vhdl_expr *right, vhdl_type *type) + vhdl_expr *right, const vhdl_type *type) : vhdl_expr(type), op_(op) { add_expr(left); @@ -944,7 +944,7 @@ 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); + (*it)->find_vars(read); } void vhdl_binop_expr::emit(std::ostream &of, int level) const diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index a27c45bac..32745b097 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -54,12 +54,12 @@ public: 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& read) {} - + protected: static void open_parens(ostream& of); static void close_parens(ostream& of); static int paren_levels; - + const vhdl_type *type_; bool isconst_; }; @@ -119,10 +119,10 @@ enum vhdl_binop_t { */ class vhdl_binop_expr : public vhdl_expr { public: - vhdl_binop_expr(vhdl_binop_t op, vhdl_type *type) + 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, vhdl_type *type); + vhdl_expr *right, const vhdl_type *type); ~vhdl_binop_expr(); void add_expr(vhdl_expr *e); @@ -271,7 +271,7 @@ private: */ class vhdl_fcall : public vhdl_expr { public: - vhdl_fcall(const string& name, vhdl_type *rtype) + vhdl_fcall(const string& name, const vhdl_type *rtype) : vhdl_expr(rtype), name_(name) {}; ~vhdl_fcall() {} @@ -820,7 +820,7 @@ public: // 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_; diff --git a/tgt-vlog95/event.c b/tgt-vlog95/event.c index 0be1ce673..8454a3727 100644 --- a/tgt-vlog95/event.c +++ b/tgt-vlog95/event.c @@ -62,8 +62,10 @@ void emit_event(ivl_scope_t scope, ivl_statement_t stmt) /* 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); - fprintf(vlog_out, "%s", ivl_event_basename(event)); + emit_id(ivl_event_basename(event)); } } } diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 2c3ed2100..698aa6349 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -72,8 +72,8 @@ static unsigned emit_power_as_shift(ivl_scope_t scope, ivl_expr_t expr, static void emit_expr_array(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); - emit_scope_module_path(scope, ivl_signal_scope(sig)); - fprintf(vlog_out, "%s", ivl_signal_basename(sig)); + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); } static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) @@ -142,9 +142,13 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) fprintf(vlog_out, " %s ", oper); emit_expr(scope, ivl_expr_oper2(expr), 0); break; + case 'R': + 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': - case 'R': emit_expr(scope, ivl_expr_oper1(expr), wid); fprintf(vlog_out, " %s ", oper); emit_expr(scope, ivl_expr_oper2(expr), 0); @@ -215,13 +219,24 @@ static void emit_expr_event(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) assert(! ivl_event_nany(event)); assert(! ivl_event_npos(event)); assert(! ivl_event_nneg(event)); - emit_scope_module_path(scope, ev_scope); - fprintf(vlog_out, "%s", ivl_event_basename(event)); + emit_scope_call_path(scope, ev_scope); + emit_id(ivl_event_basename(event)); +} + +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) { - fprintf(vlog_out, "%s", ivl_scope_name(ivl_expr_scope(expr))); + emit_expr_scope_piece(ivl_expr_scope(expr)); } static unsigned emit_param_name_in_scope(ivl_scope_t scope, ivl_expr_t expr) @@ -254,7 +269,7 @@ static unsigned emit_param_name_in_scope(ivl_scope_t scope, ivl_expr_t expr) break; } } - fprintf(vlog_out, "%s", ivl_parameter_basename(par)); + emit_id(ivl_parameter_basename(par)); return 1; } } @@ -263,7 +278,7 @@ static unsigned emit_param_name_in_scope(ivl_scope_t scope, ivl_expr_t expr) static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { - /* A select of a number is reall a parameter select. */ + /* A select of a number is really a parameter select. */ if (ivl_expr_type(expr) == IVL_EX_NUMBER) { /* Look in the current scope. */ if (emit_param_name_in_scope(scope, expr)) return; @@ -280,41 +295,129 @@ static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) 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, wid); } } +/* + * Emit an indexed part select as a concatenation of bit selects. + */ +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 idx; + assert(wid > 0); + fprintf(vlog_out, "{"); + if (msb >= lsb) { + if (sel_type == IVL_SEL_IDX_DOWN) { + lsb += wid - 1; + msb += wid - 1; + emit_select_name(scope, sig_expr, wid); + 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, wid); + 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_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u], ", idx); + } + emit_select_name(scope, sig_expr, wid); + 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_select_name(scope, sig_expr, wid); + 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, wid); + 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_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " - %u], ", idx); + } + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); + } + } +} + 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); if (sel_expr) { - int msb = 1; - int lsb = 0; unsigned width = ivl_expr_width(expr); ivl_expr_type_t type = ivl_expr_type(sig_expr); assert(width > 0); - if (type == IVL_EX_SIGNAL) { - ivl_signal_t sig = ivl_expr_signal(sig_expr); - msb = ivl_signal_msb(sig); - lsb = ivl_signal_lsb(sig); - } /* 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, wid); fprintf(vlog_out, " >> " ); - emit_scaled_expr(scope, sel_expr, msb, lsb); + emit_scaled_expr(scope, sel_expr, 1, 0); fprintf(vlog_out, ")" ); } else { - emit_select_name(scope, sig_expr, wid); + /* 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) { + ivl_signal_t sig = ivl_expr_signal(sig_expr); + msb = ivl_signal_msb(sig); + lsb = ivl_signal_lsb(sig); + } + /* A bit select. */ if (width == 1) { + emit_select_name(scope, sig_expr, wid); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); } else { - emit_scaled_range(scope, sel_expr, width, msb, lsb); + if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { + /* A constant part select. */ + emit_select_name(scope, sig_expr, wid); + emit_scaled_range(scope, sel_expr, width, + msb, lsb); + } else { + /* An indexed part select. */ + assert(sel_type != IVL_SEL_OTHER); + emit_expr_ips(scope, sig_expr, sel_expr, + sel_type, width, msb, lsb); + } } } } else { @@ -326,11 +429,10 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) /* * This routine is used to emit both system and user functions. */ -static void emit_expr_func(ivl_scope_t scope, ivl_expr_t expr, const char* name) +static void emit_expr_func(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { unsigned count = ivl_expr_parms(expr); - fprintf(vlog_out, "%s", name); - if (count != 0) { + if (count) { unsigned idx; fprintf(vlog_out, "("); count -= 1; @@ -343,16 +445,11 @@ static void emit_expr_func(ivl_scope_t scope, ivl_expr_t expr, const char* name) } } -static void emit_expr_sfunc(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) -{ - emit_expr_func(scope, expr, ivl_expr_name(expr)); -} - static void emit_expr_signal(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); - emit_scope_module_path(scope, ivl_signal_scope(sig)); - fprintf(vlog_out, "%s", ivl_signal_basename(sig)); + 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); @@ -373,13 +470,6 @@ static void emit_expr_ternary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) fprintf(vlog_out, ")"); } -static void emit_expr_ufunc(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) -{ - ivl_scope_t ufunc_def = ivl_expr_def(expr); - emit_scope_module_path(scope, ufunc_def); - emit_expr_func(scope, expr, ivl_scope_tname(ufunc_def)); -} - static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { char *oper = "invalid"; @@ -404,13 +494,15 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) case 'N': case 'X': case '!': - fprintf(vlog_out, "%s", oper); + fprintf(vlog_out, "(%s", oper); emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ")"); break; case '2': case 'i': case 'r': /* A cast is a noop. */ + emit_expr(scope, ivl_expr_oper1(expr), wid); break; default: fprintf(vlog_out, ""); @@ -457,7 +549,8 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) emit_expr_select(scope, expr, wid); break; case IVL_EX_SFUNC: - emit_expr_sfunc(scope, expr, wid); + fprintf(vlog_out, "%s", ivl_expr_name(expr)); + emit_expr_func(scope, expr, wid); break; case IVL_EX_SIGNAL: emit_expr_signal(scope, expr, wid); @@ -469,7 +562,8 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) emit_expr_ternary(scope, expr, wid); break; case IVL_EX_UFUNC: - emit_expr_ufunc(scope, expr, wid); + 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); diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 7da84fa7d..7e932762e 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -16,6 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +# include +# include # include "config.h" # include "vlog95_priv.h" @@ -54,30 +56,45 @@ static void emit_strength(ivl_drive_t drive1, ivl_drive_t drive0, const char *file, unsigned lineno) { assert(strength_type <= 2); - if ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG)) { + if ((strength_type == 2) && + ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG))) { fprintf(vlog_out, " ("); - if (strength_type > 0) { - 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(drive1)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " + "1 drive (%d)\n", file, lineno, + type, (int)drive1); + vlog_errors += 1; } - if (strength_type == 2) fprintf(vlog_out, ", "); - if ((strength_type & 0x01) == 0) { - if (emit_drive(drive0)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " - "0 drive (%d)\n", file, lineno, - type, (int)drive1); - vlog_errors += 1; - } - fprintf(vlog_out, "0"); + 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, ")"); + 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)"); } } @@ -108,28 +125,28 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t rise, ivl_expr_t fall, /* 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); + 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); + emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ", "); - emit_scaled_delayx(scope, fall); + 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); + emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ", "); - emit_scaled_delayx(scope, fall); + emit_scaled_delayx(scope, fall, 0); if (decay) { fprintf(vlog_out, ", "); - emit_scaled_delayx(scope, decay); + emit_scaled_delayx(scope, decay, 0); } fprintf(vlog_out, ")"); } @@ -215,13 +232,64 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm) 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); -static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) +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 net_logic = 0; + ivl_signal_t sig = 0; + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && + (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) continue; + 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_net_logic = ivl_nexus_ptr_log(nex_ptr); + ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); + if (t_lpm) { + assert(! lpm); + lpm = t_lpm; + } + if (t_net_const) { + assert(! net_const); + net_const = t_net_const; + } + if (t_net_logic) { + assert(! net_logic); + net_logic = t_net_logic; + } + if (t_sig) { + assert(! sig); + sig = t_sig; + } + } + if (lpm) { + assert(! net_const); + assert(! net_logic); + assert(! sig); + emit_lpm_as_ca(scope, lpm); + } else if (net_const) { + assert( !net_logic); + assert(! sig); + emit_const_nexus(scope, net_const); + } else if (net_logic) { + assert(! sig); + emit_logic_as_ca(scope, net_logic); + } else if (sig) { + emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0)); + } else assert(0); +} + +void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) { /* 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 net_logic = 0; @@ -229,7 +297,13 @@ static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && - (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) continue; + (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; + } 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_net_logic = ivl_nexus_ptr_log(nex_ptr); @@ -239,6 +313,10 @@ static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) 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; } @@ -255,23 +333,37 @@ static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) assert(! net_const); assert(! net_logic); assert(! sig); - fprintf(vlog_out, "("); + assert(! must_be_sig); emit_lpm_as_ca(scope, lpm); - fprintf(vlog_out, ")"); } else if (net_const) { assert( !net_logic); assert(! sig); - emit_const_nexus(scope, net_const); + 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 (net_logic) { assert(! sig); - fprintf(vlog_out, "("); + assert(! must_be_sig); emit_logic_as_ca(scope, net_logic); - fprintf(vlog_out, ")"); } else if (sig) { - emit_name_of_nexus(scope, nex); + if (must_be_sig) { + emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0)); + } else emit_name_of_nexus(scope, nex); // HERE: The assert causes pr1703959 to fail. // } else assert(0); - } else fprintf(vlog_out, ""); + } 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); } @@ -279,50 +371,72 @@ static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic) { -// HERE: Do we need to check that the pin count is correct for these? + 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)); fprintf(vlog_out, " & "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); + 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)); break; case IVL_LO_NAND: + assert(inputs == 2); + fprintf(vlog_out, "~("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); - fprintf(vlog_out, " ~& "); + fprintf(vlog_out, " & "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); + fprintf(vlog_out, ")"); break; case IVL_LO_NOR: - emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); - fprintf(vlog_out, " ~| "); - emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); - break; - case IVL_LO_NOT: - fprintf(vlog_out, "~ "); - emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); - break; - case IVL_LO_OR: + assert(inputs == 2); + fprintf(vlog_out, "~("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); + fprintf(vlog_out, ")"); + break; + case IVL_LO_NOT: + assert(inputs == 1); + fprintf(vlog_out, "(~ "); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); + fprintf(vlog_out, ")"); + break; + case IVL_LO_OR: + assert(inputs == 2); + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); + fprintf(vlog_out, " | "); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); + fprintf(vlog_out, ")"); break; case IVL_LO_XNOR: + assert(inputs == 2); + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " ~^ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); + fprintf(vlog_out, ")"); break; case IVL_LO_XOR: + assert(inputs == 2); + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " ^ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); + fprintf(vlog_out, ")"); break; default: fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Unknown logic type (%d).\n", + 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; @@ -333,73 +447,184 @@ 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)); - fprintf(vlog_out, "%s[", ivl_signal_basename(sig)); -// HERE : Need to scale this to match array base. + 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)); - fprintf(vlog_out, "]"); + 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, "{"); - for (idx = count-1; idx > 0; idx -= 1) { - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, idx)); - 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)); + fprintf(vlog_out, "}"); + } else { + for (idx = count-1; idx > 0; idx -= 1) { + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, idx)); + fprintf(vlog_out, ", "); + } + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); } - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, "}"); } -static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex) +static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, + int*base, int*array_word) { -// HERE: check to see if this is a signal, an array word or parameter. - return 0; + unsigned idx, count = ivl_nexus_ptrs(nex); + ivl_lpm_t lpm = 0; + ivl_net_const_t net_const = 0; + ivl_net_logic_t net_logic = 0; + ivl_signal_t sig = 0; + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && + (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) continue; + 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_net_logic = ivl_nexus_ptr_log(nex_ptr); + ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); + 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_net_logic) { + assert(! net_logic); + /* The real signal could be hidden behind a BUFZ gate. */ + if (ivl_logic_type(t_net_logic) == IVL_LO_BUFZ) { + assert(ivl_logic_pins(t_net_logic) == 2); + t_sig = nexus_is_signal(scope, + ivl_logic_pin(t_net_logic, 1), + base, array_word); + } else net_logic = t_net_logic; + } + 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); + } + } + if (sig) { + assert(! lpm); + assert(! net_const); + assert(! net_logic); + } + return sig; } static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm) { unsigned width = ivl_lpm_width(lpm); - unsigned base = ivl_lpm_base(lpm); - ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0)); + int 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); // HERE: variable parameter select needs to be rebuilt. - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); - if (! sig) return; -// HERE: We need the signal MSB/LSB to scale the select correctly. + 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)); + if (base) fprintf(vlog_out, " >> %d)", base); + return; + } + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + array_word += ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_word); + } + msb = ivl_signal_msb(sig); + lsb = ivl_signal_lsb(sig); fprintf(vlog_out, "["); if (width == 1) { ivl_nexus_t sel = ivl_lpm_data(lpm, 1); - if (sel) emit_nexus_as_ca(scope, sel); - else fprintf(vlog_out, "%u", base); + if (sel) { +// HERE: Need to scale the select nexus. + if ((msb >= lsb) && (lsb == 0)) { + emit_nexus_as_ca(scope, sel); + } 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 { + if (msb >= lsb) base += lsb; + else base = lsb - base; + fprintf(vlog_out, "%d", base); + } } else { -// HERE: No support for a variable select. - fprintf(vlog_out, "%u", base+width-1); - fprintf(vlog_out, ":"); - fprintf(vlog_out, "%u", base); +// 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, const char *name) +static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm) { - unsigned idx, count= ivl_lpm_size(lpm); - fprintf(vlog_out, "%s(", name);; - for (idx = count-1; idx > 0; idx -= 1) { - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, idx)); - fprintf(vlog_out, ", "); + unsigned count = ivl_lpm_size(lpm); + if (count) { + unsigned idx; + fprintf(vlog_out, "("); + for (idx = count-1; idx > 0; idx -= 1) { + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, idx)); + fprintf(vlog_out, ", "); + } + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); } - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); - fprintf(vlog_out, ")"); } static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) { switch (ivl_lpm_type(lpm)) { case IVL_LPM_ADD: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " + "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_ARRAY: emit_lpm_array(scope, lpm); @@ -410,42 +635,56 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); break; case IVL_LPM_CMP_EEQ: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " === "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_EQ: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " == "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_GE: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " >= "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_GT: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " > "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_NE: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " != "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_NEE: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " !== "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; 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)); fprintf(vlog_out, " / "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_MOD: // HERE: Need to check if this LPM is IVL_VT_REAL. @@ -455,21 +694,27 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; } + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " %% "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_MULT: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " * "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_MUX: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_select(lpm)); fprintf(vlog_out, " ? "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); fprintf(vlog_out, " : "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_PART_PV: emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); @@ -478,28 +723,34 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) emit_lpm_part_select(scope, lpm); break; case IVL_LPM_RE_AND: - fprintf(vlog_out, " & "); + fprintf(vlog_out, "(&"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_RE_NAND: - fprintf(vlog_out, " ~& "); + fprintf(vlog_out, "(~&"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_RE_NOR: - fprintf(vlog_out, " ~| "); + fprintf(vlog_out, "(~|"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_RE_OR: - fprintf(vlog_out, " | "); + fprintf(vlog_out, "(|"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_RE_XOR: - fprintf(vlog_out, " ^ "); + fprintf(vlog_out, "(^"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_RE_XNOR: - fprintf(vlog_out, " ~^ "); + fprintf(vlog_out, "(~^"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, ")"); break; case IVL_LPM_REPEAT: fprintf(vlog_out, "{%u{", ivl_lpm_size(lpm)); @@ -507,29 +758,37 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) fprintf(vlog_out, "}}"); break; case IVL_LPM_SFUNC: - emit_lpm_func(scope, lpm, ivl_lpm_string(lpm)); + 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)); fprintf(vlog_out, " << "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_SHIFTR: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " >> "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_SIGN_EXT: // HERE: Do we need to extend here? emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); break; case IVL_LPM_SUB: + fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0)); fprintf(vlog_out, " - "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1)); + fprintf(vlog_out, ")"); break; case IVL_LPM_UFUNC: - emit_lpm_func(scope, lpm, ivl_scope_name(ivl_lpm_define(lpm))); + emit_scope_path(scope, ivl_lpm_define(lpm)); + emit_lpm_func(scope, lpm); break; default: fprintf(vlog_out, ""); @@ -577,8 +836,48 @@ static void emit_posedge_dff_prim() static unsigned need_posedge_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) { + fprintf(vlog_out, +"\n" +"/*\n" +" * This is the copyright information for the following primitive(s)\n" +" * (library elements).\n" +" *\n" +" * Copyright (C) 2011 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(); } @@ -593,8 +892,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) unsigned emitted, have_data, have_sset; const char *aset_bits = 0; const char *sset_bits = 0; - /* For now we and the compiler only support a width of 1. */ - assert(ivl_lpm_width(lpm) == 1); + /* For now we only support a width of 1 for these bits. */ if (aset_expr) { assert(ivl_expr_width(aset_expr) == 1); aset_bits = ivl_expr_bits(aset_expr); @@ -614,7 +912,11 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) } 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. */ + /* 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)); @@ -695,13 +997,102 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) 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); + } + msb = ivl_signal_msb(sig); + lsb = ivl_signal_lsb(sig); + 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, "]"); +} + 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 (ivl_lpm_type(lpm) == IVL_LPM_FF) { + if (type == IVL_LPM_FF) { emit_lpm_ff(scope, lpm); return; } @@ -713,8 +1104,8 @@ void emit_lpm(ivl_scope_t scope, ivl_lpm_t lpm) ivl_lpm_delay(lpm, 2), 3); fprintf(vlog_out, " "); -// HERE: No support for an L-value concatenation. - emit_name_of_nexus(scope, output); + if (type == IVL_LPM_PART_PV) emit_lpm_part_pv(scope, lpm); + else emit_name_of_nexus(scope, output); fprintf(vlog_out, " = "); emit_lpm_as_ca(scope, lpm); fprintf(vlog_out, ";"); @@ -760,7 +1151,7 @@ static void emit_bufz(ivl_scope_t scope, ivl_net_logic_t nlogic) static void emit_and_save_udp_name(ivl_net_logic_t nlogic){ ivl_udp_t udp = ivl_logic_udp(nlogic); assert(udp); - fprintf(vlog_out, "%s", ivl_udp_name(udp)); + emit_id(ivl_udp_name(udp)); add_udp_to_list(udp); } @@ -788,9 +1179,11 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) // 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. +// short circuit. Verify input count. unsigned idx, count, dly_count, strength_type = 2; unsigned outputs = 1; + unsigned width = ivl_logic_width(nlogic); + 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; @@ -857,11 +1250,13 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) 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: @@ -905,8 +1300,28 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) 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 skip the name. -// fprintf(vlog_out, " %s(", ivl_logic_basename(nlogic)); +// 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; + 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; @@ -919,7 +1334,13 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx)); fprintf(vlog_out, ", "); } - emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, count)); + if (strength_type == 2) { + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx)); + } 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"); @@ -990,7 +1411,6 @@ void emit_tran(ivl_scope_t scope, ivl_switch_t tran) } fprintf(vlog_out, ");"); if (emit_file_line) { -assert(ivl_switch_lineno(tran)); fprintf(vlog_out, " /* %s:%u */", ivl_switch_file(tran), ivl_switch_lineno(tran)); @@ -1002,12 +1422,17 @@ 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 = (unsigned long) 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. */ + /* 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), @@ -1018,11 +1443,16 @@ void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig) ivl_const_delay(net_const, 1), ivl_const_delay(net_const, 2), 3); - fprintf(vlog_out, " %s = ", ivl_signal_basename(sig)); + 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 *) ((unsigned long) ivl_nexus_get_private(nex) + 1U)); return; } /* We must find the constant in the nexus. */ diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index 9388ac90c..0cf1c1489 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +# include # include # include # include "config.h" @@ -61,13 +62,109 @@ void emit_scaled_delay(ivl_scope_t scope, uint64_t 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)); + return; + } + } + emit_expr(scope, expr, 0); +} + +/* + * 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) +void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) { - if (ivl_expr_type(expr) == IVL_EX_NUMBER) { + 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); @@ -93,80 +190,82 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr) } else { int exponent = ivl_scope_time_units(scope) - sim_precision; assert(exponent >= 0); - if (exponent == 0) emit_expr(scope, expr, 0); - /* A real delay variable is not scaled by the compiler. */ - else if (ivl_expr_type(expr) == IVL_EX_SIGNAL) { - ivl_signal_t sig = ivl_expr_signal(expr); - if (ivl_signal_data_type(sig) != IVL_VT_REAL) { + 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 real " - "delay variables are scaled at run " - "time.\n", ivl_expr_file(expr), + 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_expr(scope, expr, 0); - return; + emit_delay(scope, expr, is_stmt); } else { - uint64_t scale_val, scale = 1; - int rtype; + uint64_t iscale = 1; + unsigned rtn; assert(! ivl_expr_signed(expr)); - /* This is as easy as removing the multiple that was - * added to scale the value to the simulation time, - * but we need to verify that the scaling value is - * correct first. */ - if ((ivl_expr_type(expr) != IVL_EX_BINARY) || - (ivl_expr_opcode(expr) != '*') || - (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value cannot be scaled.\n ", - ivl_expr_file(expr), - ivl_expr_lineno(expr)); - vlog_errors += 1; - return; - } - scale_val = get_uint64_from_number(ivl_expr_oper2(expr), - &rtype); - if (rtype > 0) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value scale coefficient " - "was greater then 64 bits (%d).\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: Variable time " - "expression/value scale coefficient " - "has an undefined bit.\n", - ivl_expr_file(expr), - ivl_expr_lineno(expr)); - vlog_errors += 1; - return; - } + /* Calculate the integer time scaling coefficient. */ while (exponent > 0) { - scale *= 10; + iscale *= 10; exponent -= 1; } - if (scale != scale_val) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value scale coefficient " - "did not match expected value " - "(%"PRIu64" != %"PRIu64").\n", - ivl_expr_file(expr), - ivl_expr_lineno(expr), - scale, scale_val); - vlog_errors += 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) != 'i')) { + 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; } - emit_expr(scope, ivl_expr_oper1(expr), 0); + fprintf(vlog_out, ""); } } } @@ -255,42 +354,21 @@ void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, unsigned width, int msb, int lsb) { if (msb >= lsb) { - if (ivl_expr_type(expr) == IVL_EX_NUMBER) { - int rtype; - int64_t value = get_valid_int64_from_number(expr, &rtype, - "range value"); - if (rtype) return; - value += lsb; - fprintf(vlog_out, "[%"PRId64":%"PRId64"]", - value + (int64_t)(width - 1), value); - } else { -// HERE: Need to scale the select expression and create a concatenation of -// variable bit selects for that. We need the signal name as well. -// As an optimization determine if this is an up or down to simplify -// the generated expression. - fprintf(vlog_out, "[:]"); - fprintf(stderr, "%s:%u: vlog95 error: Indexed part-selects " - "are not currently supported.\n", - ivl_expr_file(expr), ivl_expr_lineno(expr)); - vlog_errors += 1; - } + int rtype; + int64_t value = get_valid_int64_from_number(expr, &rtype, + "range value"); + if (rtype) return; + value += lsb; + fprintf(vlog_out, "[%"PRId64":%"PRId64"]", + value + (int64_t)(width - 1), value); } else { - if (ivl_expr_type(expr) == IVL_EX_NUMBER) { - int rtype; - int64_t value = get_valid_int64_from_number(expr, &rtype, - "range value"); - if (rtype) return; - value = (int64_t)lsb - value; - fprintf(vlog_out, "[%"PRId64":%"PRId64"]", - value - (int64_t)(width - 1), value); - } else { -// HERE: Do basically the same as above. - fprintf(vlog_out, "[:]"); - fprintf(stderr, "%s:%u: vlog95 error: Indexed part-selects " - "are not currently supported.\n", - ivl_expr_file(expr), ivl_expr_lineno(expr)); - vlog_errors += 1; - } + int rtype; + int64_t value = get_valid_int64_from_number(expr, &rtype, + "range value"); + if (rtype) return; + value = (int64_t)lsb - value; + fprintf(vlog_out, "[%"PRId64":%"PRId64"]", + value - (int64_t)(width - 1), value); } } @@ -377,8 +455,8 @@ static unsigned find_signal_in_nexus(ivl_scope_t scope, ivl_nexus_t nex) tmp_idx += ivl_signal_array_base(sig); fprintf(stderr, "[%"PRId64"]", tmp_idx); } - fprintf(stderr, ") found for nexus "); - fprintf(stderr, "(%s", ivl_signal_basename(use_sig)); + fprintf(stderr, ") found for nexus (%s", + ivl_signal_basename(use_sig)); if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx); fprintf(stderr, ")\n"); } else { @@ -397,9 +475,8 @@ static unsigned find_signal_in_nexus(ivl_scope_t scope, ivl_nexus_t nex) } } -// HERE: Check bit, part, etc. selects to make sure they work as well. if (use_sig) { - fprintf(vlog_out, "%s", ivl_signal_basename(use_sig)); + emit_id(ivl_signal_basename(use_sig)); if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx); return 1; } @@ -454,12 +531,16 @@ static unsigned find_const_nexus(ivl_scope_t scope, ivl_nexus_t nex) // HERE: Does this work correctly with an array reference created from @*? void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex) { + 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. */ - if (find_signal_in_nexus(get_module_scope(scope), nex)) return; + mod_scope = get_module_scope(scope); + if (mod_scope != scope) { + if (find_signal_in_nexus(mod_scope, nex)) return; + } /* If there is no signals driving this then look for a constant. */ if (find_const_nexus(scope, nex)) return; @@ -468,6 +549,8 @@ void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex) // 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. + fprintf(stderr, "?:?: vlog95 sorry: Unable to find nexus name.\n"); + vlog_errors += 1; fprintf(vlog_out, ""); } @@ -486,6 +569,19 @@ ivl_scope_t get_module_scope(ivl_scope_t scope) return scope; } +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); + } + /* Print the base scope. */ + 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 @@ -498,30 +594,48 @@ 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 = get_module_scope(scope); + ivl_scope_t call_mod_scope = get_module_scope(call_scope); + if (mod_scope != call_mod_scope) { - /* Trim off the top of the call name if it exactly matches - * the module scope of the caller. */ - char *sc_name = strdup(ivl_scope_name(mod_scope)); - const char *sc_ptr = sc_name; - char *call_name = strdup(ivl_scope_name(call_mod_scope)); - const char *call_ptr = call_name; - while ((*sc_ptr == *call_ptr) && - (*sc_ptr != 0) && (*call_ptr != 0)) { - sc_ptr += 1; - call_ptr += 1; + 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; + } } - if (*sc_ptr == 0) { - assert(*call_ptr == '.'); - call_ptr += 1; - } else { - call_ptr = call_name; - } - fprintf(vlog_out, "%s.", call_ptr); - free(sc_name); - free(call_name); } } +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, "."); + } + /* Print the base scope. */ + 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 @@ -534,28 +648,34 @@ void emit_scope_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) { - fprintf(vlog_out, "%s", ivl_scope_basename(call_scope)); + emit_id(ivl_scope_basename(call_scope)); } else { - /* Trim off the top of the call name if it exactly matches - * the module scope of the caller. */ - char *sc_name = strdup(ivl_scope_name(mod_scope)); - const char *sc_ptr = sc_name; - char *call_name = strdup(ivl_scope_name(call_scope)); - const char *call_ptr = call_name; - while ((*sc_ptr == *call_ptr) && - (*sc_ptr != 0) && (*call_ptr != 0)) { - sc_ptr += 1; - call_ptr += 1; - } - if (*sc_ptr == 0) { - assert(*call_ptr == '.'); - call_ptr += 1; - } else { - call_ptr = call_name; - } - fprintf(vlog_out, "%s", call_ptr); - free(sc_name); - free(call_name); + 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; + } + } + /* 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); +} diff --git a/tgt-vlog95/numbers.c b/tgt-vlog95/numbers.c index b89d6d8b5..ecf72dcc2 100644 --- a/tgt-vlog95/numbers.c +++ b/tgt-vlog95/numbers.c @@ -51,7 +51,7 @@ static int32_t get_int32_from_bits(const char *bits, unsigned nbits, else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ - if ((idx == 0) && (trim_wid == 1)) { + if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } @@ -190,7 +190,7 @@ uint64_t get_uint64_from_number(ivl_expr_t expr, int *result_type) else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ - if ((idx == 0) && (trim_wid == 1)) { + if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } @@ -236,7 +236,7 @@ int64_t get_int64_from_number(ivl_expr_t expr, int *result_type) else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ - if ((idx == 0) && (trim_wid == 1)) { + if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index eba4fac0f..ae2914378 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -76,14 +76,21 @@ void emit_sig_file_line(ivl_signal_t 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"); +} + 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 %s;", ivl_signal_basename(sig)); - emit_sig_file_line(sig); - fprintf(vlog_out, "\n"); + 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), @@ -92,9 +99,8 @@ void emit_var_def(ivl_signal_t sig) vlog_errors += 1; } } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { - fprintf(vlog_out, "real %s;", ivl_signal_basename(sig)); - emit_sig_file_line(sig); - fprintf(vlog_out, "\n"); + 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), @@ -105,9 +111,9 @@ void emit_var_def(ivl_signal_t sig) } else { int msb = ivl_signal_msb(sig); int lsb = ivl_signal_lsb(sig); - fprintf(vlog_out, "reg"); - if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); - fprintf(vlog_out, " %s", ivl_signal_basename(sig)); + fprintf(vlog_out, "reg "); + 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); @@ -178,23 +184,24 @@ void emit_net_def(ivl_scope_t scope, ivl_signal_t sig) if (ivl_signal_local(sig)) return; fprintf(vlog_out, "%*c", indent, ' '); if (ivl_signal_data_type(sig) == IVL_VT_REAL){ - fprintf(vlog_out, "wire %s;\n", ivl_signal_basename(sig)); + 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_signed(sig)) { - fprintf(vlog_out, "wire"); - if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); - fprintf(vlog_out, " %s;\n", ivl_signal_basename(sig)); + 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: Signed 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); - fprintf(vlog_out, " %s;\n", ivl_signal_basename(sig)); + 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)); @@ -205,59 +212,58 @@ void emit_net_def(ivl_scope_t scope, ivl_signal_t sig) 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"); + fprintf(vlog_out, "wire "); break; case IVL_SIT_TRI0: - fprintf(vlog_out, "tri0"); + fprintf(vlog_out, "tri0 "); break; case IVL_SIT_TRI1: - fprintf(vlog_out, "tri1"); + fprintf(vlog_out, "tri1 "); break; case IVL_SIT_TRIAND: - fprintf(vlog_out, "wand"); + fprintf(vlog_out, "wand "); break; case IVL_SIT_TRIOR: - fprintf(vlog_out, "wor"); + fprintf(vlog_out, "wor "); break; default: - fprintf(vlog_out, ""); + 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 (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); - fprintf(vlog_out, " %s;", ivl_signal_basename(sig)); - emit_sig_file_line(sig); - fprintf(vlog_out, "\n"); + 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 char *get_mangled_name(ivl_scope_t scope, unsigned root) +static void emit_mangled_name(ivl_scope_t scope, unsigned root) { - char *name; - /* If the module has parameters and it's not a root module than it - * may not be unique so we create a mangled name version instead. */ + /* 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) { - unsigned idx; + char *name; size_t len = strlen(ivl_scope_name(scope)) + - strlen(ivl_scope_tname(scope)) + 2; + strlen(ivl_scope_tname(scope)) + 3; name = (char *)malloc(len); (void) strcpy(name, ivl_scope_tname(scope)); - (void) strcat(name, "_"); + (void) strcat(name, "["); (void) strcat(name, ivl_scope_name(scope)); + (void) strcat(name, "]"); assert(name[len-1] == 0); - for (idx = 0; idx < len; idx += 1) { - if (name[idx] == '.') name[idx] = '_'; - } + /* Emit the mangled name as an escaped identifier. */ + fprintf(vlog_out, "\\%s ", name); + free(name); } else { - name = strdup(ivl_scope_tname(scope)); + emit_id(ivl_scope_tname(scope)); } - return name; } /* @@ -279,8 +285,9 @@ void emit_scope_variables(ivl_scope_t 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 %s = ", indent, ' ', - ivl_parameter_basename(par)); + fprintf(vlog_out, "%*cparameter ", indent, ' '); + emit_id(ivl_parameter_basename(par)); + fprintf(vlog_out, " = "); emit_expr(scope, pex, 0); fprintf(vlog_out, ";"); if (emit_file_line) { @@ -317,8 +324,9 @@ void emit_scope_variables(ivl_scope_t scope) if (ivl_event_nany(event)) continue; if (ivl_event_npos(event)) continue; if (ivl_event_nneg(event)) continue; - fprintf(vlog_out, "%*cevent %s;", indent, ' ', - ivl_event_basename(event)); + 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), @@ -339,6 +347,176 @@ static void emit_scope_file_line(ivl_scope_t 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)); + for (idx = 1; idx < count; idx += 1) { + fprintf(vlog_out, ", "); + emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, idx)); + } + fprintf(vlog_out, ")"); +} + +static ivl_signal_t get_port_from_nexus(ivl_scope_t scope, ivl_nexus_t nex) +{ + assert(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 (t_sig) { + if (ivl_signal_scope(t_sig) != scope) continue; + assert(! sig); + sig = t_sig; + } + } + return sig; +} + +static void emit_sig_type(ivl_signal_t sig) +{ + ivl_signal_type_t type = ivl_signal_type(sig); + assert(ivl_signal_dimensions(sig) == 0); + /* Check to see if we have a variable (reg) or a net. */ + if (type == IVL_SIT_REG) { + 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 = ivl_signal_msb(sig); + int lsb = ivl_signal_lsb(sig); + if (msb != 0 || lsb != 0) { + fprintf(vlog_out, " [%d:%d]", msb, lsb); + } + if (ivl_signal_signed(sig)) { + 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; + } + } + } else { + assert(type == IVL_SIT_TRI); + 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 = ivl_signal_msb(sig); + int lsb = ivl_signal_lsb(sig); + if (ivl_signal_signed(sig)) { + 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, " "); + 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 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); + 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) +{ + ivl_nexus_t nex = ivl_scope_mod_port(scope, idx); + ivl_signal_t port = get_port_from_nexus(scope, nex); + /* 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, 0)); + /* 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, 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 (count) fprintf(vlog_out, "\n"); +} + /* * 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 @@ -384,26 +562,26 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) { ivl_scope_type_t sc_type = ivl_scope_type(scope); unsigned is_auto = ivl_scope_is_auto(scope); - unsigned idx, count, start = 0; - char *name; + unsigned idx, count; /* Output the scope definition. */ switch (sc_type) { case IVL_SCT_MODULE: assert(!is_auto); - name = get_mangled_name(scope, !parent && !emitting_scopes); /* This is an instantiation. */ if (parent) { assert(indent != 0); - /* If the module has parameters than it may not be unique + /* If the module has parameters then it may not be unique * so we create a mangled name version instead. */ - fprintf(vlog_out, "\n%*c%s %s(", indent, ' ', name, - ivl_scope_basename(scope)); -// HERE: Still need to add port information. + 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"); - free(name); num_scopes_to_emit += 1; scopes_to_emit = realloc(scopes_to_emit, num_scopes_to_emit * sizeof(ivl_scope_t)); @@ -422,9 +600,9 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) "file %s at line %u. */\n", ivl_scope_def_file(scope), ivl_scope_def_lineno(scope)); - fprintf(vlog_out, "module %s", name); - free(name); -// HERE: Still need to add port information. + fprintf(vlog_out, "module "); + emit_mangled_name(scope, !parent && !emitting_scopes); + emit_module_ports(scope); break; case IVL_SCT_FUNCTION: assert(indent != 0); @@ -432,8 +610,8 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) assert(ivl_scope_ports(scope) >= 2); /* The function return information is the zero port. */ emit_func_return(ivl_scope_port(scope, 0)); - start = 1; - fprintf(vlog_out, " %s", ivl_scope_tname(scope)); + 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), @@ -443,8 +621,8 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) break; case IVL_SCT_TASK: assert(indent != 0); - fprintf(vlog_out, "\n%*ctask %s", indent, ' ', - ivl_scope_tname(scope)); + 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), @@ -470,35 +648,11 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) indent += indent_incr; /* Output the scope ports for this scope. */ - count = ivl_scope_ports(scope); - for (idx = start; idx < count; idx += 1) { - fprintf(vlog_out, "%*c", indent, ' '); - ivl_signal_t port = ivl_scope_port(scope, idx); - 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; - } - fprintf(vlog_out, " %s;", ivl_signal_basename(port)); - emit_sig_file_line(port); - fprintf(vlog_out, " \n"); + if (sc_type == IVL_SCT_MODULE) { + emit_module_port_defs(scope); + } else { + emit_task_func_port_defs(scope); } - if (count) fprintf(vlog_out, "\n"); emit_scope_variables(scope); @@ -539,7 +693,9 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) switch (sc_type) { case IVL_SCT_MODULE: assert(indent == 0); - fprintf(vlog_out, "endmodule /* %s */\n", ivl_scope_tname(scope)); + 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"); } diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index d7671fdcc..96eac0654 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -80,46 +80,138 @@ static void emit_stmt_inter_delay(ivl_scope_t scope, ivl_statement_t stmt) } if (delay) { assert(nevents == 0); - fprintf(vlog_out, "#"); - emit_expr(scope, delay, 0); - fprintf(vlog_out, " "); + fprintf(vlog_out, "#("); + emit_scaled_delayx(scope, delay, 1); + fprintf(vlog_out, ") "); } } -static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) +static void emit_stmt_lval_name(ivl_scope_t scope, ivl_lval_t lval, + ivl_signal_t sig) { - ivl_expr_t expr; - ivl_signal_t sig = ivl_lval_sig(lval); - unsigned width = ivl_lval_width(lval); - int msb, lsb; - assert(width > 0); - emit_scope_module_path(scope, ivl_signal_scope(sig)); - fprintf(vlog_out, "%s", ivl_signal_basename(sig)); - /* Check to see if we have an array word access. */ - expr = ivl_lval_idx(lval); - if (expr) { + 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, expr, msb, lsb); + emit_scaled_expr(scope, array_idx, msb, lsb); fprintf(vlog_out, "]"); } +} - /* If there are no selects then just return. */ - if (width == ivl_signal_width(sig)) return; +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, "]}"); + } + } +} + +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); + + /* 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. */ lsb = ivl_signal_lsb(sig); msb = ivl_signal_msb(sig); + 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, ivl_lval_part_off(lval), msb, lsb); + emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); } else { - emit_scaled_range(scope, ivl_lval_part_off(lval), width, msb, lsb); + /* A constant part select. */ + if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { + emit_stmt_lval_name(scope, lval, sig); + emit_scaled_range(scope, sel_expr, width, msb, lsb); + /* An indexed part select. */ + } else { + assert(sel_type != IVL_SEL_OTHER); + emit_stmt_lval_ips(scope, lval, sig, sel_expr, sel_type, + width, msb, lsb); + } } } @@ -150,18 +242,20 @@ static unsigned emit_stmt_lval(ivl_scope_t scope, ivl_statement_t stmt) } /* - * Icarus translated = into + * Icarus translated = into * begin * = ; - * = ; + * = ; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ -static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) +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; @@ -173,7 +267,9 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a delayx. */ delay = ivl_stmt_block_stmt(stmt, 1); - if (ivl_statement_type(delay) != IVL_ST_DELAYX) return 0; + 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; @@ -193,16 +289,27 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) /* It must not be an array word. */ if (ivl_expr_oper1(rval)) return 0; rsig = ivl_expr_signal(rval); - /* And finally the two signals must be the same. */ + /* 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(), ' '); -// HERE: Do we need to calculate the width? The compiler should have already -// done this for us. wid = emit_stmt_lval(scope, delayed_assign); - fprintf(vlog_out, " = #("); - emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay)); + 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); fprintf(vlog_out, ";"); @@ -213,19 +320,93 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) } /* - * Icarus translated = into + * 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) +{ + unsigned wid; + 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. */ +// HERE: Do we need to calculate the width? The compiler should have already +// done this for us. + wid = emit_stmt_lval(scope, assign); + fprintf(vlog_out, " = "); + emit_expr(scope, ivl_stmt_rval(assign), wid); + fprintf(vlog_out, "; "); + /* Emit the condition. */ + emit_expr(scope, ivl_stmt_cond_expr(while_lp), 0); + fprintf(vlog_out, "; "); + /* Emit in increment statement. */ +// HERE: Do we need to calculate the width? The compiler should have already +// done this for us. + wid = emit_stmt_lval(scope, incr_assign); + fprintf(vlog_out, " = "); + emit_expr(scope, ivl_stmt_rval(incr_assign), wid); + 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 find_event_assign(ivl_scope_t scope, ivl_statement_t stmt) +static unsigned is_repeat_event_assign(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; - ivl_statement_t assign, event, event_assign, repeat = 0; + ivl_statement_t assign, event, event_assign, repeat; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; @@ -236,14 +417,11 @@ static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt) 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. */ - event = ivl_stmt_block_stmt(stmt, 1); - if (ivl_statement_type(event) != IVL_ST_REPEAT && - ivl_statement_type(event) != IVL_ST_WAIT) return 0; - if (ivl_statement_type(event) == IVL_ST_REPEAT) { - repeat = event; - event = ivl_stmt_sub_stmt(repeat); - if (ivl_statement_type(event) != IVL_ST_WAIT) return 0; - } + 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; @@ -263,16 +441,21 @@ static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt) /* It must not be an array word. */ if (ivl_expr_oper1(rval)) return 0; rsig = ivl_expr_signal(rval); - /* And finally the two signals must be the same. */ + /* 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(), ' '); -// HERE: Do we need to calculate the width? The compiler should have already -// done this for us. wid = emit_stmt_lval(scope, event_assign); fprintf(vlog_out, " ="); - single_indent = 1; if (repeat) { fprintf(vlog_out, " repeat ("); emit_expr(scope, ivl_stmt_cond_expr(repeat), 0); @@ -298,7 +481,7 @@ static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt) * This routine looks for this pattern and turns it back into a * wait statement. */ -static unsigned find_wait(ivl_scope_t scope, ivl_statement_t stmt) +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; @@ -317,7 +500,7 @@ static unsigned find_wait(ivl_scope_t scope, ivl_statement_t stmt) 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; - /* And has a second operator that is a constant 1'b1. */ + /* 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; @@ -325,6 +508,12 @@ static unsigned find_wait(ivl_scope_t scope, ivl_statement_t stmt) 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(), ' '); @@ -415,7 +604,7 @@ typedef struct port_expr_s { union { ivl_statement_t lval; ivl_expr_t rval; - }; + } expr; } *port_expr_t; /* @@ -424,17 +613,16 @@ typedef struct port_expr_s { static void emit_port(ivl_scope_t scope, struct port_expr_s port_expr) { if (port_expr.type == IVL_SIP_INPUT) { - emit_expr(scope, port_expr.rval, 0); + emit_expr(scope, port_expr.expr.rval, 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.lval); + (void) emit_stmt_lval(scope, port_expr.expr.lval); } } - /* - * Icarus encodes a task call with arguments as: + * Icarus encodes a user task call with arguments as: * begin * = * ... @@ -454,6 +642,7 @@ static unsigned is_utask_call_with_args(ivl_scope_t scope, { 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; /* Check to see if the block is of the basic form first. */ @@ -477,23 +666,24 @@ static unsigned is_utask_call_with_args(ivl_scope_t scope, 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].rval = 0; + port_exprs[idx].expr.rval = 0; } - /* Now do a detailed check that the arguments are correct. */ + /* 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) { + if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } port_exprs[port].type = IVL_SIP_INPUT; - port_exprs[port].rval = ivl_stmt_rval(assign); + 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) { + if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } @@ -504,7 +694,12 @@ static unsigned is_utask_call_with_args(ivl_scope_t scope, } else { port_exprs[port].type = IVL_SIP_OUTPUT; } - port_exprs[port].lval = assign; + 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. */ @@ -562,7 +757,6 @@ static void emit_stmt_assign_nb(ivl_scope_t scope, ivl_statement_t stmt) static void emit_stmt_block(ivl_scope_t scope, ivl_statement_t stmt) { - if (is_utask_call_with_args(scope, stmt)) return; fprintf(vlog_out, "%*cbegin", get_indent(), ' '); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); @@ -573,8 +767,8 @@ static void emit_stmt_block(ivl_scope_t scope, ivl_statement_t stmt) 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: %s", get_indent(), ' ', - ivl_scope_basename(my_scope)); + 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); @@ -584,23 +778,23 @@ static void emit_stmt_block_named(ivl_scope_t scope, ivl_statement_t stmt) static void emit_stmt_case(ivl_scope_t scope, ivl_statement_t stmt) { - char *name; + char *case_type; unsigned idx, default_case, count = ivl_stmt_case_count(stmt); switch(ivl_statement_type(stmt)) { case IVL_ST_CASE: case IVL_ST_CASER: - name = "case"; + case_type = "case"; break; case IVL_ST_CASEX: - name = "casex"; + case_type = "casex"; break; case IVL_ST_CASEZ: - name = "casez"; + case_type = "casez"; break; default: assert(0); } - fprintf(vlog_out, "%*c%s (", get_indent(), ' ', name); + fprintf(vlog_out, "%*c%s (", get_indent(), ' ', case_type); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); @@ -704,7 +898,7 @@ static void emit_stmt_delay(ivl_scope_t scope, ivl_statement_t 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)); + emit_scaled_delayx(scope, ivl_stmt_delay_expr(stmt), 1); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; @@ -755,8 +949,8 @@ static void emit_stmt_fork(ivl_scope_t scope, ivl_statement_t stmt) 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: %s", get_indent(), ' ', - ivl_scope_basename(my_scope)); + 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); @@ -880,9 +1074,11 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) if (ivl_stmt_block_scope(stmt)) { emit_stmt_block_named(scope, stmt); } else { - if (find_delayed_assign(scope, stmt)) break; - if (find_event_assign(scope, stmt)) break; - if (find_wait(scope, stmt)) break; + 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; @@ -963,6 +1159,7 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) void emit_process(ivl_scope_t scope, ivl_process_t proc) { + ivl_statement_t stmt; fprintf(vlog_out, "\n%*c", get_indent(), ' '); switch (ivl_process_type(proc)) { case IVL_PR_INITIAL: @@ -984,6 +1181,11 @@ void emit_process(ivl_scope_t scope, ivl_process_t proc) ivl_process_file(proc), ivl_process_lineno(proc)); } - single_indent = 1; - emit_stmt(scope, ivl_process_stmt(proc)); + stmt = ivl_process_stmt(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); + } } diff --git a/tgt-vlog95/udp.c b/tgt-vlog95/udp.c index 30cffd3b3..e95d44135 100644 --- a/tgt-vlog95/udp.c +++ b/tgt-vlog95/udp.c @@ -183,24 +183,30 @@ static void emit_udp(ivl_udp_t udp) 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 %s (", ivl_udp_name(udp)); -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "out"); + 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, ", in%u", idx); + fprintf(vlog_out, ", "); + emit_id(ivl_udp_port(udp, idx)); } fprintf(vlog_out, ");\n"); -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "%*coutput out;\n", indent_incr, ' '); + 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 in%u;\n", indent_incr, ' ', idx); + 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"); -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "%*creg out;\n", indent_incr, ' '); + fprintf(vlog_out, "%*creg ", indent_incr, ' '); + emit_id(ivl_udp_port(udp, 0)); + fprintf(vlog_out, ";\n"); switch (init) { case '0': case '1': @@ -209,9 +215,9 @@ static void emit_udp(ivl_udp_t udp) init = 'x'; break; } -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "%*cinitial out = 1'b%c;\n", - indent_incr, ' ', init); + 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, ' '); diff --git a/tgt-vlog95/vlog95.c b/tgt-vlog95/vlog95.c index 4d69e6642..5b00f7739 100644 --- a/tgt-vlog95/vlog95.c +++ b/tgt-vlog95/vlog95.c @@ -105,7 +105,7 @@ int target_design(ivl_design_t des) char *eptr; long fl_value = strtol(fileline, &eptr, 0); /* Nothing usable in the file/line string. */ - if (spacing == eptr) { + if (fileline == eptr) { fprintf(stderr, "vlog95 error: Unable to extract file/line " "information from string: %s\n", fileline); return 1; diff --git a/tgt-vlog95/vlog95_priv.h b/tgt-vlog95/vlog95_priv.h index ecf22da6c..2698be1a8 100644 --- a/tgt-vlog95/vlog95_priv.h +++ b/tgt-vlog95/vlog95_priv.h @@ -69,15 +69,19 @@ 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); +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); +extern void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex); +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(); @@ -86,6 +90,7 @@ extern void add_udp_to_list(ivl_udp_t udp); extern void emit_udp_list(); 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); diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 628210ad9..44d6840ae 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -22,6 +22,7 @@ # include # include # include +# include # include # include "ivl_alloc.h" @@ -564,7 +565,7 @@ static void display_multi_driver_error(ivl_nexus_t nex, unsigned ndrivers, mdriver_type_t type) { unsigned idx; - unsigned scope_len = -1; + unsigned scope_len = UINT_MAX; ivl_signal_t sig = 0; /* Find the signal. */ for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { diff --git a/tgt-vvp/draw_ufunc.c b/tgt-vvp/draw_ufunc.c index 554462193..a7b6ecd10 100644 --- a/tgt-vvp/draw_ufunc.c +++ b/tgt-vvp/draw_ufunc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -51,7 +51,7 @@ static void function_argument_real(ivl_signal_t port, ivl_expr_t expr) /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); - fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", port, res); + fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", port, res); clr_word(res); } @@ -180,9 +180,9 @@ int draw_ufunc_real(ivl_expr_t expr) /* Call the function */ - fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); + 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"); + fprintf(vvp_out, " %%join;\n"); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index 86279578d..601b04378 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -80,7 +80,7 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result, switch (ivl_expr_type(expr)) { case IVL_EX_SIGNAL: - /* If the signal node is narrower then the signal itself, + /* 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. @@ -287,7 +287,7 @@ static void draw_vpi_taskfunc_args(const char*call_string, assert((unsigned)(dp - buffer) <= sizeof buffer); } args[idx].text = strdup(buffer); - continue; + continue; } case IVL_EX_STRING: diff --git a/tgt-vvp/eval_bool.c b/tgt-vvp/eval_bool.c index 5a8c1ce6f..e37a51f75 100644 --- a/tgt-vvp/eval_bool.c +++ b/tgt-vvp/eval_bool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -54,7 +54,8 @@ static int eval_bool64_logic(ivl_expr_t expr) if (ivl_expr_signed(expr)) s_flag = "/s"; - fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", s_flag, res, tmp.base, tmp.wid); + fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", s_flag, res, + tmp.base, tmp.wid); clr_vector(tmp); return res; @@ -75,7 +76,7 @@ static int draw_number_bool64(ivl_expr_t expr) 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); + fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", res, low, hig); return res; } diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 9a20299b4..8d10e52fb 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -750,7 +750,7 @@ static struct vector_info draw_binary_expr_lor(ivl_expr_t expr, unsigned wid, if (wid == 1) { if (lv.base >= 4 && lv.base < 8) { unsigned tmp = allocate_vector(1); - fprintf(vvp_out, " %%mov %u, %u, 1;\n", tmp, lv.base); + fprintf(vvp_out, " %%mov %u, %u, 1;\n", tmp, lv.base); lv.base = tmp; } return lv; @@ -932,7 +932,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(le,16,0) && !number_is_unknown(le)) { long imm = get_number_immediate(le); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, rv.base, imm, rv.wid); } else { lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ); @@ -948,7 +948,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(re,16,0) && !number_is_unknown(re)) { long imm = get_number_immediate(re); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, lv.base, imm, lv.wid); } else { rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ); @@ -964,7 +964,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(re,16,0) && !number_is_unknown(re)) { long imm = get_number_immediate(re); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, lv.base, imm, lv.wid); } else { rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ); @@ -979,7 +979,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(le,16,0) && !number_is_unknown(le)) { long imm = get_number_immediate(le); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, rv.base, imm, rv.wid); } else { lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ); @@ -1039,7 +1039,7 @@ static struct vector_info draw_logic_immediate(ivl_expr_t expr, switch (ivl_expr_opcode(expr)) { case '&': - fprintf(vvp_out, " %%andi %u, %lu, %u;\n", lv.base, imm, lv.wid); + fprintf(vvp_out, " %%andi %u, %lu, %u;\n", lv.base, imm, lv.wid); break; default: @@ -1158,8 +1158,9 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid) case 'l': /* << (left shift) */ lv = draw_eval_expr_wid(le, wid, 0); - /* shifting 0 gets 0. */ - if (lv.base == 0) + /* Shifting 0 gets 0, if we can be sure the shift value + contains no 'x' or 'z' bits. */ + if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; if (lv.base < 4) { @@ -1194,8 +1195,9 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid) lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0); } - /* shifting 0 gets 0. */ - if (lv.base == 0) + /* Shifting 0 gets 0, if we can be sure the shift value + contains no 'x' or 'z' bits. */ + if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; if (lv.base < 4) { @@ -1230,13 +1232,15 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid) lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0); } - /* shifting 0 gets 0. */ - if (lv.base == 0) + /* Shifting 0 gets 0, if we can be sure the shift value + contains no 'x' or 'z' bits. */ + if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; - /* Sign extend any constant begets itself, if this - expression is signed. */ - if ((lv.base < 4) && (ivl_expr_signed(expr))) + /* Similarly, sign extending any constant bit begets itself, + if this expression is signed. */ + if ((lv.base < 4) && ivl_expr_signed(expr) + && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; if (lv.base < 4) { @@ -1984,7 +1988,7 @@ static struct vector_info draw_pad_expr(ivl_expr_t expr, unsigned wid) return res; } - /* So now we know that the subexpression is smaller then the + /* So now we know that the subexpression is smaller than the desired result (the usual case) so we build the result. Evaluate the subexpression into the target buffer, then pad it as appropriate. */ @@ -2441,7 +2445,7 @@ static void draw_select_signal_dest(ivl_expr_t expr, /* Special case: If the operand is a signal (not an array) and the part select is coming from the LSB, and the part select - is no larger then the signal itself, then we can load the + is no larger than the signal itself, then we can load the value in place, directly. */ if ((ivl_signal_dimensions(sig) == 0) && (ivl_expr_width(sube) >= dest.wid) @@ -2597,7 +2601,7 @@ static struct vector_info draw_select_unsized_literal(ivl_expr_t expr, subv = res; } - /* If the subv result is narrower then the select width, then + /* If the subv result is narrower than the select width, then copy it into a wider vector. */ if (subv.wid < wid && ivl_expr_signed(sube)) { res.base = allocate_vector(wid); @@ -3008,7 +3012,7 @@ static struct vector_info draw_unary_expr(ivl_expr_t expr, unsigned wid) break; } - /* If the result needs to be bigger then the calculated + /* If the result needs to be bigger than the calculated value, then write it into a padded vector. */ if (res.wid < wid) { struct vector_info tmp; @@ -3071,7 +3075,7 @@ static struct vector_info draw_unary_expr(ivl_expr_t expr, unsigned wid) fprintf(vvp_out, " %%inv %u, 1;\n", res.base); } - /* If the result needs to be bigger then the calculated + /* If the result needs to be bigger than the calculated value, then write it into a passed vector. */ if (res.wid < wid) { struct vector_info tmp; diff --git a/tgt-vvp/vvp.c b/tgt-vvp/vvp.c index 1317d2591..3277a7005 100644 --- a/tgt-vvp/vvp.c +++ b/tgt-vvp/vvp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -22,12 +22,13 @@ # 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-2009 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2001-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" @@ -45,6 +46,7 @@ static const char*version_string = FILE*vvp_out = 0; int vvp_errors = 0; +unsigned show_file_line = 0; __inline__ static void draw_execute_header(ivl_design_t des) { @@ -95,8 +97,39 @@ int target_design(ivl_design_t des) 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"); + 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 diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index aba52522b..b46c6492a 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -1,7 +1,7 @@ #ifndef __vvp_priv_H #define __vvp_priv_H /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -39,6 +39,12 @@ extern FILE* vvp_out; */ extern int vvp_errors; +/* + * 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; diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index dce4d6e31..8d64cfb58 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -111,13 +111,13 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; if (word_ix) { draw_eval_expr_into_integer(word_ix, 3); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); } else { - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); } draw_eval_expr_into_integer(part_off_ex, 1); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); @@ -128,12 +128,12 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; if (word_ix) { draw_eval_expr_into_integer(word_ix, 3); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); } else { - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); } - fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); if (word_ix) /* Only need this label if word_ix is set. */ fprintf(vvp_out, "t_%u ;\n", skip_set); @@ -155,9 +155,9 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; unsigned index_reg = 3; draw_eval_expr_into_integer(word_ix, index_reg); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); - fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } @@ -171,9 +171,10 @@ static void set_to_lvariable(ivl_lval_t lval, directly to the word and save the index calculation. */ if (word_ix == 0) { if (use_word < ivl_signal_array_count(sig)) { - fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", + use_word); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); } else { fprintf(vvp_out, " ; %%set/v v%p_%lu, %u, %u " @@ -184,9 +185,9 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; unsigned index_reg = 3; draw_eval_expr_into_integer(word_ix, index_reg); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); - fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } @@ -535,6 +536,24 @@ static void set_vec_to_lval(ivl_statement_t net, struct vector_info res) } } +/* + * Routine to insert statement tracing information into the output stream + * when requested by the user (compiler). + */ +static 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 %d %d \"%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); @@ -637,6 +656,8 @@ static 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); @@ -739,6 +760,8 @@ static int show_stmt_assign_nb(ivl_statement_t 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); @@ -922,6 +945,8 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope) unsigned idx, default_case; + show_stmt_file_line(net, "Case statement."); + local_count += count + 1; /* First draw the branch table. All the non-default cases @@ -1039,8 +1064,9 @@ static int show_stmt_case_r(ivl_statement_t net, ivl_scope_t sscope) unsigned idx, default_case; - local_count += count + 1; + show_stmt_file_line(net, "Case statement."); + 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 @@ -1282,6 +1308,8 @@ 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); @@ -1318,6 +1346,8 @@ 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; @@ -1372,8 +1402,11 @@ 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); - struct vector_info cond - = draw_eval_expr(expr, STUFF_OK_XZ|STUFF_OK_47|STUFF_OK_RO); + struct vector_info cond; + + show_stmt_file_line(net, "If statement."); + + cond = draw_eval_expr(expr, STUFF_OK_XZ|STUFF_OK_47|STUFF_OK_RO); assert(cond.wid == 1); @@ -1426,6 +1459,8 @@ static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope) 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); /* Lots of things can happen during a delay. */ clear_expression_lookaside(); @@ -1447,6 +1482,8 @@ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) 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."); + switch (ivl_expr_value(expr)) { case IVL_VT_BOOL: @@ -1480,8 +1517,10 @@ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; - ivl_scope_t target = ivl_stmt_call(net); + + show_stmt_file_line(net, "Disable statement."); + fprintf(vvp_out, " %%disable S_%p;\n", target); return rc; @@ -1492,6 +1531,8 @@ 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); @@ -1524,6 +1565,8 @@ static int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope) 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); @@ -1614,6 +1657,8 @@ 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; @@ -1682,9 +1727,13 @@ 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); - struct vector_info cnt = draw_eval_expr(expr, 0); + struct vector_info cnt; const char *sign = ivl_expr_signed(expr) ? "s" : "u"; + show_stmt_file_line(net, "Repeat statement."); + + cnt = draw_eval_expr(expr, 0); + /* Test that 0 < expr */ fprintf(vvp_out, "T_%u.%u %%cmp/%s 0, %u, %u;\n", thread_count, lab_top, sign, cnt.base, cnt.wid); @@ -1712,6 +1761,9 @@ 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, " %%set/v E_%p, 0,1;\n", ev); return 0; } @@ -1720,6 +1772,8 @@ 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); @@ -1730,6 +1784,8 @@ static int show_stmt_utask(ivl_statement_t net) static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) { + 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); @@ -1793,6 +1849,8 @@ static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) 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. */ @@ -1824,6 +1882,8 @@ static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) static int show_system_task_call(ivl_statement_t net) { + show_stmt_file_line(net, "System task call."); + draw_vpi_task_call(net); /* VPI calls can manipulate anything, so clear the expression @@ -1833,6 +1893,361 @@ static int show_system_task_call(ivl_statement_t 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; + + /* 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; + + /* 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, 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; + /* 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))) { + 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; + /* 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)."); + 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 @@ -1842,6 +2257,7 @@ 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; + unsigned saved_file_line = 0; switch (code) { @@ -1860,8 +2276,21 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) case IVL_ST_BLOCK: if (ivl_stmt_block_scope(net)) rc += show_stmt_block_named(net, sscope); - else + else { + /* 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: diff --git a/verinum.cc b/verinum.cc index 5c79077dc..1b576c224 100644 --- a/verinum.cc +++ b/verinum.cc @@ -287,7 +287,7 @@ verinum::verinum(const verinum&that) verinum::verinum(const verinum&that, unsigned nbits) { - string_flag_ = false; + string_flag_ = that.string_flag_ && (that.nbits_ == nbits); nbits_ = nbits; bits_ = new V[nbits_]; has_len_ = true; @@ -591,6 +591,39 @@ verinum pad_to_width(const verinum&that, unsigned width) 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()) + pad = verinum::V0; + if (that.has_len() && !that.has_sign()) { + 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 @@ -1097,6 +1130,7 @@ verinum pow(const verinum&left, const verinum&right) verinum operator << (const verinum&that, unsigned shift) { verinum result(verinum::V0, that.len() + shift, that.has_len()); + result.has_sign(that.has_sign()); for (unsigned idx = 0 ; idx < that.len() ; idx += 1) result.set(idx+shift, that.get(idx)); @@ -1119,6 +1153,7 @@ verinum operator >> (const verinum&that, unsigned shift) verinum result(that.has_sign()? that.get(that.len()-1) : verinum::V0, that.len() - shift, that.has_len()); + result.has_sign(that.has_sign()); for (unsigned idx = shift ; idx < that.len() ; idx += 1) result.set(idx-shift, that.get(idx)); diff --git a/verinum.h b/verinum.h index d44941291..acab1d673 100644 --- a/verinum.h +++ b/verinum.h @@ -1,7 +1,7 @@ #ifndef __verinum_H #define __verinum_H /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-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 @@ -64,6 +64,7 @@ class verinum { // 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_; } @@ -126,6 +127,12 @@ inline verinum::V sign_bit(const verinum&val) 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&); diff --git a/vhdlpp/Makefile.in b/vhdlpp/Makefile.in index 12b37f7a9..e8c94723c 100644 --- a/vhdlpp/Makefile.in +++ b/vhdlpp/Makefile.in @@ -57,7 +57,7 @@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @EXTRALIBS@ -M = StringHeap.o LineInfo.o +M = StringHeap.o LineInfo.o O = main.o architec.o compiler.o entity.o entity_elaborate.o \ expression.o vtype.o lexor.o lexor_keyword.o parse.o parse_misc.o vhdlreal.o vhdlint.o debug.o \ diff --git a/vhdlpp/lexor.lex b/vhdlpp/lexor.lex index 18531b560..831a3d82a 100644 --- a/vhdlpp/lexor.lex +++ b/vhdlpp/lexor.lex @@ -1,7 +1,6 @@ %option never-interactive %option nounput -%option noyywrap %{ /* @@ -99,7 +98,7 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* \n { yylloc.first_line += 1; } "*/" { BEGIN(comment_enter); } -\'.\' { +\'.\' { yylval.text = escape_apostrophe_and_dup(yytext); return CHARACTER_LITERAL; } @@ -121,7 +120,7 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* if(!are_underscores_correct(yytext)) std::cerr << "An invalid underscore in the identifier:" << yytext << std::endl; - //yywarn(yylloc, "An invalid underscore in the identifier"); + //yywarn(yylloc, "An invalid underscore in the identifier"); yylval.text = strdupnew(yytext); break; default: @@ -129,10 +128,10 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* } return rc; } - + \\([^\\]|\\\\)*\\ { /* extended identifiers */ yylval.text = strdupnew(yytext); - return IDENTIFIER; + return IDENTIFIER; } {decimal_literal} { @@ -159,9 +158,9 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* {based_literal} { if(!are_underscores_correct(yytext) || !is_based_correct(yytext)) - std::cerr << "An invalid form of based literal:" + std::cerr << "An invalid form of based literal:" << yytext << std::endl; - + if(strchr(yytext, '.')) { double val = make_double_from_based(yytext); @@ -176,7 +175,7 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* } } - + /* Compound symbols */ "<=" { return LEQ; } ">=" { return GEQ; } @@ -187,10 +186,10 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* "=>" { return ARROW; } "<<" { return DLT; } ">>" { return DGT; } - /* - Here comes a list of symbols that are more then strange, + /* + Here comes a list of symbols that are more than strange, at least for the time being. - + "??" { return K_CC; } "?=" {} "?/=" {} @@ -213,7 +212,7 @@ extern void yyparse_set_filepath(const char*path); * \return true is returned if underscores are placed * correctly according to specification */ -static bool are_underscores_correct(char* text) +static bool are_underscores_correct(char* text) { unsigned char underscore_allowed = 0; const char* cp; @@ -257,12 +256,12 @@ static bool is_based_correct(char* text) ++clean_base_ptr; } unsigned length = clean_base_ptr - clean_base; - unsigned 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'); + base = 10*(clean_base[0] - '0') + (clean_base[1] - '0'); //the base exceeds 16 or equals 0 if(base > 16 || base == 0) return 0; @@ -275,7 +274,7 @@ static bool is_based_correct(char* text) } bool point = 0; set allowed_chars; - + unsigned c; if(base <= 10) { for(c = 0; c < base; ++c) @@ -291,7 +290,7 @@ static bool is_based_correct(char* text) //MANTISSA examination for(ptr = strchr(text, '#') + 1, length = 0; ptr != strrchr(text, '#'); ++ptr) { - if(*ptr == '.') + if(*ptr == '.') { //we found a dot and another one was already found if(point == 1) @@ -310,7 +309,7 @@ static bool is_based_correct(char* text) } if(length == 0) return 0; - + //EXPONENT examination if(strchr(text, '\0') - strrchr(text, '#') > 1) { //the number contains an exponent if(*(strrchr(text, '#') + 2) == '-') @@ -322,7 +321,7 @@ static bool is_based_correct(char* text) if(!((*ptr >= '0' && *ptr <= '9') || (*ptr >= 'a' && *ptr <= 'f'))) return 0; } - } + } return 1; } @@ -336,13 +335,13 @@ static bool is_based_correct(char* text) 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); ) + for(new_idx = 0, old_idx = 0; old_idx < strlen(text); ) { - if(text[old_idx] == '"' && old_idx == 0) + if(text[old_idx] == '"' && old_idx == 0) { //the beginning of the literal - ++old_idx; + ++old_idx; continue; } else @@ -352,12 +351,12 @@ static char* escape_quot_and_dup(char* text) return newstr; } else - if(text[old_idx] == '"' && text[old_idx+1] == '"') + if(text[old_idx] == '"' && text[old_idx+1] == '"') { newstr[new_idx++] = '"'; old_idx += 2; //jump across two chars } - else + else { newstr[new_idx] = text[old_idx]; ++old_idx; @@ -383,7 +382,7 @@ static char* escape_apostrophe_and_dup(char* text) } /** -* This function takes a floating point based number +* 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 @@ -396,10 +395,10 @@ static double make_double_from_based(char* text) //put null byte in lieu of hashes *first_hash_ptr = '\0'; *second_hash_ptr = '\0'; - + //now lets 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) @@ -425,12 +424,12 @@ static double make_double_from_based(char* text) { 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; - + ptr = *(second_hash_ptr + 2) == '+' ? second_hash_ptr + 3 : second_hash_ptr + 2; + for( ; *ptr != '\0'; ++ptr) { if(*ptr != '_') @@ -439,13 +438,13 @@ static double make_double_from_based(char* text) } } 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) +static unsigned short short_from_hex_char(char ch) { if(ch >= '0' && ch <= '9') return ch - '0'; @@ -464,9 +463,9 @@ static int64_t make_long_from_based(char* text) { char* second_hash_ptr = strrchr(text, '#'); char* end_ptr = strrchr(text, '\0'); //now lets deduce the base - *first_hash_ptr = '\0'; + *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) @@ -479,9 +478,9 @@ static int64_t make_long_from_based(char* text) { //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; + int64_t exponent = 0L; + + ptr = *(second_hash_ptr + 2) == '+' ? second_hash_ptr + 3 : second_hash_ptr + 2; for( ; *ptr != '\0'; ++ptr) { if(*ptr != '_') @@ -511,3 +510,8 @@ void reset_lexor(FILE*fd, const char*path) yyparse_set_filepath(path); } + +int yywrap() +{ + return 1; +} diff --git a/vhdlpp/lexor_keyword.gperf b/vhdlpp/lexor_keyword.gperf index ce630ed83..9c8d307b6 100644 --- a/vhdlpp/lexor_keyword.gperf +++ b/vhdlpp/lexor_keyword.gperf @@ -71,7 +71,7 @@ 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 +not, GN_KEYWORD_2008, K_not null, GN_KEYWORD_2008, K_null of, GN_KEYWORD_2008, K_of on, GN_KEYWORD_2008, K_on diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 5dd1a49f6..4b65c6720 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -48,6 +48,7 @@ int parse_errors = 0; port_mode_t port_mode; char*text; + std::list* name_list; bool flag; @@ -199,7 +200,7 @@ design_unit ; design_units - : design_units design_unit + : design_units design_unit | design_unit ; @@ -225,7 +226,7 @@ entity_declaration if(tmp->get_name() != $7) { errormsg(@1, "Syntax error in entity clause. \n"); } - + delete[]$2; delete[]$7; // Transfer the ports diff --git a/vhdlpp/vhdlint.cc b/vhdlpp/vhdlint.cc index 5d0371677..3add32f50 100644 --- a/vhdlpp/vhdlint.cc +++ b/vhdlpp/vhdlint.cc @@ -46,9 +46,9 @@ vhdlint::vhdlint(const char* text) 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) @@ -65,7 +65,7 @@ vhdlint::vhdlint(const char* text) istringstream str(new_text); delete[] new_text; - + //TODO: check if numbers greater than MAX_INT are handled correctly str >> value_; } diff --git a/vhdlpp/vhdlint.h b/vhdlpp/vhdlint.h index a01afa18c..3a4b841dd 100644 --- a/vhdlpp/vhdlint.h +++ b/vhdlpp/vhdlint.h @@ -29,11 +29,11 @@ class vhdlint 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); diff --git a/vhdlpp/vhdlreal.cc b/vhdlpp/vhdlreal.cc index 52d7f81b0..6651b2d14 100644 --- a/vhdlpp/vhdlreal.cc +++ b/vhdlpp/vhdlreal.cc @@ -39,7 +39,7 @@ vhdlreal::vhdlreal(const vhdlreal& val) { 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 == '_') @@ -47,7 +47,7 @@ vhdlreal::vhdlreal(const char* text) { *buf_ptr = *text; } *buf_ptr = '\0'; - + value_ = strtod(buffer, NULL); delete[] buffer; } diff --git a/vhdlpp/vhdlreal.h b/vhdlpp/vhdlreal.h index bc891672e..25b0616e1 100644 --- a/vhdlpp/vhdlreal.h +++ b/vhdlpp/vhdlreal.h @@ -29,7 +29,7 @@ */ class vhdlreal { - public: + public: friend std::ostream& operator<< (std::ostream&, const vhdlreal&); friend vhdlreal operator+ (const vhdlreal&, const vhdlreal&); friend vhdlreal operator- (const vhdlreal&, const vhdlreal&); diff --git a/vpi/vcd_priv.c b/vpi/vcd_priv.c index ce2230391..dcbb59fbe 100644 --- a/vpi/vcd_priv.c +++ b/vpi/vcd_priv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -36,7 +36,8 @@ int is_escaped_id(const char *name) 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] == '_')) { + if (!(isalnum((int)name[lp]) || + name[lp] == '_') || name[lp] == '$') { return 1; } } diff --git a/vpi_user.h b/vpi_user.h index e365c029d..e0d25de96 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -1,7 +1,7 @@ #ifndef __vpi_user_H #define __vpi_user_H /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * 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 @@ -373,6 +373,8 @@ typedef struct t_vpi_delay { # 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 diff --git a/vvp/Makefile.in b/vvp/Makefile.in index d959f14c3..604f4ce3a 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -67,8 +67,8 @@ V = vpi_modules.o vpi_callback.o vpi_const.o vpi_event.o vpi_iter.o vpi_mcd.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o O = main.o parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \ - concat.o dff.o enum_type.o extend.o npmos.o part.o permaheap.o \ - reduce.o resolv.o \ + concat.o dff.o enum_type.o extend.o file_line.o npmos.o part.o \ + permaheap.o reduce.o resolv.o \ sfunc.o stop.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 \ event.o logic.o delay.o words.o island_tran.o $V diff --git a/vvp/README.txt b/vvp/README.txt index 1819598f1..7142941d8 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -874,7 +874,7 @@ of four possible values (0, 1, x and z) so two bits are needed to represent them. So the input of the functor is 8 bits, and the output 2 bits. A complete lookup table for generating the 2-bit output from an 8-bit input is 512 bits. That can be packed into 64 bytes. This is -small enough that the table should take less space then the code to +small enough that the table should take less space than the code to implement the logic. To implement the truth table, we need to assign 2-bit encodings for @@ -1034,7 +1034,7 @@ becomes: Notice the first parameter of the .functor is the type. The type includes a truth table that describes the output with a given -input. If the gate is wider then four inputs, then cascade +input. If the gate is wider than four inputs, then cascade functors. For example: and gate (out, i1, i2, i3, i4, i5, i6, i7, i8); diff --git a/vvp/codes.cc b/vvp/codes.cc index 22453127b..d1b4650f8 100644 --- a/vvp/codes.cc +++ b/vvp/codes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -109,6 +109,8 @@ void codespace_delete(void) vpi_call_delete((cur+idx)->handle); } else if ((cur+idx)->opcode == &of_EXEC_UFUNC) { exec_ufunc_delete((cur+idx)); + } else if ((cur+idx)->opcode == &of_FILE_LINE) { + delete((cur+idx)->handle); } if (count_opcodes == 0) break; } diff --git a/vvp/codes.h b/vvp/codes.h index 32dc25e66..68769b496 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -1,7 +1,7 @@ #ifndef __codes_H #define __codes_H /* - * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -94,6 +94,7 @@ extern bool of_EVCTL(vthread_t thr, vvp_code_t code); extern bool of_EVCTLC(vthread_t thr, vvp_code_t code); extern bool of_EVCTLI(vthread_t thr, vvp_code_t code); extern bool of_EVCTLS(vthread_t thr, vvp_code_t code); +extern bool of_FILE_LINE(vthread_t thr, vvp_code_t code); extern bool of_FORCE_LINK(vthread_t thr, vvp_code_t code); extern bool of_FORCE_V(vthread_t thr, vvp_code_t code); extern bool of_FORCE_WR(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index e7641f47b..f742a50a5 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -1709,6 +1709,23 @@ void compile_fork(char*label, struct symb_s dest, struct symb_s scope) compile_vpi_lookup(&code->handle, scope.text); } +void compile_file_line(char*label, long file_idx, long lineno, + char*description) +{ + if (label) compile_codelabel(label); + + /* Create an instruction in the code space. */ + vvp_code_t code = codespace_allocate(); + code->opcode = &of_FILE_LINE; + + /* Create a vpiHandle that contains the information. */ + code->handle = vpip_build_file_line(description, file_idx, lineno); + assert(code->handle); + + /* Done with the lexor-allocated name string. */ + delete[] description; +} + void compile_vpi_call(char*label, char*name, bool func_as_task_err, bool func_as_task_warn, long file_idx, long lineno, diff --git a/vvp/compile.h b/vvp/compile.h index 442a696b0..a37042d34 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -1,7 +1,7 @@ #ifndef __compile_H #define __compile_H /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -404,6 +404,9 @@ typedef struct comp_operands_s*comp_operands_t; extern void compile_code(char*label, char*mnem, comp_operands_t opa); extern void compile_disable(char*label, struct symb_s symb); +extern void compile_file_line(char*label, long file_idx, long lineno, + char*description); + extern void compile_vpi_call(char*label, char*name, bool func_as_task_err, bool func_as_task_warn, long file_idx, long lineno, @@ -454,7 +457,7 @@ extern void compile_var_real(char*label, char*name, int msb, int lsb); /* - * The compile_net functio is called to create a .net vector with a + * The compile_net function is called to create a .net vector with a * given name. * * The vpi_type_code argument of compile_net() is one of the vpi diff --git a/vvp/event.h b/vvp/event.h index f01af38e6..ad812234b 100644 --- a/vvp/event.h +++ b/vvp/event.h @@ -350,7 +350,7 @@ class vvp_fun_event_or_aa : public vvp_fun_event_or, public automatic_hooks_s { }; /* - * A named event is simpler then a vvp_fun_edge in that it triggers on + * A named event is simpler than a vvp_fun_edge in that it triggers on * any input at all to port-0. The idea here is that behavioral code * can use a %set/v instruction to trigger the event. */ diff --git a/vvp/file_line.cc b/vvp/file_line.cc new file mode 100644 index 000000000..924f7d710 --- /dev/null +++ b/vvp/file_line.cc @@ -0,0 +1,93 @@ +/* + * 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 "compile.h" +# include "vpi_priv.h" + +struct __vpiFileLine { + struct __vpiHandle base; + const char *description; + unsigned file_idx; + unsigned lineno; +}; + +bool show_file_line = false; +bool code_is_instrumented = false; + +static int file_line_get(int type, vpiHandle ref) +{ + struct __vpiFileLine*rfp = (struct __vpiFileLine*)ref; + + assert(ref->vpi_type->type_code == _vpiFileLine); + + switch (type) { + case vpiLineNo: + return rfp->lineno; + default: + return vpiUndefined; + } +} + +static char *file_line_get_str(int type, vpiHandle ref) +{ + struct __vpiFileLine*rfp = (struct __vpiFileLine*)ref; + + assert(ref->vpi_type->type_code == _vpiFileLine); + + switch (type) { + case vpiFile: + assert(rfp->file_idx < file_names.size()); + return simple_set_rbuf_str(file_names[rfp->file_idx]); + case _vpiDescription: + if (rfp->description) return simple_set_rbuf_str(rfp->description); + else return simple_set_rbuf_str("Procedural tracing."); + default: + return 0; + } +} + +static const struct __vpirt vpip_file_line_rt = { + _vpiFileLine, + file_line_get, + file_line_get_str, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +vpiHandle vpip_build_file_line(char*description, long file_idx, long lineno) +{ + struct __vpiFileLine*obj = new struct __vpiFileLine; + + /* Turn on the diagnostic output when we find a %file_line. */ + show_file_line = true; + code_is_instrumented = true; + + obj->base.vpi_type = &vpip_file_line_rt; + if (description) obj->description = vpip_name_string(description); + else obj->description = 0; + obj->file_idx = (unsigned) file_idx; + obj->lineno = (unsigned) lineno; + + return &obj->base; +} diff --git a/vvp/lexor.lex b/vvp/lexor.lex index b043cf488..5fe395dea 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -204,13 +204,14 @@ static char* strdupnew(char const *str) kind of instruction this really is. The few exceptions (that have exceptional parameter requirements) are listed first. */ -"%vpi_call" { return K_vpi_call; } +"%vpi_call" { return K_vpi_call; } "%vpi_call/w" { return K_vpi_call_w; } "%vpi_call/i" { return K_vpi_call_i; } -"%vpi_func" { return K_vpi_func; } +"%vpi_func" { return K_vpi_func; } "%vpi_func/r" { return K_vpi_func_r; } -"%disable" { return K_disable; } -"%fork" { return K_fork; } +"%disable" { return K_disable; } +"%fork" { return K_fork; } +"%file_line" { return K_file_line; } /* Handle the specialized variable access functions. */ diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index fc3b62106..c51f3092a 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * */ @@ -385,6 +385,18 @@ that this information has been cleared. You can get an assert if this information is not managed correctly. +* %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.". + + * %force/v