mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c53ea10c75 | ||
|
|
8fa54b1c4d | ||
|
|
e5fae03364 | ||
|
|
081fd3b789 | ||
|
|
0edd3c9e95 | ||
|
|
659c7b78d5 | ||
|
|
34ef3c9c38 | ||
|
|
90af180ec1 | ||
|
|
0d71c1154d | ||
|
|
5da5e32e86 | ||
|
|
ea59a39661 | ||
|
|
f5ca1a04cc | ||
|
|
b6c905dffc | ||
|
|
e4a3f9b38c | ||
|
|
b3e2d26e35 | ||
|
|
79e9fbaeb7 | ||
|
|
f5f5cd522b | ||
|
|
e713c8ce57 | ||
|
|
94ed817897 | ||
|
|
ff360738b5 | ||
|
|
f7f73a0825 | ||
|
|
2cedd14d43 | ||
|
|
77c2d49d1a | ||
|
|
dd8d5ef687 | ||
|
|
cfb05cd70a | ||
|
|
6f2f668449 | ||
|
|
6ffbb7cabf | ||
|
|
6c1782e4de | ||
|
|
10e3cc6e3b | ||
|
|
34424e70d8 | ||
|
|
38ad8727af | ||
|
|
4e115d4b69 | ||
|
|
f6f8073058 | ||
|
|
902ba752a3 | ||
|
|
97d9de3dad | ||
|
|
97fef7c60b | ||
|
|
a58e7d94ec | ||
|
|
c0be8bcefb | ||
|
|
8846b365f4 | ||
|
|
72eb361131 | ||
|
|
b83b606267 | ||
|
|
59d7d9e8c3 | ||
|
|
3411279294 | ||
|
|
8d6bea8975 | ||
|
|
01725f662f | ||
|
|
d841e68f4f | ||
|
|
13ecb8e177 | ||
|
|
26436cf4dd | ||
|
|
96c70ea2df | ||
|
|
c8849094fc | ||
|
|
afea6d84e3 | ||
|
|
1fb0af7fba | ||
|
|
8ad1a68420 | ||
|
|
6ef7d5f0fd | ||
|
|
d0fbdfac07 | ||
|
|
16997d153f | ||
|
|
7777d10e9b | ||
|
|
00214f738f | ||
|
|
07e6bc17db | ||
|
|
afdfa4df87 | ||
|
|
1f714c6813 | ||
|
|
5df3032b11 | ||
|
|
eac3458647 | ||
|
|
00efa05b50 | ||
|
|
cc1d380864 | ||
|
|
f96942a526 | ||
|
|
b23fc06388 | ||
|
|
f818ddc71c | ||
|
|
37c8cc82b2 | ||
|
|
03ebd5554f | ||
|
|
a2a7021914 | ||
|
|
c6650f88e1 | ||
|
|
55a25674a2 | ||
|
|
05c3d520a6 | ||
|
|
6b3304320b | ||
|
|
8d15276625 | ||
|
|
354f107fed | ||
|
|
1ff55c20e0 | ||
|
|
9583f0d3cc | ||
|
|
3acb85a005 | ||
|
|
08d041cb93 | ||
|
|
274b2002c2 | ||
|
|
681e91f9d8 | ||
|
|
6e333bd9ab | ||
|
|
6a331e37fc | ||
|
|
d8a020905a | ||
|
|
efa6f4cd95 | ||
|
|
ca537dc3ed | ||
|
|
3b64f54cea | ||
|
|
32c1e38b84 | ||
|
|
a7aa8820e1 | ||
|
|
2582a83376 | ||
|
|
0094cd7a81 | ||
|
|
d9b33d74a4 | ||
|
|
539a773ea7 | ||
|
|
b1831d7e33 | ||
|
|
b8ccb7a4c1 | ||
|
|
0eb75a41bb | ||
|
|
4cc47e3297 | ||
|
|
7bf3366041 | ||
|
|
c4b9f4bccf | ||
|
|
455c552132 | ||
|
|
f26fb51509 | ||
|
|
ab3c6576ed | ||
|
|
d1bd994113 | ||
|
|
d1548b1161 | ||
|
|
8a43f41ed6 | ||
|
|
42759e4011 | ||
|
|
27fa19eb94 | ||
|
|
426ed8589b |
@@ -0,0 +1,4 @@
|
||||
*.v linguist-language=SystemVerilog
|
||||
*.vh linguist-language=SystemVerilog
|
||||
*.sv linguist-language=SystemVerilog
|
||||
nodist linguist-detectable=false
|
||||
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If 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 convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
@@ -2,6 +2,92 @@ Revision history for Verilator
|
||||
|
||||
The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
* Verilator 4.016 2016-06-16
|
||||
|
||||
*** Add --quiet-exit, bug1436. [Todd Strader]
|
||||
|
||||
**** Error continuation lines no longer have %Error prefix.
|
||||
|
||||
**** Support logical equivalence operator <->.
|
||||
|
||||
**** Support VerilatedFstC set_time_unit, bug1433. [Pieter Kapsenberg]
|
||||
|
||||
**** Support deferred assertions, bug1449. [Charles Eddleston]
|
||||
|
||||
**** Mark infrequently called functions with GCC cold attribute.
|
||||
|
||||
**** Fix sign-compare warning in verilated.cpp, bug1437. [Sergey Kvachonok]
|
||||
|
||||
**** Fix fault on $realtime with %t, bug1443. [Julien Margetts]
|
||||
|
||||
**** Fix $display with string without %s, bug1441. [Denis Rystsov]
|
||||
|
||||
**** Fix parameter function string returns, bug1441. [Denis Rystsov]
|
||||
|
||||
**** Fix invalid XML output due to special chars, bug1444. [Kanad Kanhere]
|
||||
|
||||
**** Fix performance when mulithreaded on 1 CPU, bug1455. [Stefan Wallentowitz]
|
||||
|
||||
**** Fix type and real parameter issues, bug1427, bug1456, bug1458. [Todd Strader]
|
||||
|
||||
**** Fix build error on MinGW, bug1460. [Richard Myers]
|
||||
|
||||
**** Fix not reporting some duplicate signals, bug1462. [Peter Gerst]
|
||||
|
||||
**** Fix --savable invalid C++ on packed arrays, bug1465. [Alex Chadwick]
|
||||
|
||||
**** Fix constant function return of function var, bug1467. [Roman Popov]
|
||||
|
||||
|
||||
* Verilator 4.014 2019-05-08
|
||||
|
||||
*** Add --trace-fst-thread.
|
||||
|
||||
**** Support '#' comments in $readmem, bug1411. [Frederick Requin]
|
||||
|
||||
**** Support "'dx" constants, bug1423. [Udi Finkelstein]
|
||||
|
||||
**** For FST tracing use LZ4 compression. [Tony Bybell]
|
||||
|
||||
**** Add error when use parameters without value, bug1424. [Peter Gerst]
|
||||
|
||||
**** Auto-extend and WIDTH warn on unsized X/Zs, bug1423. [Udi Finkelstein]
|
||||
|
||||
**** Fix missing VL_SHIFTL_ errors, bug1412, bug1415. [Larry Lee]
|
||||
|
||||
**** Fix MinGW GCC 6 printf formats, bug1413. [Sergey Kvachonok]
|
||||
|
||||
**** Fix test problems when missing fst2vcd, bug1417. [Todd Strader]
|
||||
|
||||
**** Fix GTKWave register warning, bug1421. [Pieter Kapsenberg]
|
||||
|
||||
**** Fix FST enums not displaying, bug1426. [Danilo Ramos]
|
||||
|
||||
**** Fix table compile error with multiinterfaces, bug1431. [Bogdan Vukobratovic]
|
||||
|
||||
|
||||
* Verilator 4.012 2019-3-23
|
||||
|
||||
*** Add +verilator+seed, bug1396. [Stan Sokorac]
|
||||
|
||||
*** Support $fread. [Leendert van Doorn]
|
||||
|
||||
*** Support void' cast on functions called as tasks, bug1383. [Al Grant]
|
||||
|
||||
*** Add IGNOREDRETURN warning, bug1383.
|
||||
|
||||
**** Report PORTSHORT errors on concat constants, bug 1400. [Will Korteland]
|
||||
|
||||
**** Fix VERILATOR_GDB being ignored, msg2860. [Yu Sheng Lin]
|
||||
|
||||
**** Fix $value$plus$args missing verilated_heavy.h. [Yi-Chung Chen]
|
||||
|
||||
**** Fix MSVC compile error, bug1406. [Benjamin Gartner]
|
||||
|
||||
**** Fix maintainer test when no Parallel::Forker, msg2630. [Enzo Chi]
|
||||
|
||||
**** Fix +1364-1995ext flags applying too late, bug1384. [Al Grant]
|
||||
|
||||
|
||||
* Verilator 4.010 2019-01-27
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
\.clang-tidy
|
||||
\.git/
|
||||
\.git$
|
||||
\.svn/
|
||||
\.(bak|old)/
|
||||
\.(bak|old)$
|
||||
@@ -27,6 +28,8 @@
|
||||
^Makefile$
|
||||
bin/verilator_bin.*
|
||||
bin/verilator_coverage_bin.*
|
||||
docs/Makefile$
|
||||
docs/doxygen-doc/.*
|
||||
src/Makefile$
|
||||
src/Makefile_obj$
|
||||
include/verilated.mk$
|
||||
@@ -46,7 +49,6 @@ nodist/
|
||||
/simv.daidir/
|
||||
/vc_hdrs.h$
|
||||
/csrc/
|
||||
doxygen-doc/.*
|
||||
obj_dir/.*
|
||||
TAGS
|
||||
.*~
|
||||
|
||||
+15
-5
@@ -114,9 +114,11 @@ INFOS = README README.html README.pdf internals.txt internals.html \
|
||||
# Files that can be generated, but should be up to date for a distribution.
|
||||
DISTDEP = info Makefile
|
||||
|
||||
DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
DISTFILES_INC = $(INFOS) .gitignore \
|
||||
*.in *.ac \
|
||||
Changes TODO \
|
||||
Artistic \
|
||||
Changes \
|
||||
LICENSE \
|
||||
MANIFEST.SKIP \
|
||||
bin/verilator \
|
||||
bin/verilator_coverage \
|
||||
@@ -124,7 +126,13 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
doxygen-mainpage doxygen.config verilator_logo.png \
|
||||
docs/.gitignore \
|
||||
docs/CONTRIBUTORS \
|
||||
docs/Makefile.in \
|
||||
docs/TODO \
|
||||
docs/doxygen-mainpage \
|
||||
docs/doxygen.config \
|
||||
docs/verilator_logo.png \
|
||||
install-sh configure *.pod \
|
||||
include/*.[chv]* \
|
||||
include/*.in \
|
||||
@@ -222,9 +230,11 @@ examples: all_nomsg
|
||||
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
|
||||
done
|
||||
|
||||
.PHONY: docs
|
||||
docs: info
|
||||
|
||||
info: $(INFOS)
|
||||
|
||||
# Use --no-split to avoid creating filenames > 14 chars.
|
||||
%.1: ${srcdir}/bin/%
|
||||
pod2man $< $@
|
||||
|
||||
@@ -530,7 +540,7 @@ TAGS: $(TAGFILES)
|
||||
.PHONY: doxygen
|
||||
|
||||
doxygen:
|
||||
$(DOXYGEN) doxygen.config
|
||||
$(MAKE) -C docs doxygen
|
||||
|
||||
######################################################################
|
||||
# Test targets
|
||||
|
||||
+143
-72
@@ -5,30 +5,15 @@
|
||||
|
||||
=head1 NAME
|
||||
|
||||
This is the Verilator package README file.
|
||||
Welcome to Verilator. This is the Verilator package's README file.
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
http://www.veripool.org/verilator
|
||||
|
||||
This package is Copyright 2003-2019 by Wilson Snyder. (Report bugs to
|
||||
L<http://www.veripool.org/>.)
|
||||
|
||||
Verilator is free software; you can redistribute it and/or modify it under
|
||||
the terms of either the GNU Lesser General Public License Version 3 or the
|
||||
Perl Artistic License Version 2.0. (See the documentation for more
|
||||
details.)
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
This document describes how to initially install Verilator. For more
|
||||
general information please see L<http://verilator.org>.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Verilator converts synthesizable (generally not behavioral) Verilog code
|
||||
into C++ or SystemC code. It is not a complete simulator, just a
|
||||
translator.
|
||||
Verilator is a simulator which "Verilates" synthesizable (generally not
|
||||
behavioral) Verilog code into "Verilated" C++ or SystemC code.
|
||||
|
||||
Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It
|
||||
reads the specified Verilog code, lints it, and optionally adds coverage
|
||||
@@ -47,25 +32,75 @@ Verilator is developed and has primary testing on Ubuntu. Versions have
|
||||
also built on Redhat Linux, Macs OS-X, HPUX and Solaris. It should run
|
||||
with minor porting on any Linix-ish platform. Verilator also works on
|
||||
Windows under Cygwin, and Windows under MinGW (gcc -mno-cygwin). Verilated
|
||||
output (not Verilator itself) compiles under MSVC++ 2008 and newer.
|
||||
output (not Verilator itself) compiles under all the options above, plus
|
||||
MSVC++ 2008 and newer.
|
||||
|
||||
=head1 INSTALLATION
|
||||
|
||||
For more details see
|
||||
The following are detailed installation instructions.
|
||||
Alternatively, for a quick summary please see
|
||||
L<http://www.veripool.org/projects/verilator/wiki/Installing>.
|
||||
|
||||
If you will be modifying Verilator, you should use the "git" method as it
|
||||
will let you track changes.
|
||||
|
||||
=over 4
|
||||
|
||||
=item
|
||||
|
||||
The latest version is available at L<http://www.veripool.org/verilator>.
|
||||
Obtain binary or sources:
|
||||
|
||||
Download the latest package from that site, and decompress.
|
||||
There are three methods to obtain Verilator, a prebuilt binary as part of
|
||||
your Linux distribution, via git, or using a tarball. If you will be
|
||||
modifying Verilator, you should use the "git" method as it will let you
|
||||
track changes and hopefully contribute in the future.
|
||||
|
||||
tar xvzf verilator_version.tgz
|
||||
=over 4
|
||||
|
||||
=item
|
||||
|
||||
Prebuilt binary:
|
||||
|
||||
You may install a binary on Ubuntu or other distributions using a package
|
||||
manager. This is unlikely to be the most recent version.
|
||||
|
||||
apt-get install verilator
|
||||
|
||||
You may now skip the remaining installation steps.
|
||||
|
||||
=item
|
||||
|
||||
Git:
|
||||
|
||||
Get the sources from the repository.
|
||||
|
||||
git clone http://git.veripool.org/git/verilator # Only first time
|
||||
## Note the URL above is not a page you can see with a browser, it's for git only
|
||||
|
||||
=item
|
||||
|
||||
Tarball:
|
||||
|
||||
Get a recent tarball package from L<http://www.veripool.org/verilator>.
|
||||
Click the "Download" tab, scroll down to the latest package
|
||||
(i.e. verilator-#.###.tgz), download it, and decompress with:
|
||||
|
||||
tar xvzf verilator_#-###.tgz
|
||||
|
||||
=back
|
||||
|
||||
=item
|
||||
|
||||
Install prerequisites:
|
||||
|
||||
=over 4
|
||||
|
||||
=item
|
||||
|
||||
To use Verilator you will need the C<perl>, C<make> (or C<gmake>), and
|
||||
C<g++> (or C<clang>) packages. To compile Verilator in addition to the above you need the C<flex>,
|
||||
C<bison> and C<texi2html> packages installed.
|
||||
|
||||
sudo apt-get install git make autoconf g++ flex bisonz # First time prerequisites
|
||||
sudo apt-get install libgz # Non-Ubuntu (ignore if gives error)
|
||||
sudo apt-get install libfl2 libfl-dev zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
||||
=item
|
||||
|
||||
@@ -78,22 +113,32 @@ instead.)
|
||||
|
||||
=item
|
||||
|
||||
To use Verilator you will need the C<perl>, C<make> (or C<gmake>), and
|
||||
C<g++> (or C<clang>) packages.
|
||||
|
||||
To use Verilator FST tracing you will need the C<gtkwave> and C<libgz> (on
|
||||
To use Verilator FST tracing you will need the C<gtkwave> and C<libgz> (and on
|
||||
Ubuntu C<zlibc> C<zlib1g> C<zlib1g-dev>) packages installed.
|
||||
|
||||
To compile Verilator in addition to the above you need the C<flex>,
|
||||
C<bison> and C<texi2html> packages installed.
|
||||
=back
|
||||
|
||||
=item
|
||||
|
||||
C<cd> to the Verilator directory containing this README.
|
||||
Prepare for building:
|
||||
|
||||
cd verilator # Needed if not already in the package
|
||||
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
|
||||
unset VERILATOR_ROOT # For bash; ignore error if on bash
|
||||
# If using git:
|
||||
git pull # Make sure we're up-to-date
|
||||
git tag # See what versions exist
|
||||
#git checkout master # Use development branch (e.g. recent bug fix)
|
||||
#git checkout stable # Use most recent release
|
||||
#git checkout v{version} # Switch to specified release version
|
||||
#
|
||||
autoconf # Create ./configure script
|
||||
|
||||
=item
|
||||
|
||||
You now have to decide how you're going to eventually install the kit.
|
||||
Installation Choices
|
||||
|
||||
You have to decide how you're going to eventually install the kit.
|
||||
|
||||
Note Verilator builds the current value of VERILATOR_ROOT, SYSTEMC_INCLUDE,
|
||||
and SYSTEMC_LIBDIR as defaults into the executable, so try to have them
|
||||
@@ -103,25 +148,32 @@ correct before configuring.
|
||||
|
||||
=item 1.
|
||||
|
||||
Our personal favorite is to always run Verilator from the kit directory.
|
||||
This allows the easiest experimentation and upgrading. It's also how most
|
||||
EDA tools operate; to run you point to the tarball, no install is needed.
|
||||
Our personal favorite is to always run Verilator from its git directory.
|
||||
This allows the easiest experimentation and upgrading, and allows many
|
||||
versions of Verilator to co-exist on a system. To run you point to the
|
||||
program's files, no install is needed.
|
||||
|
||||
export VERILATOR_ROOT=`pwd` # if your shell is bash
|
||||
setenv VERILATOR_ROOT `pwd` # if your shell is csh
|
||||
./configure
|
||||
|
||||
Note after installing (below steps), a calling program should set the
|
||||
environment variable VERILATOR_ROOT to point to this git directory, then
|
||||
execute $VERILATOR_ROOT/bin/verilator, which will find the path to all
|
||||
needed files.
|
||||
|
||||
=item 2.
|
||||
|
||||
To install globally onto a "cad" disk with multiple versions of every tool,
|
||||
and add it to path using Modules/modulecmd:
|
||||
You may eventually be instaling onto a project/company-wide "CAD" tools
|
||||
disk that may support multiple versions of every tool.
|
||||
|
||||
unset VERILATOR_ROOT # if your shell is bash
|
||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||
# For the tarball, use the version number instead of git describe
|
||||
./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"`
|
||||
|
||||
After installing you'll want a module file like the following:
|
||||
Note after installing (below steps), if you use C<modulecmd>, you'll want a
|
||||
module file like the following:
|
||||
|
||||
set install_root /CAD_DISK/verilator/{version-number-used-above}
|
||||
unsetenv VERILATOR_ROOT
|
||||
@@ -131,50 +183,49 @@ and add it to path using Modules/modulecmd:
|
||||
|
||||
=item 3.
|
||||
|
||||
The next option is to install it globally, using the normal system paths:
|
||||
The next option is to eventually install it globally, using the normal system paths:
|
||||
|
||||
unset VERILATOR_ROOT # if your shell is bash
|
||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||
./configure
|
||||
|
||||
Then after installing (below) the binary directories should already be in
|
||||
your PATH.
|
||||
|
||||
=item 4.
|
||||
|
||||
Alternatively you can configure a prefix that install will populate, as
|
||||
most GNU tools support:
|
||||
Finally, you may eventually install it into a specific installation prefix,
|
||||
as most GNU tools support:
|
||||
|
||||
unset VERILATOR_ROOT # if your shell is bash
|
||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||
./configure --prefix /opt/verilator-VERSION
|
||||
|
||||
Then after installing you will need to add /opt/verilator-VERSION/bin to
|
||||
Then after installing (below steps) you will need to add /opt/verilator-VERSION/bin to
|
||||
PATH.
|
||||
|
||||
=back
|
||||
|
||||
Note all of the options above did:
|
||||
|
||||
./configure ... some options ...
|
||||
|
||||
Add to this line C<--enable-longtests> for more complete developer tests.
|
||||
Additional packages may be required for these tests.
|
||||
|
||||
=item
|
||||
|
||||
Type C<make> to compile Verilator.
|
||||
|
||||
=item
|
||||
|
||||
Type C<make test> to check the compilation.
|
||||
|
||||
Configure with C<--enable-longtests> for more complete developer tests.
|
||||
Additional packages may be required for these tests.
|
||||
|
||||
You may get a error about a typedef conflict for uint32_t. Edit
|
||||
verilated.h to change the typedef to work, probably to @samp{typedef
|
||||
unsigned long uint32_t;}.
|
||||
|
||||
=item
|
||||
|
||||
If you used the VERILATOR_ROOT scheme you're done. Programs should set the
|
||||
environment variable VERILATOR_ROOT to point to this distribution, then
|
||||
execute $VERILATOR_ROOT/bin/verilator, which will find the path to all
|
||||
needed files.
|
||||
If you used the prefix scheme, now do a C<make install>.
|
||||
|
||||
If you used the prefix scheme, now do a C<make install>. To run verilator,
|
||||
have the verilator binary directory in your PATH (this should already be
|
||||
true if using the default configure), and make sure VERILATOR_ROOT is not
|
||||
set.
|
||||
=item
|
||||
|
||||
You may now wish to consult the examples directory. Type C<make> inside any
|
||||
example directory to run the example.
|
||||
@@ -187,25 +238,45 @@ Detailed documentation and the man page can be seen by running:
|
||||
|
||||
bin/verilator --help
|
||||
|
||||
or reading verilator.txt in the same directory as this README.
|
||||
or reading verilator.pdf in the same directory as this README.
|
||||
|
||||
=head1 DIRECTORY STRUCTURE
|
||||
or see L<https://www.veripool.org/ftp/verilator_doc.pdf> (which is the most
|
||||
recent version and thus may differ in some respects from the version you installed).
|
||||
|
||||
The directories in the kit after de-taring are as follows:
|
||||
=head1 PACKAGE DIRECTORY STRUCTURE
|
||||
|
||||
The directories in the package directory are as follows:
|
||||
|
||||
Changes => Version history
|
||||
bin/verilator => Compiler Wrapper invoked to Verilate code
|
||||
include/ => Files that should be in your -I compiler path
|
||||
include/verilated*.cpp => Global routines to link into your simulator
|
||||
include/verilated*.h => Global headers
|
||||
include/verilated.v => Stub defines for linting
|
||||
include/verilated.mk => Common makefile
|
||||
src/ => Translator source code
|
||||
docs/ => Additional documentation
|
||||
examples/hello_world_c => Example simple Verilog->C++ conversion
|
||||
examples/hello_world_sc => Example simple Verilog->SystemC conversion
|
||||
examples/tracing_c => Example Verilog->C++ with tracing
|
||||
examples/tracing_sc => Example Verilog->SystemC with tracing
|
||||
include/ => Files that should be in your -I compiler path
|
||||
include/verilated*.cpp => Global routines to link into your simulator
|
||||
include/verilated*.h => Global headers
|
||||
include/verilated.mk => Common Makefile
|
||||
include/verilated.v => Stub defines for linting
|
||||
src/ => Translator source code
|
||||
test_regress => Internal tests
|
||||
verilator.pdf => Primary documentation
|
||||
verilator.txt => Primary documentation (text)
|
||||
|
||||
=head1 LIMITATIONS
|
||||
For files created after Verilation, see the manual.
|
||||
|
||||
See verilator.txt (or execute C<bin/verilator --help>) for limitations.
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
This package is Copyright 2003-2019 by Wilson Snyder. (Report bugs to
|
||||
L<http://www.veripool.org/>.)
|
||||
|
||||
Verilator is free software; you can redistribute it and/or modify it under
|
||||
the terms of either the GNU Lesser General Public License Version 3 or the
|
||||
Perl Artistic License Version 2.0. (See the documentation for more
|
||||
details.)
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
+105
-59
@@ -42,6 +42,7 @@ autoflush STDERR 1;
|
||||
$Debug = 0;
|
||||
my $opt_gdb;
|
||||
my $opt_gdbbt;
|
||||
my $opt_quiet_exit;
|
||||
|
||||
# No arguments can't do anything useful. Give help
|
||||
if ($#ARGV < 0) {
|
||||
@@ -58,17 +59,18 @@ foreach my $sw (@ARGV) {
|
||||
}
|
||||
|
||||
Getopt::Long::config("no_auto_abbrev","pass_through");
|
||||
if (! GetOptions (
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
"debug:s" => \&debug,
|
||||
# "version!" => \&version, # Also passthru'ed
|
||||
# Switches
|
||||
"gdb!" => \$opt_gdb,
|
||||
"gdbbt!" => \$opt_gdbbt,
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
if (! GetOptions(
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
"debug:s" => \&debug,
|
||||
# "version!" => \&version, # Also passthru'ed
|
||||
# Switches
|
||||
"gdb!" => \$opt_gdb,
|
||||
"gdbbt!" => \$opt_gdbbt,
|
||||
"quiet-exit!" => \$opt_quiet_exit,
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ if ($opt_gdbbt && !gdb_works()) {
|
||||
my @quoted_sw = map {sh_escape($_)} @Opt_Verilator_Sw;
|
||||
if ($opt_gdb) {
|
||||
# Generic GDB interactive
|
||||
run (("gdb"||$ENV{VERILATOR_GDB})
|
||||
run (($ENV{VERILATOR_GDB}||"gdb")
|
||||
." ".verilator_bin()
|
||||
# Note, uncomment to set breakpoints before running:
|
||||
# ." -ex 'break main'"
|
||||
@@ -185,9 +187,9 @@ sub run {
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
if ($status & 127) {
|
||||
if (($status & 127) == 4 # SIGILL
|
||||
|| ($status & 127) == 8 # SIGFPA
|
||||
|| ($status & 127) == 11) { # SIGSEGV
|
||||
if (($status & 127) == 4 # SIGILL
|
||||
|| ($status & 127) == 8 # SIGFPA
|
||||
|| ($status & 127) == 11) { # SIGSEGV
|
||||
warn "%Error: Verilator internal fault, sorry. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator aborted. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||
@@ -195,7 +197,14 @@ sub run {
|
||||
warn "%Error: Verilator threw signal $status. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||
}
|
||||
}
|
||||
die "%Error: Command Failed $command\n";
|
||||
if ($opt_quiet_exit) {
|
||||
# Same return code as die
|
||||
exit $! if $!; # errno
|
||||
exit $? >> 8 if $? >> 8; # child exit status
|
||||
exit 255; # last resort
|
||||
} else {
|
||||
die "%Error: Command Failed $command\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +355,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--private Debugging; see docs
|
||||
--public Debugging; see docs
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
--quiet-exit Don't print the command on failure
|
||||
--relative-includes Resolve includes relative to current file
|
||||
--no-relative-cfuncs Disallow 'this->' in generated functions
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
@@ -361,6 +371,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--top-module <topname> Name of top level input module
|
||||
--trace Enable waveform creation
|
||||
--trace-fst Enable FST waveform creation
|
||||
--trace-fst-thread Enable FST threaded waveform creation
|
||||
--trace-depth <levels> Depth of tracing
|
||||
--trace-max-array <depth> Maximum bit width for tracing
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
@@ -399,7 +410,8 @@ detailed descriptions in L</"RUNTIME ARGUMENTS"> for more information.
|
||||
+verilator+prof+threads+file+I<filename> Set profile filename
|
||||
+verilator+prof+threads+start+I<value> Set profile starting point
|
||||
+verilator+prof+threads+window+I<value> Set profile duration
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+rand+reset+I<value> Set random reset technique
|
||||
+verilator+seed+I<value> Set random seed
|
||||
+verilator+V Verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
|
||||
@@ -1022,15 +1034,15 @@ developers.
|
||||
|
||||
=item --output-split I<bytes>
|
||||
|
||||
Enables splitting the output .cpp files into multiple outputs. When a
|
||||
C++ file exceeds the specified number of operations, a new file will be
|
||||
created at the next function boundary. In addition, any slow routines will
|
||||
be placed into __Slow files. This accelerates compilation by as
|
||||
optimization can be disabled on the slow routines, and the remaining files
|
||||
can be compiled on parallel machines. Using --output-split should have
|
||||
only a trivial impact on performance. With GCC 3.3 on a 2GHz Opteron,
|
||||
--output-split 20000 will result in splitting into approximately
|
||||
one-minute-compile chunks.
|
||||
Enables splitting the output .cpp files into multiple outputs. When a C++
|
||||
file exceeds the specified number of operations, a new file will be created
|
||||
at the next function boundary. In addition, any infrequently executed
|
||||
"cold" routines will be placed into __Slow files. This accelerates
|
||||
compilation by as optimization can be disabled on the routines in __Slow,
|
||||
and the remaining files can be compiled on parallel machines. Using
|
||||
--output-split should have only a trivial impact on performance. With GCC
|
||||
3.3 on a 2GHz Opteron, --output-split 20000 will result in splitting into
|
||||
approximately one-minute-compile chunks.
|
||||
|
||||
=item --output-split-cfuncs I<statements>
|
||||
|
||||
@@ -1155,6 +1167,10 @@ inlining. This will also turn off inlining as if all modules had a
|
||||
Overwrites the given parameter(s) of the toplevel module. See -G for a
|
||||
detailed description.
|
||||
|
||||
=item --quiet-exit
|
||||
|
||||
When exiting due to an error, do not display the "Command Failed" message.
|
||||
|
||||
=item --relative-includes
|
||||
|
||||
When a file references an include file, resolve the filename relative to
|
||||
@@ -1290,7 +1306,14 @@ even when waveforms are not turned on during model execution.
|
||||
|
||||
=item --trace-fst
|
||||
|
||||
Enable FST waveform tracing in the model. This overrides C<--trace>.
|
||||
Enable FST waveform tracing in the model. This overrides C<--trace> and
|
||||
C<--trace-fst-thread>. See also C<--trace-fst-thread>.
|
||||
|
||||
=item --trace-fst-thread
|
||||
|
||||
Enable FST waveform tracing in the model, using a separate thread. This is
|
||||
typically faster in runtime but slower in total computes than
|
||||
C<--trace-fst>. This overrides C<--trace> and C<--trace-fst>.
|
||||
|
||||
=item --trace-depth I<levels>
|
||||
|
||||
@@ -1455,10 +1478,10 @@ Enable all code style related warning messages. This is equivalent to
|
||||
=item --x-assign unique
|
||||
|
||||
Controls the two-state value that is replaced when an assignment to X is
|
||||
encountered. --x-assign=fast, the default, converts all Xs to whatever is
|
||||
best for performance. --x-assign=0 converts all Xs to 0s, and is also fast.
|
||||
--x-assign=1 converts all Xs to 1s, this is nearly as fast as 0, but more
|
||||
likely to find reset bugs as active high logic will fire. --x-assign=unique
|
||||
encountered. C<--x-assign fast>, the default, converts all Xs to whatever is
|
||||
best for performance. C<--x-assign 0> converts all Xs to 0s, and is also fast.
|
||||
C<--x-assign 1> converts all Xs to 1s, this is nearly as fast as 0, but more
|
||||
likely to find reset bugs as active high logic will fire. C<--x-assign unique>
|
||||
will call a function to determine the value, this allows randomization of
|
||||
all Xs to find reset bugs and is the slowest, but safest for finding reset
|
||||
bugs in code.
|
||||
@@ -1483,13 +1506,13 @@ variables are controlled with --x-initial.
|
||||
Controls the two-state value that is used to initialize variables that are
|
||||
not otherwise initialized.
|
||||
|
||||
--x-initial=0, initializes all otherwise uninitialized variables to zero.
|
||||
C<--x-initial 0>, initializes all otherwise uninitialized variables to zero.
|
||||
|
||||
--x-initial=unique, the default, initializes variables using a function,
|
||||
C<--x-initial unique>, the default, initializes variables using a function,
|
||||
which determines the value to use each initialization. This gives greatest
|
||||
flexibility and allows finding reset bugs. See L</"Unknown states">.
|
||||
|
||||
--x-initial=fast, is best for performance, and initializes all variables to
|
||||
C<--x-initial fast>, is best for performance, and initializes all variables to
|
||||
a state Verilator determines is optimal. This may allow further code
|
||||
optimizations, but will likely hide any code bugs relating to missing
|
||||
resets.
|
||||
@@ -1524,7 +1547,7 @@ ensure that X E<rarr> 1 triggers a C<posedge>.
|
||||
B<Note.> Some users have reported that using this option can affect
|
||||
convergence, and that it may be necessary to use --converge-limit to
|
||||
increase the number of convergence iterations. This may be another
|
||||
indication of problems with the modelled design that should be addressed.
|
||||
indication of problems with the modeled design that should be addressed.
|
||||
|
||||
=item --xml-only
|
||||
|
||||
@@ -1595,10 +1618,15 @@ posedge eval() and one negedge eval().
|
||||
|
||||
=item +verilator+rand+reset+I<value>
|
||||
|
||||
When a model was Verilated using "-x-inital unique", sets the
|
||||
When a model was Verilated using "-x-initial unique", sets the
|
||||
initialization technique. 0 = Reset to zeros. 1 = Reset to all-ones. 2 =
|
||||
Randomize. See L</"Unknown states">.
|
||||
|
||||
=item +verilator+seed+I<value>
|
||||
|
||||
For $random and "-x-initial unique", set the random seed value. If zero or
|
||||
not specified picks a value from the system random number generator.
|
||||
|
||||
=item +verilator+V
|
||||
|
||||
Shows the verbose version, including configuration information.
|
||||
@@ -1698,7 +1726,7 @@ This is an example similar to the above, but using SystemC.
|
||||
#include "Vour.h"
|
||||
int sc_main(int argc, char **argv) {
|
||||
Verilated::commandArgs(argc, argv);
|
||||
sc_clock clk ("clk",10, 0.5, 3, true);
|
||||
sc_clock clk ("clk", 10, 0.5, 3, true);
|
||||
Vour* top;
|
||||
top = new Vour("top");
|
||||
top->clk(clk);
|
||||
@@ -1759,8 +1787,8 @@ the examples directory in the distribution.
|
||||
=head1 BENCHMARKING & OPTIMIZATION
|
||||
|
||||
For best performance, run Verilator with the "-O3 --x-assign=fast
|
||||
--x-initial=fast --noassert" flags. The -O3 flag will require longer
|
||||
compile times, and --x-assign=fast --x-initial=fast may increase the risk
|
||||
--x-initial fast --noassert" flags. The -O3 flag will require longer
|
||||
compile times, and "--x-assign fast --x-initial fast" may increase the risk
|
||||
of reset bugs in trade for performance; see the above documentation for
|
||||
these flags.
|
||||
|
||||
@@ -1843,7 +1871,7 @@ For -cc and -sc mode, it also creates:
|
||||
|
||||
{prefix}.cpp // Top level C++ file
|
||||
{prefix}.h // Top level header
|
||||
{prefix}__Slow{__n}.cpp // Constructors and infrequent routines
|
||||
{prefix}__Slow{__n}.cpp // Constructors and infrequent cold routines
|
||||
{prefix}{__n}.cpp // Additional top C++ files (--output-split)
|
||||
{prefix}{each_verilog_module}.cpp // Lower level internal C++ files
|
||||
{prefix}{each_verilog_module}.h // Lower level internal header files
|
||||
@@ -2068,7 +2096,7 @@ command line, or the link), you'd then:
|
||||
|
||||
#include "svdpi.h"
|
||||
#include "Vour__Dpi.h"
|
||||
int add (int a, int b) { return a+b; }
|
||||
int add(int a, int b) { return a+b; }
|
||||
|
||||
=head2 DPI System Task/Functions
|
||||
|
||||
@@ -2118,7 +2146,7 @@ respect to that top level module, then the scope could be set with
|
||||
|
||||
#include "svdpi.h"
|
||||
...
|
||||
svSetScope (svGetScopeFromName ("dut"));
|
||||
svSetScope(svGetScopeFromName("dut"));
|
||||
|
||||
(Remember that Verilator adds a "V" to the top of the module hierarchy.)
|
||||
|
||||
@@ -2132,7 +2160,7 @@ comments within the svdpi.h header for more information.
|
||||
Verilator allows writing $display like functions using this syntax:
|
||||
|
||||
import "DPI-C" function void
|
||||
\$my_display (input string formatted /*verilator sformat*/ );
|
||||
\$my_display(input string formatted /*verilator sformat*/ );
|
||||
|
||||
The /*verilator sformat*/ indicates that this function accepts a $display
|
||||
like format specifier followed by any number of arguments to satisfy the
|
||||
@@ -2240,7 +2268,7 @@ accesses the above signal "readme" would be:
|
||||
#include "verilated_vpi.h" // Required to get definitions
|
||||
|
||||
vluint64_t main_time = 0; // See comments in first example
|
||||
double sc_time_stamp () { return main_time; }
|
||||
double sc_time_stamp() { return main_time; }
|
||||
|
||||
void read_and_check() {
|
||||
vpiHandle vh1 = vpi_handle_by_name((PLI_BYTE8*)"TOP.our.readme", NULL);
|
||||
@@ -2339,9 +2367,12 @@ provides one of those threads, and the generated model will create and
|
||||
manage the other N-1 threads. It's the client's responsibility not to
|
||||
oversubscribe the available CPU cores. Under CPU oversubscription, the
|
||||
Verilated model should not livelock nor deadlock, however, you can expect
|
||||
performance to be far worse than it would be with proper stoichiometry of
|
||||
performance to be far worse than it would be with proper ratio of
|
||||
threads and CPU cores.
|
||||
|
||||
With --trace-fst-thread, tracing occurs in a separate thread from the main
|
||||
simulation thread(s). This option is orthogonal to --threads.
|
||||
|
||||
The remainder of this section describe behavior with --threads 1 or
|
||||
--threads N (not --no-threads).
|
||||
|
||||
@@ -2387,7 +2418,7 @@ completing after the $stop or $finish.
|
||||
If using --coverage, the coverage routines are fully thread safe.
|
||||
|
||||
If using --dpi, Verilator assumes pure DPI imports are thread safe,
|
||||
balancing performance versus saftey. See --threads-dpi.
|
||||
balancing performance versus safety. See --threads-dpi.
|
||||
|
||||
If using --savable, the save/restore classes are not multithreaded and are
|
||||
must be called only by the eval thread.
|
||||
@@ -2892,7 +2923,7 @@ clock pins to be sc_clocks and this is no longer needed.
|
||||
|
||||
Used after a port declaration. It sets the port to be of sc_bv<I<width>>
|
||||
type, instead of bool, vluint32_t or vluint64_t. This may be useful if
|
||||
the port width is parametrized and different of such modules interface
|
||||
the port width is parameterized and different of such modules interface
|
||||
a templated module (such as a transactor) or for other reasons. In general
|
||||
you should avoid using this attribute when not necessary as with increasing
|
||||
usage of sc_bv the performance increases significantly.
|
||||
@@ -3018,9 +3049,10 @@ from a four state simulator. An === comparison to X will always be false,
|
||||
so that Verilog code which checks for uninitialized logic will not fire.
|
||||
|
||||
Assigning a variable to a X will actually assign the variable to a random
|
||||
value (see the --x-assign switch.) Thus if the value is actually used, the
|
||||
random value should cause downstream errors. Integers also randomize, even
|
||||
though the Verilog 2001 specification says they initialize to zero.
|
||||
value (see the --x-assign switch and +verilator+rand+reset runtime switch.)
|
||||
Thus if the value is actually used, the random value should cause
|
||||
downstream errors. Integers also randomize, even though the Verilog 2001
|
||||
specification says they initialize to zero.
|
||||
|
||||
All variables, depending on --x-initial setting, are typically randomly
|
||||
initialized using a function. By running several random simulation runs
|
||||
@@ -3282,7 +3314,7 @@ $value$plusargs.
|
||||
|
||||
Not supported as Verilator needs to determine all formatting at compile
|
||||
time. Generally you can just ifdef them out for no ill effect. Note also
|
||||
VL_TIME_MULTIPLER can be defined at compile time to move the decimal point
|
||||
VL_TIME_MULTIPLIER can be defined at compile time to move the decimal point
|
||||
when displaying all times, model wide.
|
||||
|
||||
=back
|
||||
@@ -3398,7 +3430,7 @@ it's a bit slower than a non-delayed assignment.) Here's an example
|
||||
always @ (posedge clk)
|
||||
if (~reset_l) begin
|
||||
for (i=0; i<`ARRAY_SIZE; i++) begin
|
||||
array[i] = 0; // Non-delayed for verilator
|
||||
array[i] = 0; // Non-delayed for verilator
|
||||
end
|
||||
|
||||
This message is only seen on large or complicated loops because Verilator
|
||||
@@ -3573,6 +3605,19 @@ code to a case statement, or a SystemVerilog 'unique if' or 'priority if'.
|
||||
Disabled by default as this is a code style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
=item IGNOREDRETURN
|
||||
|
||||
Warns that a non-void function is being called as a task, and hence the
|
||||
return value is being ignored.
|
||||
|
||||
This warning is required by IEEE. The portable way to suppress this warning
|
||||
(in SystemVerilog) is to use a void cast, e.g.
|
||||
|
||||
void'(function_being_called_as_task());
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
|
||||
=item IMPERFECTSCH
|
||||
|
||||
Warns that the scheduling of the model is not absolutely perfect, and some
|
||||
@@ -3863,7 +3908,7 @@ Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
|
||||
synthesis which analyzes interconnection per-bit, but is circular to
|
||||
simulation which analyzes per-bus:
|
||||
|
||||
wire [2:0] x = {x[1:0],shift_in};
|
||||
wire [2:0] x = {x[1:0], shift_in};
|
||||
|
||||
This statement needs to be evaluated multiple times, as a change in
|
||||
"shift_in" requires "x" to be computed 3 times before it becomes stable.
|
||||
@@ -3872,12 +3917,12 @@ causes the warning.
|
||||
|
||||
For significantly better performance, split this into 2 separate signals:
|
||||
|
||||
wire [2:0] xout = {x[1:0],shift_in};
|
||||
wire [2:0] xout = {x[1:0], shift_in};
|
||||
|
||||
and change all receiving logic to instead receive "xout". Alternatively,
|
||||
change it to
|
||||
|
||||
wire [2:0] x = {xin[1:0],shift_in};
|
||||
wire [2:0] x = {xin[1:0], shift_in};
|
||||
|
||||
and change all driving logic to instead drive "xin".
|
||||
|
||||
@@ -3993,7 +4038,7 @@ Concatenate leading zeros when doing arithmetic. In the statement
|
||||
|
||||
The best fix, which clarifies intent and will also make all tools happy is:
|
||||
|
||||
wire [5:0] plus_one = from[5:0] + 6'd1 + {5'd0,carry[0]};
|
||||
wire [5:0] plus_one = from[5:0] + 6'd1 + {5'd0, carry[0]};
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
@@ -4005,12 +4050,12 @@ has an indeterminate width. In most cases this violates the Verilog rule
|
||||
that widths inside concatenates and replicates must be sized, and should be
|
||||
fixed in the code.
|
||||
|
||||
wire [63:0] concat = {1,2};
|
||||
wire [63:0] concat = {1, 2};
|
||||
|
||||
An example where this is technically legal (though still bad form) is:
|
||||
|
||||
parameter PAR = 1;
|
||||
wire [63:0] concat = {PAR,PAR};
|
||||
wire [63:0] concat = {PAR, PAR};
|
||||
|
||||
The correct fix is to either size the 1 ("32'h1"), or add the width to the
|
||||
parameter definition ("parameter [31:0]"), or add the width to the
|
||||
@@ -4213,7 +4258,8 @@ trace file if you want all data to land in the same output file.
|
||||
|
||||
FST a format by GTKWave.
|
||||
This version provides a basic FST support.
|
||||
To dump FST format, add the --trace switch to Verilator and change the include path in the testbench to:
|
||||
To dump FST format, add the --trace-fst switch to Verilator and change the include
|
||||
path in the testbench to:
|
||||
|
||||
#include "verilated_fst_c.h"
|
||||
VerilatedFstC* tfp = new VerilatedFstC;
|
||||
|
||||
+38
-38
@@ -19,8 +19,8 @@ require 5.006_001;
|
||||
use warnings;
|
||||
BEGIN {
|
||||
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
|
||||
# Magic to allow author testing of perl packages in local directory
|
||||
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
|
||||
# Magic to allow author testing of perl packages in local directory
|
||||
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +56,12 @@ foreach my $sw (@ARGV) {
|
||||
|
||||
Getopt::Long::config("no_auto_abbrev","pass_through");
|
||||
if (! GetOptions (
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
"debug:s" => \&debug,
|
||||
# "version!" => \&version, # Also passthru'ed
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
"debug:s" => \&debug,
|
||||
# "version!" => \&version, # Also passthru'ed
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
}
|
||||
@@ -90,25 +90,25 @@ sub verilator_coverage_bin {
|
||||
my $bin = "";
|
||||
# Use VERILATOR_ROOT if defined, else assume verilator_bin is in the search path
|
||||
my $basename = ($ENV{VERILATOR_COVERAGE_BIN}
|
||||
|| "verilator_coverage_bin_dbg");
|
||||
|| "verilator_coverage_bin_dbg");
|
||||
if (defined($ENV{VERILATOR_ROOT})) {
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
if (-x "$dir/bin/$basename"
|
||||
|| -x "$dir/bin/$basename.exe") { # From a "make install" into VERILATOR_ROOT
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
} else {
|
||||
if (-x "$RealBin/$basename"
|
||||
|| -x "$RealBin/$basename.exe") {
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
}
|
||||
# Note we don't look under bin/$basename which would be right if running
|
||||
# in the kit dir. Running that would likely break, since
|
||||
# VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
}
|
||||
# Note we don't look under bin/$basename which would be right if running
|
||||
# in the kit dir. Running that would likely break, since
|
||||
# VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
|
||||
}
|
||||
return $bin;
|
||||
}
|
||||
@@ -125,23 +125,23 @@ sub run {
|
||||
system($command);
|
||||
my $status = $?;
|
||||
if ($status) {
|
||||
if ($! =~ /no such file or directory/i) {
|
||||
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
|
||||
}
|
||||
if ($Debug) { # For easy rerunning
|
||||
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
if ($status & 127) {
|
||||
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
|
||||
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;
|
||||
} else {
|
||||
warn "%Error: Verilator_coverage threw signal $status.\n" if !$Debug;
|
||||
}
|
||||
}
|
||||
die "%Error: Command Failed $command\n";
|
||||
if ($! =~ /no such file or directory/i) {
|
||||
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
|
||||
}
|
||||
if ($Debug) { # For easy rerunning
|
||||
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
if ($status & 127) {
|
||||
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
|
||||
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;
|
||||
} else {
|
||||
warn "%Error: Verilator_coverage threw signal $status.\n" if !$Debug;
|
||||
}
|
||||
}
|
||||
die "%Error: Command Failed $command\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+52
-52
@@ -38,11 +38,11 @@ my $Opt_Lineno = 1;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions (
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
"lineno!" => \$Opt_Lineno,
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
"lineno!" => \$Opt_Lineno,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'verilator_difftree --help'\n";
|
||||
}
|
||||
@@ -68,22 +68,22 @@ sub diff_dir {
|
||||
my %files;
|
||||
|
||||
foreach my $fn (glob("$a/*.tree")) {
|
||||
(my $base = $fn) =~ s!.*/!!;
|
||||
$files{$base}{a} = $fn;
|
||||
(my $base = $fn) =~ s!.*/!!;
|
||||
$files{$base}{a} = $fn;
|
||||
}
|
||||
foreach my $fn (glob("$b/*.tree")) {
|
||||
(my $base = $fn) =~ s!.*/!!;
|
||||
$files{$base}{b} = $fn;
|
||||
(my $base = $fn) =~ s!.*/!!;
|
||||
$files{$base}{b} = $fn;
|
||||
}
|
||||
my $any;
|
||||
foreach my $base (sort (keys %files)) {
|
||||
my $a = $files{$base}{a};
|
||||
my $b = $files{$base}{b};
|
||||
next if !$a || !$b;
|
||||
print "="x70,"\n";
|
||||
print "= $a <-> $b\n";
|
||||
diff_file($a,$b);
|
||||
$any = 1;
|
||||
my $a = $files{$base}{a};
|
||||
my $b = $files{$base}{b};
|
||||
next if !$a || !$b;
|
||||
print "="x70,"\n";
|
||||
print "= $a <-> $b\n";
|
||||
diff_file($a,$b);
|
||||
$any = 1;
|
||||
}
|
||||
$any or warn("%Warning: No .tree files found that have similar base names:\n "
|
||||
.join("\n ", sort keys %files),"\n");
|
||||
@@ -101,7 +101,7 @@ sub diff_file {
|
||||
my $vera = version_from($a);
|
||||
my $verb = version_from($b);
|
||||
my $verCvt = (($vera < 0x3900 && $verb >= 0x3900)
|
||||
|| ($vera >= 0x3900 && $verb < 0x3900));
|
||||
|| ($vera >= 0x3900 && $verb < 0x3900));
|
||||
|
||||
filter($a, $tmp_a, $verCvt);
|
||||
filter($b, $tmp_b, $verCvt);
|
||||
@@ -115,8 +115,8 @@ sub version_from {
|
||||
# Return dump format
|
||||
my $f1 = IO::File->new ($fn) or die "%Error: $! $fn,";
|
||||
while (defined (my $line=$f1->getline())) {
|
||||
last if $. > 10;
|
||||
return hex $1 if $line =~ /\(format (0x[0-9.]+)\)/;
|
||||
last if $. > 10;
|
||||
return hex $1 if $line =~ /\(format (0x[0-9.]+)\)/;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
@@ -130,36 +130,36 @@ sub filter {
|
||||
my $f2 = IO::File->new ($fn2,"w") or die "%Error: $! $fn2,";
|
||||
while (defined (my $line=$f1->getline())) {
|
||||
same_line:
|
||||
next if $line =~ / This=/;
|
||||
$line =~ s/0x[a-f0-9]+/0x/g;
|
||||
$line =~ s/<e[0-9]+\#?>/<e>/g;
|
||||
$line =~ s/{[a-z]*\d+}/{}/g if !$Opt_Lineno;
|
||||
if ($verCvt) {
|
||||
next if $line =~ /^ NETLIST/;
|
||||
$line =~ s!\@dt=0x\(G?/?([^)]+)\)!$1!g; # NEW: @dt -> OLD: non @dt format
|
||||
# # Below Ver1_Non_Dtyped may replace above further
|
||||
if ($line =~ /: ([A-Z]+) /) {
|
||||
my $type = $1;
|
||||
next if $type =~ 'DTYPE';
|
||||
if ($type eq 'TYPETABLE' || $type eq 'RANGE') {
|
||||
$line =~ /^(\s+\S+:) /; my $prefix = $1;
|
||||
while (defined ($line=$f1->getline())) {
|
||||
next if $line =~ /^\s+[a-z]/; # Table body
|
||||
next if $line =~ /^${prefix}[0-9]:/;
|
||||
goto same_line;
|
||||
}
|
||||
next;
|
||||
}
|
||||
if ($Ver1_Non_Dtyped{$type}) {
|
||||
$line =~ s! w[0-9]+!!g;
|
||||
}
|
||||
}
|
||||
$line =~ s!\@dt=0$!NoW!g; # NEW: dt=null -> common format
|
||||
$line =~ s!\@dt=0 !NoW !g; # NEW: dt=null -> common format
|
||||
$line =~ s! s?w0$! NoW!g; # OLD: no width -> common format
|
||||
$line =~ s! s?w0 ! NoW !g; # OLD: no width -> common format
|
||||
}
|
||||
print $f2 $line;
|
||||
next if $line =~ / This=/;
|
||||
$line =~ s/0x[a-f0-9]+/0x/g;
|
||||
$line =~ s/<e[0-9]+\#?>/<e>/g;
|
||||
$line =~ s/{[a-z]*\d+}/{}/g if !$Opt_Lineno;
|
||||
if ($verCvt) {
|
||||
next if $line =~ /^ NETLIST/;
|
||||
$line =~ s!\@dt=0x\(G?/?([^)]+)\)!$1!g; # NEW: @dt -> OLD: non @dt format
|
||||
# # Below Ver1_Non_Dtyped may replace above further
|
||||
if ($line =~ /: ([A-Z]+) /) {
|
||||
my $type = $1;
|
||||
next if $type =~ 'DTYPE';
|
||||
if ($type eq 'TYPETABLE' || $type eq 'RANGE') {
|
||||
$line =~ /^(\s+\S+:) /; my $prefix = $1;
|
||||
while (defined ($line=$f1->getline())) {
|
||||
next if $line =~ /^\s+[a-z]/; # Table body
|
||||
next if $line =~ /^${prefix}[0-9]:/;
|
||||
goto same_line;
|
||||
}
|
||||
next;
|
||||
}
|
||||
if ($Ver1_Non_Dtyped{$type}) {
|
||||
$line =~ s! w[0-9]+!!g;
|
||||
}
|
||||
}
|
||||
$line =~ s!\@dt=0$!NoW!g; # NEW: dt=null -> common format
|
||||
$line =~ s!\@dt=0 !NoW !g; # NEW: dt=null -> common format
|
||||
$line =~ s! s?w0$! NoW!g; # OLD: no width -> common format
|
||||
$line =~ s! s?w0 ! NoW !g; # OLD: no width -> common format
|
||||
}
|
||||
print $f2 $line;
|
||||
}
|
||||
$f1->close;
|
||||
$f2->close;
|
||||
@@ -179,11 +179,11 @@ sub debug {
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!defined $Opt_A) {
|
||||
$Opt_A = $param;
|
||||
$Opt_A = $param;
|
||||
} elsif (!defined $Opt_B) {
|
||||
$Opt_B = $param;
|
||||
$Opt_B = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+85
-85
@@ -24,11 +24,11 @@ my $Opt_File;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions (
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'verilator_profcfunc --help'\n";
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ sub debug {
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!defined $Opt_File) {
|
||||
$Opt_File = $param;
|
||||
$Opt_File = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,13 +66,13 @@ sub profcfunc {
|
||||
my %funcs;
|
||||
|
||||
while (defined (my $line=$fh->getline())) {
|
||||
# %time cumesec selfsec calls {stuff} name
|
||||
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
||||
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
|
||||
$funcs{$func}{pct} += $pct;
|
||||
$funcs{$func}{sec} += $sec;
|
||||
$funcs{$func}{calls} += $calls;
|
||||
}
|
||||
# %time cumesec selfsec calls {stuff} name
|
||||
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
||||
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
|
||||
$funcs{$func}{pct} += $pct;
|
||||
$funcs{$func}{sec} += $sec;
|
||||
$funcs{$func}{calls} += $calls;
|
||||
}
|
||||
# Older gprofs have no call column for single-call functions
|
||||
# %time cumesec selfsec {stuff} name
|
||||
elsif ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
||||
@@ -88,10 +88,10 @@ sub profcfunc {
|
||||
my %pointer_mods;
|
||||
my %verilated_mods;
|
||||
foreach my $func (keys %funcs) {
|
||||
if ($func =~ /(.*)::_eval\(([a-zA-Z_0-9]+__Syms).*\)$/) {
|
||||
$verilated_mods{$1} = qr/^$1/;
|
||||
$pointer_mods{$2} = $1;
|
||||
}
|
||||
if ($func =~ /(.*)::_eval\(([a-zA-Z_0-9]+__Syms).*\)$/) {
|
||||
$verilated_mods{$1} = qr/^$1/;
|
||||
$pointer_mods{$2} = $1;
|
||||
}
|
||||
}
|
||||
#print Dumper(\%pointer_mods, \%verilated_mods);
|
||||
|
||||
@@ -99,72 +99,72 @@ sub profcfunc {
|
||||
my %vfuncs;
|
||||
my %groups;
|
||||
foreach my $func (keys %funcs) {
|
||||
my $pct = $funcs{$func}{pct};
|
||||
my $vfunc = $func;
|
||||
my $design;
|
||||
my $pct = $funcs{$func}{pct};
|
||||
my $vfunc = $func;
|
||||
my $design;
|
||||
|
||||
if ($func =~ /\(([a-zA-Z_0-9]+__Syms)/) {
|
||||
$design = $pointer_mods{$1};
|
||||
}
|
||||
if ($func =~ /\(([a-zA-Z_0-9]+__Syms)/) {
|
||||
$design = $pointer_mods{$1};
|
||||
}
|
||||
|
||||
foreach my $vde (keys %verilated_mods) {
|
||||
last if $design;
|
||||
if ($func =~ /$verilated_mods{$vde}/) {
|
||||
$design=$vde;
|
||||
last;
|
||||
}
|
||||
}
|
||||
foreach my $vde (keys %verilated_mods) {
|
||||
last if $design;
|
||||
if ($func =~ /$verilated_mods{$vde}/) {
|
||||
$design=$vde;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if ($vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
|
||||
$vfunc = sprintf("VBlock %s:%d", $1, $2);
|
||||
$groups{type}{"Verilog Blocks under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$1} += $pct;
|
||||
} else {
|
||||
if ($design) {
|
||||
$vfunc = sprintf("VCommon %s", $func);
|
||||
$groups{type}{"Common code under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$design." common code"} += $pct;
|
||||
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|
||||
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|
||||
|| $func =~ /^verilated/i) {
|
||||
$vfunc = sprintf("VLib %s", $func);
|
||||
$groups{type}{'VLib'} += $pct;
|
||||
$groups{design}{'VLib'} += $pct;
|
||||
$groups{module}{'VLib'} += $pct;
|
||||
} elsif ($func =~ /^_mcount_private/) {
|
||||
$vfunc = sprintf("Prof %s", $func);
|
||||
$groups{type}{'Prof'} += $pct;
|
||||
$groups{design}{'Prof'} += $pct;
|
||||
$groups{module}{'Prof'} += $pct;
|
||||
} else {
|
||||
$vfunc = sprintf("C++ %s", $func);
|
||||
$groups{type}{'C++'} += $pct;
|
||||
$groups{design}{'C++'} += $pct;
|
||||
$groups{module}{'C++'} += $pct;
|
||||
}
|
||||
}
|
||||
$vfuncs{$vfunc} = $funcs{$func};
|
||||
if ($vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
|
||||
$vfunc = sprintf("VBlock %s:%d", $1, $2);
|
||||
$groups{type}{"Verilog Blocks under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$1} += $pct;
|
||||
} else {
|
||||
if ($design) {
|
||||
$vfunc = sprintf("VCommon %s", $func);
|
||||
$groups{type}{"Common code under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$design." common code"} += $pct;
|
||||
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|
||||
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|
||||
|| $func =~ /^verilated/i) {
|
||||
$vfunc = sprintf("VLib %s", $func);
|
||||
$groups{type}{'VLib'} += $pct;
|
||||
$groups{design}{'VLib'} += $pct;
|
||||
$groups{module}{'VLib'} += $pct;
|
||||
} elsif ($func =~ /^_mcount_private/) {
|
||||
$vfunc = sprintf("Prof %s", $func);
|
||||
$groups{type}{'Prof'} += $pct;
|
||||
$groups{design}{'Prof'} += $pct;
|
||||
$groups{module}{'Prof'} += $pct;
|
||||
} else {
|
||||
$vfunc = sprintf("C++ %s", $func);
|
||||
$groups{type}{'C++'} += $pct;
|
||||
$groups{design}{'C++'} += $pct;
|
||||
$groups{module}{'C++'} += $pct;
|
||||
}
|
||||
}
|
||||
$vfuncs{$vfunc} = $funcs{$func};
|
||||
}
|
||||
|
||||
|
||||
foreach my $type (qw(type design module)) {
|
||||
my $missing = 100;
|
||||
foreach (sort (keys %{$groups{$type}})) {
|
||||
$missing -= $groups{$type}{$_};
|
||||
}
|
||||
if ($missing) {
|
||||
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
|
||||
}
|
||||
my $missing = 100;
|
||||
foreach (sort (keys %{$groups{$type}})) {
|
||||
$missing -= $groups{$type}{$_};
|
||||
}
|
||||
if ($missing) {
|
||||
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
|
||||
}
|
||||
|
||||
print("Overall summary by $type:\n");
|
||||
printf(" %-6s %s\n","% time",$type);
|
||||
foreach my $what (sort (keys %{$groups{$type}})) {
|
||||
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
|
||||
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
|
||||
}
|
||||
print("\n");
|
||||
print("Overall summary by $type:\n");
|
||||
printf(" %-6s %s\n","% time",$type);
|
||||
foreach my $what (sort (keys %{$groups{$type}})) {
|
||||
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
|
||||
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
print("Verilog code profile:\n");
|
||||
@@ -181,14 +181,14 @@ sub profcfunc {
|
||||
|
||||
my $cume = 0;
|
||||
foreach my $func (sort {$vfuncs{$b}{sec} <=> $vfuncs{$a}{sec}
|
||||
|| $a cmp $b}
|
||||
(keys %vfuncs)) {
|
||||
$cume += $vfuncs{$func}{sec};
|
||||
printf +("%6.2f %9.2f %8.2f %8d %s\n",
|
||||
$vfuncs{$func}{pct},
|
||||
$cume, $vfuncs{$func}{sec},
|
||||
$vfuncs{$func}{calls},
|
||||
$func);
|
||||
|| $a cmp $b}
|
||||
(keys %vfuncs)) {
|
||||
$cume += $vfuncs{$func}{sec};
|
||||
printf +("%6.2f %9.2f %8.2f %8d %s\n",
|
||||
$vfuncs{$func}{pct},
|
||||
$cume, $vfuncs{$func}{sec},
|
||||
$vfuncs{$func}{calls},
|
||||
$func);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ verilator_profcfunc - Read gprof report created with --prof-cfuncs
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Verilator_profcfunc reads a profile report created by gprof. The names of
|
||||
the functions are then transformed, assuming the user used verilator's
|
||||
the functions are then transformed, assuming the user used Verilator's
|
||||
--prof-cfuncs, and a report printed showing the percentage of time, etc,
|
||||
in each Verilog block.
|
||||
|
||||
|
||||
+2
-2
@@ -6,14 +6,14 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.010 2019-01-27],
|
||||
AC_INIT([Verilator],[4.016 2019-06-16],
|
||||
[https://www.veripool.org/verilator],
|
||||
[verilator],[https://www.veripool.org/verilator])
|
||||
# When releasing, also update header of Changes file
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
|
||||
AC_CONFIG_HEADER(src/config_build.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc)
|
||||
AC_CONFIG_FILES(Makefile docs/Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc)
|
||||
|
||||
AC_MSG_RESULT([configuring for $PACKAGE_STRING])
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Makefile
|
||||
@@ -0,0 +1,13 @@
|
||||
The contributors listed below have certified their Verilator contributions
|
||||
under the Developer Certificate of Origin
|
||||
(https://developercertificate.org/).
|
||||
|
||||
Please see the Verilator manual for additional contributors.
|
||||
|
||||
Jeremy Bennett
|
||||
Alex Chadwick
|
||||
John Coiner
|
||||
Kanad Kanhere
|
||||
Richard Myers
|
||||
Stefan Wallentowitz
|
||||
Wilson Snyder
|
||||
@@ -0,0 +1,50 @@
|
||||
#*****************************************************************************
|
||||
# DESCRIPTION: Verilator documentation: Makefile pre-configure version
|
||||
#
|
||||
# This file is part of Verilator.
|
||||
#
|
||||
# Code available from: http://www.veripool.org/verilator
|
||||
#
|
||||
#*****************************************************************************
|
||||
#
|
||||
# Copyright 2003-2019 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
#
|
||||
# Verilator is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#****************************************************************************/
|
||||
#
|
||||
# This file is intended only to be called from the top-level Verilator Makefile.
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
DOXYGEN = doxygen
|
||||
|
||||
#### End of system configuration section. ####
|
||||
######################################################################
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
default:
|
||||
@echo "error: make not supported here, run 'make docs' from Verilator top-level"
|
||||
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
rm -f $(SCRIPTS) *.tmp
|
||||
rm -f *.aux *.cp *.cps *.dvi *.fn *.fns *.ky *.kys *.log
|
||||
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
|
||||
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
|
||||
rm -f *.tex
|
||||
|
||||
distclean maintainer-clean::
|
||||
rm -f *.info* *.1 $(INFOS)
|
||||
rm -f Makefile
|
||||
|
||||
.PHONY: doxygen
|
||||
|
||||
doxygen:
|
||||
$(DOXYGEN) doxygen.config
|
||||
@@ -45,7 +45,7 @@ PROJECT_BRIEF = "Verilog to C translator"
|
||||
# exceed 55 pixels and the maximum width should not exceed 200 pixels.
|
||||
# Doxygen will copy the logo to the output directory.
|
||||
|
||||
PROJECT_LOGO = veripool-logo.png
|
||||
PROJECT_LOGO = verilator_logo.png
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
@@ -27,17 +27,21 @@ int main(int argc, char** argv, char** env) {
|
||||
|
||||
// Prevent unused variable warnings
|
||||
if (0 && argc && argv && env) {}
|
||||
// Pass arguments so Verilated code can see them, e.g. $value$plusargs
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
// Set debug level, 0 is off, 9 is highest presently used
|
||||
// May be overridden by commandArgs
|
||||
Verilated::debug(0);
|
||||
|
||||
// Randomization reset policy
|
||||
// May be overridden by commandArgs
|
||||
Verilated::randReset(2);
|
||||
|
||||
// Pass arguments so Verilated code can see them, e.g. $value$plusargs
|
||||
// This needs to be called before you create any model
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
|
||||
Vtop* top = new Vtop; // Or use a const unique_ptr, or the VL_UNIQUE_PTR wrapper
|
||||
Vtop* top = new Vtop; // Or use a const unique_ptr, or the VL_UNIQUE_PTR wrapper
|
||||
|
||||
#if VM_TRACE
|
||||
// If verilator was invoked with --trace argument,
|
||||
@@ -90,14 +94,14 @@ int main(int argc, char** argv, char** env) {
|
||||
|
||||
#if VM_TRACE
|
||||
// Dump trace data for this cycle
|
||||
if (tfp) tfp->dump (main_time);
|
||||
if (tfp) tfp->dump(main_time);
|
||||
#endif
|
||||
|
||||
// Read outputs
|
||||
VL_PRINTF ("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x"
|
||||
" -> oquad=%" VL_PRI64"x owide=%x_%08x_%08x\n",
|
||||
main_time, top->clk, top->reset_l, top->in_quad,
|
||||
top->out_quad, top->out_wide[2], top->out_wide[1], top->out_wide[0]);
|
||||
VL_PRINTF("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x"
|
||||
" -> oquad=%" VL_PRI64"x owide=%x_%08x_%08x\n",
|
||||
main_time, top->clk, top->reset_l, top->in_quad,
|
||||
top->out_quad, top->out_wide[2], top->out_wide[1], top->out_wide[0]);
|
||||
}
|
||||
|
||||
// Final model cleanup
|
||||
|
||||
@@ -24,15 +24,19 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// Prevent unused variable warnings
|
||||
if (0 && argc && argv) {}
|
||||
// Pass arguments so Verilated code can see them, e.g. $value$plusargs
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
// Set debug level, 0 is off, 9 is highest presently used
|
||||
// May be overridden by commandArgs
|
||||
Verilated::debug(0);
|
||||
|
||||
// Randomization reset policy
|
||||
// May be overridden by commandArgs
|
||||
Verilated::randReset(2);
|
||||
|
||||
// Pass arguments so Verilated code can see them, e.g. $value$plusargs
|
||||
// This needs to be called before you create any model
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
// General logfile
|
||||
ios::sync_with_stdio();
|
||||
|
||||
@@ -111,9 +115,9 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// Apply inputs
|
||||
if (VL_TIME_Q() > 1 && VL_TIME_Q() < 10) {
|
||||
reset_l = !1; // Assert reset
|
||||
reset_l = !1; // Assert reset
|
||||
} else if (VL_TIME_Q() > 1) {
|
||||
reset_l = !0; // Deassert reset
|
||||
reset_l = !0; // Deassert reset
|
||||
}
|
||||
|
||||
// Simulate 1ns
|
||||
|
||||
@@ -49,7 +49,7 @@ module sub
|
||||
|
||||
// An example assertion
|
||||
always_ff @ (posedge clk) begin
|
||||
AssertionExample: assert (!reset_l || count_c<100);
|
||||
AssertionExample: assert(!reset_l || count_c<100);
|
||||
end
|
||||
|
||||
// And example coverage analysis
|
||||
|
||||
@@ -663,6 +663,7 @@ return(rc);
|
||||
}
|
||||
|
||||
|
||||
#ifndef FST_DYNAMIC_ALIAS2_DISABLE
|
||||
static int fstWriterSVarint(FILE *handle, int64_t v)
|
||||
{
|
||||
unsigned char buf[15]; /* ceil(64/7) = 10 + sign byte padded way up */
|
||||
@@ -688,6 +689,7 @@ len = pnt-buf;
|
||||
fstFwrite(buf, len, 1, handle);
|
||||
return(len);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************/
|
||||
@@ -4810,6 +4812,7 @@ uint32_t cur_blackout = 0;
|
||||
int packtype;
|
||||
unsigned char *mc_mem = NULL;
|
||||
uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */
|
||||
int dumpvars_state = 0;
|
||||
|
||||
if(!xc) return(0);
|
||||
|
||||
@@ -4819,7 +4822,6 @@ length_remaining = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
|
||||
|
||||
if(fv)
|
||||
{
|
||||
fprintf(fv, "$dumpvars\n");
|
||||
#ifndef FST_WRITEX_DISABLE
|
||||
fflush(fv);
|
||||
setvbuf(fv, (char *) NULL, _IONBF, 0); /* even buffered IO is slow so disable it and use our own routines that don't need seeking */
|
||||
@@ -4966,8 +4968,10 @@ for(;;)
|
||||
|
||||
if(beg_tim)
|
||||
{
|
||||
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
|
||||
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
|
||||
}
|
||||
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
|
||||
{
|
||||
@@ -5400,8 +5404,10 @@ for(;;)
|
||||
}
|
||||
}
|
||||
|
||||
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
|
||||
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
|
||||
|
||||
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
|
||||
{
|
||||
|
||||
@@ -248,7 +248,7 @@ static const int LZ4_minLength = (MFLIMIT+1);
|
||||
/**************************************
|
||||
* Common functions
|
||||
**************************************/
|
||||
static unsigned LZ4_NbCommonBytes (register size_t val)
|
||||
static unsigned LZ4_NbCommonBytes (size_t val)
|
||||
{
|
||||
if (LZ4_isLittleEndian())
|
||||
{
|
||||
|
||||
+629
-500
File diff suppressed because it is too large
Load Diff
+508
-470
File diff suppressed because it is too large
Load Diff
+15
-1
@@ -116,8 +116,22 @@ endif
|
||||
|
||||
ifneq ($(VM_THREADS),0)
|
||||
ifneq ($(VM_THREADS),)
|
||||
CPPFLAGS += -DVL_THREADED
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_TRACE_THREADED),0)
|
||||
ifneq ($(VM_TRACE_THREADED),)
|
||||
CPPFLAGS += -DVL_TRACE_THREADED
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VK_LIBS_THREADED),0)
|
||||
ifneq ($(VK_LIBS_THREADED),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
CPPFLAGS += -DVL_THREADED $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
LDLIBS += $(CFG_LDLIBS_THREADS)
|
||||
endif
|
||||
endif
|
||||
|
||||
+238
-236
@@ -50,10 +50,10 @@ public: // But only local to this file
|
||||
// CONSTRUCTORS
|
||||
// Derived classes should call zero() in their constructor
|
||||
VerilatedCovImpItem() {
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
m_keys[i]=KEY_UNDEF;
|
||||
m_vals[i]=0;
|
||||
}
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
m_keys[i] = KEY_UNDEF;
|
||||
m_vals[i] = 0;
|
||||
}
|
||||
}
|
||||
virtual ~VerilatedCovImpItem() {}
|
||||
virtual vluint64_t count() const = 0;
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex; ///< Protects all members, when VL_THREADED. Wrapper deals with setting it.
|
||||
VerilatedMutex m_mutex; ///< Protects all members, when VL_THREADED. Wrapper deals with setting it.
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); ///< List of all items
|
||||
@@ -107,294 +107,296 @@ private:
|
||||
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp() {
|
||||
m_insertp = NULL;
|
||||
m_insertFilenamep = NULL;
|
||||
m_insertLineno = 0;
|
||||
m_insertp = NULL;
|
||||
m_insertFilenamep = NULL;
|
||||
m_insertLineno = 0;
|
||||
}
|
||||
VL_UNCOPYABLE(VerilatedCovImp);
|
||||
public:
|
||||
~VerilatedCovImp() { clearGuts(); }
|
||||
static VerilatedCovImp& imp() VL_MT_SAFE {
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
|
||||
static int nextIndex = KEY_UNDEF+1;
|
||||
ValueIndexMap::iterator iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
nextIndex++; assert(nextIndex>0);
|
||||
m_valueIndexes.insert(std::make_pair(value, nextIndex));
|
||||
m_indexValues.insert(std::make_pair(nextIndex, value));
|
||||
return nextIndex;
|
||||
static int nextIndex = KEY_UNDEF+1;
|
||||
ValueIndexMap::iterator iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
nextIndex++; assert(nextIndex>0);
|
||||
m_valueIndexes.insert(std::make_pair(value, nextIndex));
|
||||
m_indexValues.insert(std::make_pair(nextIndex, value));
|
||||
return nextIndex;
|
||||
}
|
||||
static std::string dequote(const std::string& text) VL_PURE {
|
||||
// Quote any special characters
|
||||
std::string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; ++pos) {
|
||||
if (!isprint(*pos) || *pos=='%' || *pos=='"') {
|
||||
char hex[10]; sprintf(hex,"%%%02X",pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
}
|
||||
}
|
||||
return rtn;
|
||||
// Quote any special characters
|
||||
std::string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; ++pos) {
|
||||
if (!isprint(*pos) || *pos=='%' || *pos=='"') {
|
||||
char hex[10]; sprintf(hex, "%%%02X", pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
}
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
static bool legalKey(const std::string& key) VL_PURE {
|
||||
// Because we compress long keys to a single letter, and
|
||||
// don't want applications to either get confused if they use
|
||||
// a letter differently, nor want them to rely on our compression...
|
||||
// (Considered using numeric keys, but will remain back compatible.)
|
||||
if (key.length()<2) return false;
|
||||
if (key.length()==2 && isdigit(key[1])) return false;
|
||||
return true;
|
||||
// Because we compress long keys to a single letter, and
|
||||
// don't want applications to either get confused if they use
|
||||
// a letter differently, nor want them to rely on our compression...
|
||||
// (Considered using numeric keys, but will remain back compatible.)
|
||||
if (key.length()<2) return false;
|
||||
if (key.length()==2 && isdigit(key[1])) return false;
|
||||
return true;
|
||||
}
|
||||
static std::string keyValueFormatter(const std::string& key, const std::string& value) VL_PURE {
|
||||
std::string name;
|
||||
if (key.length()==1 && isalpha(key[0])) {
|
||||
name += std::string("\001")+key;
|
||||
} else {
|
||||
name += std::string("\001")+dequote(key);
|
||||
}
|
||||
name += std::string("\002")+dequote(value);
|
||||
return name;
|
||||
std::string name;
|
||||
if (key.length()==1 && isalpha(key[0])) {
|
||||
name += std::string("\001")+key;
|
||||
} else {
|
||||
name += std::string("\001")+dequote(key);
|
||||
}
|
||||
name += std::string("\002")+dequote(value);
|
||||
return name;
|
||||
}
|
||||
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
|
||||
// (foo.a.x, foo.b.x) => foo.*.x
|
||||
// (foo.a.x, foo.b.y) => foo.*
|
||||
// (foo.a.x, foo.b) => foo.*
|
||||
if (old == add) return add;
|
||||
// (foo.a.x, foo.b.x) => foo.*.x
|
||||
// (foo.a.x, foo.b.y) => foo.*
|
||||
// (foo.a.x, foo.b) => foo.*
|
||||
if (old == add) return add;
|
||||
if (old.empty()) return add;
|
||||
if (add.empty()) return old;
|
||||
|
||||
const char* a = old.c_str();
|
||||
const char* b = add.c_str();
|
||||
const char* a = old.c_str();
|
||||
const char* b = add.c_str();
|
||||
|
||||
// Scan forward to first mismatch
|
||||
const char* apre = a;
|
||||
const char* bpre = b;
|
||||
while (*apre == *bpre) { apre++; bpre++; }
|
||||
// Scan forward to first mismatch
|
||||
const char* apre = a;
|
||||
const char* bpre = b;
|
||||
while (*apre == *bpre) { apre++; bpre++; }
|
||||
|
||||
// We used to backup and split on only .'s but it seems better to be verbose
|
||||
// and not assume . is the separator
|
||||
std::string prefix = std::string(a,apre-a);
|
||||
// We used to backup and split on only .'s but it seems better to be verbose
|
||||
// and not assume . is the separator
|
||||
std::string prefix = std::string(a, apre-a);
|
||||
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a+strlen(a)-1;
|
||||
const char* bpost = b+strlen(b)-1;
|
||||
while (*apost == *bpost
|
||||
&& apost>apre && bpost>bpre) { apost--; bpost--; }
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a+strlen(a)-1;
|
||||
const char* bpost = b+strlen(b)-1;
|
||||
while (*apost == *bpost
|
||||
&& apost>apre && bpost>bpre) { apost--; bpost--; }
|
||||
|
||||
// Forward to . so we have a whole word
|
||||
std::string suffix = *bpost ? std::string(bpost+1) : "";
|
||||
// Forward to . so we have a whole word
|
||||
std::string suffix = *bpost ? std::string(bpost+1) : "";
|
||||
|
||||
std::string out = prefix+"*"+suffix;
|
||||
std::string out = prefix+"*"+suffix;
|
||||
|
||||
//cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add<<"\nch o="<<out<<endl;
|
||||
return out;
|
||||
//cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add<<"\nch o="<<out<<endl;
|
||||
return out;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) VL_REQUIRES(m_mutex) {
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (std::string::npos != val.find(match)) { // Found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (std::string::npos != val.find(match)) { // Found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void selftest() VL_MT_SAFE {
|
||||
// Little selftest
|
||||
if (combineHier ("a.b.c","a.b.c") !="a.b.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.b.c","a.b") !="a.b*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.x.c","a.y.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.z.z.z.c","a.b.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("z","a") !="*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.a","q.b") !="q.*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.za","q.zb") !="q.z*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("1.2.3.a","9.8.7.a") !="*.a") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
// Little selftest
|
||||
if (combineHier("a.b.c","a.b.c") !="a.b.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("a.b.c","a.b") !="a.b*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("a.x.c","a.y.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("a.z.z.z.c","a.b.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("z","a") !="*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("q.a","q.b") !="q.*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("q.za","q.zb") !="q.z*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("1.2.3.a","9.8.7.a") !="*.a") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
}
|
||||
void clearGuts() VL_REQUIRES(m_mutex) {
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
delete itemp;
|
||||
}
|
||||
m_items.clear();
|
||||
m_indexValues.clear();
|
||||
m_valueIndexes.clear();
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
delete itemp;
|
||||
}
|
||||
m_items.clear();
|
||||
m_indexValues.clear();
|
||||
m_valueIndexes.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
// PUBLIC METHODS
|
||||
void clear() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
clearGuts();
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
clearGuts();
|
||||
}
|
||||
void clearNonMatch(const char* matchp) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
if (!itemMatchesString(itemp, matchp)) {
|
||||
delete itemp;
|
||||
} else {
|
||||
newlist.push_back(itemp);
|
||||
}
|
||||
}
|
||||
m_items = newlist;
|
||||
}
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
if (!itemMatchesString(itemp, matchp)) {
|
||||
delete itemp;
|
||||
} else {
|
||||
newlist.push_back(itemp);
|
||||
}
|
||||
}
|
||||
m_items = newlist;
|
||||
}
|
||||
}
|
||||
void zero() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
(*it)->zero();
|
||||
}
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
(*it)->zero();
|
||||
}
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti(VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf(const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp(const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0]="filename"; valps[0]=m_insertFilenamep;
|
||||
std::string linestr = vlCovCvtToStr(m_insertLineno);
|
||||
ckeyps[1]="lineno"; valps[1]=linestr.c_str();
|
||||
// Default page if not specified
|
||||
const char* fnstartp = m_insertFilenamep;
|
||||
while (const char* foundp = strchr(fnstartp,'/')) fnstartp=foundp+1;
|
||||
const char* fnendp = fnstartp;
|
||||
while (*fnendp && *fnendp!='.') fnendp++;
|
||||
std::string page_default = "sp_user/"+std::string(fnstartp,fnendp-fnstartp);
|
||||
ckeyps[2]="page"; valps[2]=page_default.c_str();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0]="filename"; valps[0]=m_insertFilenamep;
|
||||
std::string linestr = vlCovCvtToStr(m_insertLineno);
|
||||
ckeyps[1]="lineno"; valps[1]=linestr.c_str();
|
||||
// Default page if not specified
|
||||
const char* fnstartp = m_insertFilenamep;
|
||||
while (const char* foundp = strchr(fnstartp,'/')) fnstartp = foundp+1;
|
||||
const char* fnendp = fnstartp;
|
||||
while (*fnendp && *fnendp!='.') fnendp++;
|
||||
std::string page_default = "sp_user/"+std::string(fnstartp, fnendp-fnstartp);
|
||||
ckeyps[2]="page"; valps[2]=page_default.c_str();
|
||||
|
||||
// Keys -> strings
|
||||
std::string keys[MAX_KEYS];
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) {
|
||||
keys[i] = ckeyps[i];
|
||||
}
|
||||
}
|
||||
// Ignore empty keys
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
// Keys -> strings
|
||||
std::string keys[MAX_KEYS];
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) {
|
||||
keys[i] = ckeyps[i];
|
||||
}
|
||||
}
|
||||
// Ignore empty keys
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (!keys[i].empty()) {
|
||||
for (int j=i+1; j<MAX_KEYS; ++j) {
|
||||
if (keys[i] == keys[j]) { // Duplicate key. Keep the last one
|
||||
keys[i] = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert the values
|
||||
int addKeynum=0;
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
const std::string key = keys[i];
|
||||
for (int j=i+1; j<MAX_KEYS; ++j) {
|
||||
if (keys[i] == keys[j]) { // Duplicate key. Keep the last one
|
||||
keys[i] = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert the values
|
||||
int addKeynum = 0;
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
const std::string key = keys[i];
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
//cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
addKeynum++;
|
||||
if (!legalKey(key)) {
|
||||
std::string msg = "%Error: Coverage keys of one character, or letter+digit are illegal: "+key;
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
m_items.push_back(m_insertp);
|
||||
// Prepare for next
|
||||
m_insertp = NULL;
|
||||
const std::string val = valps[i];
|
||||
//cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
addKeynum++;
|
||||
if (!legalKey(key)) {
|
||||
std::string msg
|
||||
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
|
||||
+key);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
m_items.push_back(m_insertp);
|
||||
// Prepare for next
|
||||
m_insertp = NULL;
|
||||
}
|
||||
|
||||
void write(const char* filename) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
#ifndef VM_COVERAGE
|
||||
VL_FATAL_MT("",0,"","%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
#endif
|
||||
selftest();
|
||||
selftest();
|
||||
|
||||
std::ofstream os(filename);
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '")+filename+"'";
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
return;
|
||||
}
|
||||
os << "# SystemC::Coverage-3\n";
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '")+filename+"'";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
return;
|
||||
}
|
||||
os << "# SystemC::Coverage-3\n";
|
||||
|
||||
// Build list of events; totalize if collapsing hierarchy
|
||||
typedef std::map<std::string,std::pair<std::string,vluint64_t> > EventMap;
|
||||
EventMap eventCounts;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
std::string name;
|
||||
std::string hier;
|
||||
bool per_instance = false;
|
||||
// Build list of events; totalize if collapsing hierarchy
|
||||
typedef std::map<std::string,std::pair<std::string,vluint64_t> > EventMap;
|
||||
EventMap eventCounts;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
std::string name;
|
||||
std::string hier;
|
||||
bool per_instance = false;
|
||||
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
std::string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (key == VL_CIK_PER_INSTANCE) {
|
||||
if (val != "0") per_instance = true;
|
||||
}
|
||||
if (key == VL_CIK_HIER) {
|
||||
hier = val;
|
||||
} else {
|
||||
// Print it
|
||||
name += keyValueFormatter(key,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (per_instance) { // Not collapsing hierarchies
|
||||
name += keyValueFormatter(VL_CIK_HIER,hier);
|
||||
hier = "";
|
||||
}
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
std::string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (key == VL_CIK_PER_INSTANCE) {
|
||||
if (val != "0") per_instance = true;
|
||||
}
|
||||
if (key == VL_CIK_HIER) {
|
||||
hier = val;
|
||||
} else {
|
||||
// Print it
|
||||
name += keyValueFormatter(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (per_instance) { // Not collapsing hierarchies
|
||||
name += keyValueFormatter(VL_CIK_HIER, hier);
|
||||
hier = "";
|
||||
}
|
||||
|
||||
// Group versus point labels don't matter here, downstream
|
||||
// deals with it. Seems bad for sizing though and doesn't
|
||||
// allow easy addition of new group codes (would be
|
||||
// inefficient)
|
||||
// Group versus point labels don't matter here, downstream
|
||||
// deals with it. Seems bad for sizing though and doesn't
|
||||
// allow easy addition of new group codes (would be
|
||||
// inefficient)
|
||||
|
||||
// Find or insert the named event
|
||||
EventMap::iterator cit = eventCounts.find(name);
|
||||
if (cit != eventCounts.end()) {
|
||||
const std::string& oldhier = cit->second.first;
|
||||
cit->second.second += itemp->count();
|
||||
cit->second.first = combineHier(oldhier, hier);
|
||||
} else {
|
||||
eventCounts.insert(std::make_pair(name, make_pair(hier,itemp->count())));
|
||||
}
|
||||
}
|
||||
// Find or insert the named event
|
||||
EventMap::iterator cit = eventCounts.find(name);
|
||||
if (cit != eventCounts.end()) {
|
||||
const std::string& oldhier = cit->second.first;
|
||||
cit->second.second += itemp->count();
|
||||
cit->second.first = combineHier(oldhier, hier);
|
||||
} else {
|
||||
eventCounts.insert(std::make_pair(name, make_pair(hier, itemp->count())));
|
||||
}
|
||||
}
|
||||
|
||||
// Output body
|
||||
for (EventMap::const_iterator it=eventCounts.begin(); it!=eventCounts.end(); ++it) {
|
||||
os<<"C '"<<std::dec;
|
||||
os<<it->first;
|
||||
if (!it->second.first.empty()) os<<keyValueFormatter(VL_CIK_HIER,it->second.first);
|
||||
os<<"' "<<it->second.second;
|
||||
os<<std::endl;
|
||||
}
|
||||
// Output body
|
||||
for (EventMap::const_iterator it=eventCounts.begin(); it!=eventCounts.end(); ++it) {
|
||||
os<<"C '"<<std::dec;
|
||||
os<<it->first;
|
||||
if (!it->second.first.empty()) os<<keyValueFormatter(VL_CIK_HIER, it->second.first);
|
||||
os<<"' "<<it->second.second;
|
||||
os<<std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -420,7 +422,7 @@ void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().insertf(filename,lineno);
|
||||
VerilatedCovImp::imp().insertf(filename, lineno);
|
||||
}
|
||||
|
||||
#define K(n) const char* key ## n
|
||||
@@ -446,14 +448,14 @@ void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
// And versions with fewer arguments (oh for a language with named parameters!)
|
||||
void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
// Backward compatibility for Verilator
|
||||
void VerilatedCov::_insertp(A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
@@ -461,10 +463,10 @@ void VerilatedCov::_insertp(A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
std::string val2str = vlCovCvtToStr(val2);
|
||||
std::string val3str = vlCovCvtToStr(val3);
|
||||
_insertp(C(0),C(1),
|
||||
key2,val2str.c_str(), key3,val3str.c_str(), key4, val4.c_str(),
|
||||
C(5),C(6),N(7),N(8),N(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
key2,val2str.c_str(), key3,val3str.c_str(), key4, val4.c_str(),
|
||||
C(5),C(6),N(7),N(8),N(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
#undef A
|
||||
#undef C
|
||||
|
||||
+19
-19
@@ -44,31 +44,31 @@
|
||||
/// The value may be a string, or another type which will be auto-converted to a string.
|
||||
///
|
||||
/// Some typical keys:
|
||||
/// filename File the recording occurs in. Defaults to __FILE__
|
||||
/// lineno Line number the recording occurs in. Defaults to __LINE__
|
||||
/// column Column number (or occurrence# for dup file/lines). Defaults to undef.
|
||||
/// hier Hierarchical name. Defaults to name()
|
||||
/// type Type of coverage. Defaults to "user"
|
||||
/// Other types are 'block', 'fsm', 'toggle'.
|
||||
/// comment Description of the coverage event. Should be set by the user.
|
||||
/// Comments for type==block: 'if', 'else', 'elsif', 'case'
|
||||
/// thresh Threshold to consider fully covered.
|
||||
/// If unspecified, downstream tools will determine it.
|
||||
/// filename File the recording occurs in. Defaults to __FILE__
|
||||
/// lineno Line number the recording occurs in. Defaults to __LINE__
|
||||
/// column Column number (or occurrence# for dup file/lines). Defaults to undef.
|
||||
/// hier Hierarchical name. Defaults to name()
|
||||
/// type Type of coverage. Defaults to "user"
|
||||
/// Other types are 'block', 'fsm', 'toggle'.
|
||||
/// comment Description of the coverage event. Should be set by the user.
|
||||
/// Comments for type==block: 'if', 'else', 'elsif', 'case'
|
||||
/// thresh Threshold to consider fully covered.
|
||||
/// If unspecified, downstream tools will determine it.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// vluint32_t m_cases[10];
|
||||
/// constructor {
|
||||
/// for (int i=0; i<10; ++i) { m_cases[i]=0; }
|
||||
/// vluint32_t m_cases[10];
|
||||
/// constructor {
|
||||
/// for (int i=0; i<10; ++i) { m_cases[i]=0; }
|
||||
/// }
|
||||
/// for (int i=0; i<10; ++i) {
|
||||
/// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i));
|
||||
/// }
|
||||
/// for (int i=0; i<10; ++i) {
|
||||
/// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i));
|
||||
/// }
|
||||
|
||||
#define VL_COVER_INSERT(countp,args...) \
|
||||
VL_IF_COVER(VerilatedCov::_inserti(countp); \
|
||||
VerilatedCov::_insertf(__FILE__,__LINE__); \
|
||||
VerilatedCov::_insertp("hier", name(), args))
|
||||
VL_IF_COVER(VerilatedCov::_inserti(countp); \
|
||||
VerilatedCov::_insertf(__FILE__,__LINE__); \
|
||||
VerilatedCov::_insertp("hier", name(), args))
|
||||
|
||||
//=============================================================================
|
||||
/// Convert VL_COVER_INSERT value arguments to strings
|
||||
|
||||
@@ -320,7 +320,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
return;
|
||||
}
|
||||
default:
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
|
||||
varp->vltype());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -348,7 +349,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
|
||||
return;
|
||||
}
|
||||
default:
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
|
||||
varp->vltype());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -370,7 +372,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
|
||||
return;
|
||||
}
|
||||
default:
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
|
||||
varp->vltype());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -707,11 +710,13 @@ void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
svPutBitArrElem1(d, value, indx1);
|
||||
}
|
||||
void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, int indx2) {
|
||||
void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value,
|
||||
int indx1, int indx2) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
svPutBitArrElem2(d, value, indx1, indx2);
|
||||
}
|
||||
void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int indx2, int indx3) {
|
||||
void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value,
|
||||
int indx1, int indx2, int indx3) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
svPutBitArrElem3(d, value, indx1, indx2, indx3);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
// GTKWave configuration
|
||||
#ifdef VL_TRACE_THREADED
|
||||
# define HAVE_LIBPTHREAD
|
||||
# define FST_WRITER_PARALLEL
|
||||
#endif
|
||||
|
||||
// Include the GTKWave implementation directly
|
||||
#include "gtkwave/fastlz.c"
|
||||
#include "gtkwave/fstapi.c"
|
||||
@@ -57,7 +63,7 @@ protected:
|
||||
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
|
||||
VerilatedFstCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {}
|
||||
~VerilatedFstCallInfo() {}
|
||||
};
|
||||
|
||||
@@ -72,6 +78,10 @@ VerilatedFst::VerilatedFst(void* fst)
|
||||
void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
#ifdef VL_TRACE_THREADED
|
||||
fstWriterSetParallelMode(m_fst, 1);
|
||||
#endif
|
||||
m_curScope.clear();
|
||||
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
@@ -164,7 +174,8 @@ void VerilatedFst::addCallback(
|
||||
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
std::string msg = (std::string("Internal: ")+__FILE__+"::"+__FUNCTION__
|
||||
+" called with already open file");
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
}
|
||||
VerilatedFstCallInfo* vci = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis, 1);
|
||||
|
||||
+18
-12
@@ -77,11 +77,11 @@ public:
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = NULL;
|
||||
}
|
||||
// void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
||||
// void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const char* unitp) { fstWriterSetTimescaleFromString(m_fst, unitp); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
|
||||
// void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
|
||||
// void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const char* unitp) { if (unitp) {} }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
// double timescaleToDouble(const char* unitp);
|
||||
// std::string doubleToTimescale(double value);
|
||||
@@ -157,12 +157,18 @@ public:
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], array2Str(newval, bits));
|
||||
}
|
||||
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) { chgBit(code, newval); }
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) { chgBus(code, newval, bits); }
|
||||
void fullDouble(vluint32_t code, const double newval) { chgDouble(code, newval); }
|
||||
void fullFloat(vluint32_t code, const float newval) { chgFloat(code, newval); }
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) { chgQuad(code, newval, bits); }
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) { chgArray(code, newval, bits); }
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) {
|
||||
chgBit(code, newval); }
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
chgBus(code, newval, bits); }
|
||||
void fullDouble(vluint32_t code, const double newval) {
|
||||
chgDouble(code, newval); }
|
||||
void fullFloat(vluint32_t code, const float newval) {
|
||||
chgFloat(code, newval); }
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
chgQuad(code, newval, bits); }
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
chgArray(code, newval, bits); }
|
||||
|
||||
void declTriBit (vluint32_t code, const char* name, int arraynum);
|
||||
void declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
@@ -216,11 +222,11 @@ public:
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_unit(const char* unit) { /* TODO */ }
|
||||
void set_time_unit(const char* unitp) { m_sptrace.set_time_unit(unitp); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_resolution(const char* unit) { /* TODO */ }
|
||||
void set_time_resolution(const char* unitp) { m_sptrace.set_time_resolution(unitp); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
/// Internal class access
|
||||
|
||||
@@ -70,8 +70,10 @@ extern void VL_READMEM_N(bool hex, int width, int depth, int array_lsb,
|
||||
extern void VL_WRITEMEM_N(bool hex, int width, int depth, int array_lsb,
|
||||
const std::string& filename,
|
||||
const void* memp, IData start, IData end) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld,
|
||||
const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output,
|
||||
const char* formatp, ...) VL_MT_SAFE;
|
||||
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
|
||||
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
|
||||
|
||||
+149
-145
@@ -92,32 +92,32 @@ public:
|
||||
// METHODS
|
||||
//// Add message to queue (called by producer)
|
||||
void post(const VerilatedMsg& msg) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_queue.insert(msg); // Pass by value to copy the message into queue
|
||||
++m_depth;
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_queue.insert(msg); // Pass by value to copy the message into queue
|
||||
++m_depth;
|
||||
}
|
||||
/// Service queue until completion (called by consumer)
|
||||
void process() VL_EXCLUDES(m_mutex) {
|
||||
// Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size,
|
||||
// but on the reader side it's 4x faster to test an atomic then getting a mutex
|
||||
while (m_depth) {
|
||||
// Wait for a message to be added to the queue
|
||||
// We don't use unique_lock as want to unlock with the message copy still in scope
|
||||
// Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size,
|
||||
// but on the reader side it's 4x faster to test an atomic then getting a mutex
|
||||
while (m_depth) {
|
||||
// Wait for a message to be added to the queue
|
||||
// We don't use unique_lock as want to unlock with the message copy still in scope
|
||||
m_mutex.lock();
|
||||
assert(!m_queue.empty()); // Otherwise m_depth is wrong
|
||||
// Unfortunately to release the lock we need to copy the message
|
||||
// (Or have the message be a pointer, but then new/delete cost on each message)
|
||||
// We assume messages are small, so copy
|
||||
auto it = m_queue.begin();
|
||||
const VerilatedMsg msg = *(it);
|
||||
m_queue.erase(it);
|
||||
m_mutex.unlock();
|
||||
m_depth--; // Ok if outside critical section as only this code checks the value
|
||||
{
|
||||
// (Or have the message be a pointer, but then new/delete cost on each message)
|
||||
// We assume messages are small, so copy
|
||||
auto it = m_queue.begin();
|
||||
const VerilatedMsg msg = *(it);
|
||||
m_queue.erase(it);
|
||||
m_mutex.unlock();
|
||||
m_depth--; // Ok if outside critical section as only this code checks the value
|
||||
{
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
|
||||
msg.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -128,15 +128,15 @@ public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedThreadMsgQueue() { }
|
||||
~VerilatedThreadMsgQueue() {
|
||||
// The only call of this with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
// The only call of this with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
}
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
|
||||
// METHODS
|
||||
static VerilatedThreadMsgQueue& threadton() {
|
||||
static VL_THREAD_LOCAL VerilatedThreadMsgQueue t_s;
|
||||
return t_s;
|
||||
static VL_THREAD_LOCAL VerilatedThreadMsgQueue t_s;
|
||||
return t_s;
|
||||
}
|
||||
public:
|
||||
/// Add message to queue, called by producer
|
||||
@@ -153,11 +153,11 @@ public:
|
||||
}
|
||||
/// Push all messages to the eval's queue
|
||||
static void flush(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
while (!threadton().m_queue.empty()) {
|
||||
evalMsgQp->post(threadton().m_queue.front());
|
||||
threadton().m_queue.pop();
|
||||
Verilated::endOfEvalReqdDec();
|
||||
}
|
||||
while (!threadton().m_queue.empty()) {
|
||||
evalMsgQp->post(threadton().m_queue.front());
|
||||
threadton().m_queue.pop();
|
||||
Verilated::endOfEvalReqdDec();
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif // VL_THREADED
|
||||
@@ -200,11 +200,11 @@ class VerilatedImp {
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS
|
||||
VerilatedImp()
|
||||
: m_argVecLoaded(false), m_exportNext(0) {
|
||||
m_fdps.resize(3);
|
||||
m_fdps[0] = stdin;
|
||||
m_fdps[1] = stdout;
|
||||
m_fdps[2] = stderr;
|
||||
: m_argVecLoaded(false), m_exportNext(0) {
|
||||
m_fdps.resize(3);
|
||||
m_fdps[0] = stdin;
|
||||
m_fdps[1] = stdout;
|
||||
m_fdps[2] = stderr;
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
private:
|
||||
@@ -219,21 +219,21 @@ public:
|
||||
static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex);
|
||||
static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex);
|
||||
static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard lock(s_s.m_argMutex);
|
||||
// Note prefixp does not include the leading "+"
|
||||
size_t len = strlen(prefixp);
|
||||
if (VL_UNLIKELY(!s_s.m_argVecLoaded)) {
|
||||
s_s.m_argVecLoaded = true; // Complain only once
|
||||
VL_FATAL_MT("unknown",0,"",
|
||||
"%Error: Verilog called $test$plusargs or $value$plusargs without"
|
||||
" testbench C first calling Verilated::commandArgs(argc,argv).");
|
||||
}
|
||||
for (ArgVec::const_iterator it=s_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
|
||||
if ((*it)[0]=='+') {
|
||||
if (0==strncmp(prefixp, it->c_str()+1, len)) return *it;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
VerilatedLockGuard lock(s_s.m_argMutex);
|
||||
// Note prefixp does not include the leading "+"
|
||||
size_t len = strlen(prefixp);
|
||||
if (VL_UNLIKELY(!s_s.m_argVecLoaded)) {
|
||||
s_s.m_argVecLoaded = true; // Complain only once
|
||||
VL_FATAL_MT("unknown", 0, "",
|
||||
"%Error: Verilog called $test$plusargs or $value$plusargs without"
|
||||
" testbench C first calling Verilated::commandArgs(argc,argv).");
|
||||
}
|
||||
for (ArgVec::const_iterator it=s_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
|
||||
if ((*it)[0]=='+') {
|
||||
if (0==strncmp(prefixp, it->c_str()+1, len)) return *it;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private:
|
||||
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex);
|
||||
@@ -247,75 +247,77 @@ public:
|
||||
// There's often many more scopes than userdata's and thus having a ~48byte
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
UserMap::iterator it=s_s.m_userMap.find(std::make_pair(scopep,userKey));
|
||||
if (it != s_s.m_userMap.end()) it->second = userData;
|
||||
// When we support VL_THREADs, we need a lock around this insert, as it's runtime
|
||||
else s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep,userKey),userData));
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
UserMap::iterator it=s_s.m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s_s.m_userMap.end()) it->second = userData;
|
||||
// When we support VL_THREADs, we need a lock around this insert, as it's runtime
|
||||
else s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep, userKey), userData));
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
UserMap::const_iterator it=s_s.m_userMap.find(std::make_pair(scopep,userKey));
|
||||
if (VL_LIKELY(it != s_s.m_userMap.end())) return it->second;
|
||||
else return NULL;
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
UserMap::const_iterator it=s_s.m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (VL_LIKELY(it != s_s.m_userMap.end())) return it->second;
|
||||
else return NULL;
|
||||
}
|
||||
private:
|
||||
/// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
for (UserMap::iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ) {
|
||||
if (it->first.first == scopep) {
|
||||
s_s.m_userMap.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
for (UserMap::iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ) {
|
||||
if (it->first.first == scopep) {
|
||||
s_s.m_userMap.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void userDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex); // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (UserMap::const_iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" userDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n",
|
||||
it->first.first, it->first.second, it->second);
|
||||
}
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex); // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (UserMap::const_iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" userDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n",
|
||||
it->first.first, it->first.second, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - scope name
|
||||
static void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at construction
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it == s_s.m_nameMap.end()) {
|
||||
s_s.m_nameMap.insert(it, std::make_pair(scopep->name(),scopep));
|
||||
}
|
||||
// Slow ok - called once/scope at construction
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it == s_s.m_nameMap.end()) {
|
||||
s_s.m_nameMap.insert(it, std::make_pair(scopep->name(), scopep));
|
||||
}
|
||||
}
|
||||
static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex); // If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
||||
VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_nameMap.end())) return it->second;
|
||||
else return NULL;
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
||||
VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_nameMap.end())) return it->second;
|
||||
else return NULL;
|
||||
}
|
||||
static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at destruction
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
userEraseScope(scopep);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it);
|
||||
// Slow ok - called once/scope at destruction
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
userEraseScope(scopep);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it);
|
||||
}
|
||||
static void scopesDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.begin(); it!=s_s.m_nameMap.end(); ++it) {
|
||||
const VerilatedScope* scopep = it->second;
|
||||
scopep->scopeDump();
|
||||
}
|
||||
VL_PRINTF_MT("\n");
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.begin();
|
||||
it!=s_s.m_nameMap.end(); ++it) {
|
||||
const VerilatedScope* scopep = it->second;
|
||||
scopep->scopeDump();
|
||||
}
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE_POSTINIT {
|
||||
// Thread save only assuming this is called only after model construction completed
|
||||
// Thread save only assuming this is called only after model construction completed
|
||||
return &s_s.m_nameMap;
|
||||
}
|
||||
|
||||
@@ -329,40 +331,42 @@ public: // But only for verilated*.cpp
|
||||
// Rather than a 2D map, the integer scheme saves 500ish ns on a likely
|
||||
// miss at the cost of a multiply, and all lookups move to slowpath.
|
||||
static int exportInsert(const char* namep) VL_MT_SAFE {
|
||||
// Slow ok - called once/function at creation
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
ExportNameMap::iterator it=s_s.m_exportMap.find(namep);
|
||||
if (it == s_s.m_exportMap.end()) {
|
||||
s_s.m_exportMap.insert(it, std::make_pair(namep, s_s.m_exportNext++));
|
||||
return s_s.m_exportNext++;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
// Slow ok - called once/function at creation
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
ExportNameMap::iterator it=s_s.m_exportMap.find(namep);
|
||||
if (it == s_s.m_exportMap.end()) {
|
||||
s_s.m_exportMap.insert(it, std::make_pair(namep, s_s.m_exportNext++));
|
||||
return s_s.m_exportNext++;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
static int exportFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
ExportNameMap::const_iterator it=s_s.m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ")+namep
|
||||
+" but no such DPI export function name exists in ANY model");
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
return -1;
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
ExportNameMap::const_iterator it=s_s.m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ")+namep
|
||||
+" but no such DPI export function name exists in ANY model");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
static const char* exportName(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath; find name for given export; errors only so no map to reverse-map it
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (it->second == funcnum) return it->first;
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
// Slowpath; find name for given export; errors only so no map to reverse-map it
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin();
|
||||
it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (it->second == funcnum) return it->first;
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
}
|
||||
static void exportsDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
bool first = true;
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" exportDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_EXPORT_NAME %05d: %s\n", it->second, it->first);
|
||||
}
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
bool first = true;
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin();
|
||||
it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" exportDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_EXPORT_NAME %05d: %s\n", it->second, it->first);
|
||||
}
|
||||
}
|
||||
// We don't free up m_exportMap until the end, because we can't be sure
|
||||
// what other models are using the assigned funcnum's.
|
||||
@@ -370,32 +374,32 @@ public: // But only for verilated*.cpp
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
static IData fdNew(FILE* fp) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if (s_s.m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
size_t start = s_s.m_fdps.size();
|
||||
s_s.m_fdps.resize(start*2);
|
||||
for (size_t i=start; i<start*2; ++i) s_s.m_fdFree.push_back(static_cast<IData>(i));
|
||||
}
|
||||
IData idx = s_s.m_fdFree.back(); s_s.m_fdFree.pop_back();
|
||||
s_s.m_fdps[idx] = fp;
|
||||
return (idx | (1UL<<31)); // bit 31 indicates not MCD
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if (s_s.m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
size_t start = s_s.m_fdps.size();
|
||||
s_s.m_fdps.resize(start*2);
|
||||
for (size_t i=start; i<start*2; ++i) s_s.m_fdFree.push_back(static_cast<IData>(i));
|
||||
}
|
||||
IData idx = s_s.m_fdFree.back(); s_s.m_fdFree.pop_back();
|
||||
s_s.m_fdps[idx] = fp;
|
||||
return (idx | (1UL<<31)); // bit 31 indicates not MCD
|
||||
}
|
||||
static void fdDelete(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free
|
||||
s_s.m_fdps[idx] = NULL;
|
||||
s_s.m_fdFree.push_back(idx);
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex);
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free
|
||||
s_s.m_fdps[idx] = NULL;
|
||||
s_s.m_fdFree.push_back(idx);
|
||||
}
|
||||
static inline FILE* fdToFp(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex); // This might get slow, if it does we can cache it
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return NULL;
|
||||
return s_s.m_fdps[idx];
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard lock(s_s.m_fdMutex); // This might get slow, if it does we can cache it
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return NULL;
|
||||
return s_s.m_fdps[idx];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+59
-60
@@ -56,17 +56,18 @@ bool VerilatedDeserialize::readDiffers(const void* __restrict datap, size_t size
|
||||
const vluint8_t* __restrict dp = static_cast<const vluint8_t* __restrict>(datap);
|
||||
vluint8_t miss = 0;
|
||||
while (size--) {
|
||||
miss |= (*dp++ ^ *m_cp++);
|
||||
miss |= (*dp++ ^ *m_cp++);
|
||||
}
|
||||
return (miss!=0);
|
||||
}
|
||||
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap,size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize save-restore file as was made from different model");
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
|
||||
size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap, size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = "Can't deserialize save-restore file as was made from different model";
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
@@ -84,10 +85,10 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong header signature");
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong header signature");
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
os.read(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
}
|
||||
@@ -101,10 +102,10 @@ void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature");
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature");
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,19 +117,19 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n",filenamep););
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n", filenamep););
|
||||
|
||||
if (filenamep[0]=='|') {
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
// cppcheck-suppress duplicateExpression
|
||||
m_fd = ::open(filenamep, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK|O_CLOEXEC
|
||||
, 0666);
|
||||
if (m_fd<0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
if (m_fd<0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_isOpen = true;
|
||||
m_filename = filenamep;
|
||||
@@ -139,19 +140,19 @@ void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n",filenamep););
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n", filenamep););
|
||||
|
||||
if (filenamep[0]=='|') {
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
// cppcheck-suppress duplicateExpression
|
||||
m_fd = ::open(filenamep, O_CREAT|O_RDONLY|O_LARGEFILE|O_CLOEXEC
|
||||
, 0666);
|
||||
if (m_fd<0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
if (m_fd<0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_isOpen = true;
|
||||
m_filename = filenamep;
|
||||
@@ -184,21 +185,21 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
vluint8_t* wp = m_bufp;
|
||||
while (1) {
|
||||
ssize_t remaining = (m_cp - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t remaining = (m_cp - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::write(m_fd, wp, remaining);
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
}
|
||||
@@ -213,30 +214,28 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
// Read into buffer starting at m_endp
|
||||
while (1) {
|
||||
ssize_t remaining = (m_bufp+bufferSize() - m_endp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t remaining = (m_bufp+bufferSize() - m_endp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::read(m_fd, m_endp, remaining);
|
||||
if (got>0) {
|
||||
m_endp += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
close();
|
||||
break;
|
||||
if (got>0) {
|
||||
m_endp += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
}
|
||||
} else { // got==0, EOF
|
||||
// Fill buffer from here to end with NULLs so reader's don't
|
||||
// need to check eof each character.
|
||||
while (m_endp < m_bufp+bufferSize()) *m_endp++ = '\0';
|
||||
break;
|
||||
}
|
||||
while (m_endp < m_bufp+bufferSize()) *m_endp++ = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Serialization of types
|
||||
|
||||
|
||||
|
||||
+55
-55
@@ -34,10 +34,10 @@ class VerilatedSerialize {
|
||||
protected:
|
||||
// MEMBERS
|
||||
// For speed, keep m_cp as the first member of this structure
|
||||
vluint8_t* m_cp; ///< Current pointer into m_bufp buffer
|
||||
vluint8_t* m_bufp; ///< Output buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
vluint8_t* m_cp; ///< Current pointer into m_bufp buffer
|
||||
vluint8_t* m_bufp; ///< Output buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
@@ -50,13 +50,13 @@ protected:
|
||||
VL_UNCOPYABLE(VerilatedSerialize);
|
||||
public:
|
||||
VerilatedSerialize() {
|
||||
m_isOpen = false;
|
||||
m_bufp = new vluint8_t [bufferSize()];
|
||||
m_cp = m_bufp;
|
||||
m_isOpen = false;
|
||||
m_bufp = new vluint8_t [bufferSize()];
|
||||
m_cp = m_bufp;
|
||||
}
|
||||
virtual ~VerilatedSerialize() {
|
||||
close();
|
||||
if (m_bufp) { delete m_bufp; m_bufp=NULL; }
|
||||
close();
|
||||
if (m_bufp) { delete m_bufp; m_bufp=NULL; }
|
||||
}
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
@@ -64,24 +64,24 @@ public:
|
||||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
while (dp < maxp) *m_cp++ = *dp++;
|
||||
size -= blk;
|
||||
}
|
||||
return *this; // For function chaining
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
while (dp < maxp) *m_cp++ = *dp++;
|
||||
size -= blk;
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
private:
|
||||
VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_cp > (m_bufp+(bufferSize()-bufferInsertSize())))) {
|
||||
flush();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_cp > (m_bufp+(bufferSize()-bufferInsertSize())))) {
|
||||
flush();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,12 +93,12 @@ class VerilatedDeserialize {
|
||||
protected:
|
||||
// MEMBERS
|
||||
// For speed, keep m_cp as the first member of this structure
|
||||
vluint8_t* m_cp; ///< Current pointer into m_bufp buffer
|
||||
vluint8_t* m_bufp; ///< Output buffer
|
||||
vluint8_t* m_endp; ///< Last valid byte in m_bufp buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
vluint8_t* m_cp; ///< Current pointer into m_bufp buffer
|
||||
vluint8_t* m_bufp; ///< Output buffer
|
||||
vluint8_t* m_endp; ///< Last valid byte in m_bufp buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
inline static size_t bufferInsertSize() { return 16*1024; }
|
||||
@@ -111,14 +111,14 @@ protected:
|
||||
VL_UNCOPYABLE(VerilatedDeserialize);
|
||||
public:
|
||||
VerilatedDeserialize() {
|
||||
m_isOpen = false;
|
||||
m_bufp = new vluint8_t [bufferSize()];
|
||||
m_cp = m_bufp;
|
||||
m_endp = NULL;
|
||||
m_isOpen = false;
|
||||
m_bufp = new vluint8_t [bufferSize()];
|
||||
m_cp = m_bufp;
|
||||
m_endp = NULL;
|
||||
}
|
||||
virtual ~VerilatedDeserialize() {
|
||||
close();
|
||||
if (m_bufp) { delete m_bufp; m_bufp=NULL; }
|
||||
close();
|
||||
if (m_bufp) { delete m_bufp; m_bufp=NULL; }
|
||||
}
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
@@ -126,15 +126,15 @@ public:
|
||||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
vluint8_t* __restrict dp = (vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
while (dp < maxp) *dp++ = *m_cp++;
|
||||
size -= blk;
|
||||
}
|
||||
return *this; // For function chaining
|
||||
vluint8_t* __restrict dp = (vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
while (dp < maxp) *dp++ = *m_cp++;
|
||||
size -= blk;
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
// Read a datum and compare with expected value
|
||||
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
@@ -143,12 +143,12 @@ public:
|
||||
private:
|
||||
bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY((m_cp+bufferInsertSize()) > m_endp)) {
|
||||
fill();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY((m_cp+bufferInsertSize()) > m_endp)) {
|
||||
fill();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
};
|
||||
|
||||
@@ -158,7 +158,7 @@ private:
|
||||
|
||||
class VerilatedSave : public VerilatedSerialize {
|
||||
private:
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -177,7 +177,7 @@ public:
|
||||
|
||||
class VerilatedRestore : public VerilatedDeserialize {
|
||||
private:
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -237,12 +237,12 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, std::string& rhs) {
|
||||
vluint32_t len=rhs.length();
|
||||
vluint32_t len = rhs.length();
|
||||
os<<len;
|
||||
return os.write(rhs.data(), len);
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) {
|
||||
vluint32_t len=0;
|
||||
vluint32_t len = 0;
|
||||
os>>len;
|
||||
rhs.resize(len);
|
||||
return os.read((void*)rhs.data(), len);
|
||||
|
||||
@@ -142,22 +142,28 @@ public:
|
||||
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
|
||||
// DPI accessors
|
||||
int left(int dim) const {
|
||||
return dim==0 ? m_packed.left() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].left() : 0;
|
||||
return dim==0 ? m_packed.left()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].left() : 0;
|
||||
}
|
||||
int right(int dim) const {
|
||||
return dim==0 ? m_packed.right() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].right() : 0;
|
||||
return dim==0 ? m_packed.right()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].right() : 0;
|
||||
}
|
||||
int low(int dim) const {
|
||||
return dim==0 ? m_packed.low() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].low() : 0;
|
||||
return dim==0 ? m_packed.low()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].low() : 0;
|
||||
}
|
||||
int high(int dim) const {
|
||||
return dim==0 ? m_packed.high() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].high() : 0;
|
||||
return dim==0 ? m_packed.high()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].high() : 0;
|
||||
}
|
||||
int increment(int dim) const {
|
||||
return dim==0 ? m_packed.increment() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].increment() : 0;
|
||||
return dim==0 ? m_packed.increment()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].increment() : 0;
|
||||
}
|
||||
int elements(int dim) const {
|
||||
return dim==0 ? m_packed.elements() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].elements() : 0;
|
||||
return dim==0 ? m_packed.elements()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].elements() : 0;
|
||||
}
|
||||
/// Total size in bytes (note DPI limited to 4GB)
|
||||
size_t totalSize() const;
|
||||
|
||||
@@ -119,8 +119,11 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling)
|
||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||
unsigned cpus = std::thread::hardware_concurrency();
|
||||
if (cpus < nThreads+1) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n", cpus, nThreads+1);
|
||||
static int warnedOnce = 0;
|
||||
if (!warnedOnce++) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n", cpus, nThreads+1);
|
||||
}
|
||||
}
|
||||
// Create'em
|
||||
for (int i=0; i<nThreads; ++i) {
|
||||
|
||||
@@ -52,6 +52,10 @@ public:
|
||||
|
||||
// METHODS
|
||||
static vluint64_t yields() { return s_yields; }
|
||||
static void yieldThread() {
|
||||
++s_yields; // Statistics
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
// Block until notify() has occurred, then return.
|
||||
// If notify() has already occurred, return immediately.
|
||||
@@ -63,11 +67,10 @@ public:
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!notified())) {
|
||||
VL_CPU_RELAX();
|
||||
ct++;
|
||||
++ct;
|
||||
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
||||
ct = 0;
|
||||
++s_yields; // Statistics
|
||||
std::this_thread::yield();
|
||||
yieldThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,8 +152,14 @@ public:
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||
VL_CPU_RELAX();
|
||||
++ct;
|
||||
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
||||
ct = 0;
|
||||
VlNotification::yieldThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,14 +68,17 @@ public:
|
||||
}
|
||||
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(),
|
||||
singleton().s_vcdVecp.end(), vcdp);
|
||||
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||
}
|
||||
static void flush_all() VL_EXCLUDES(singleton().s_vcdMutex) VL_MT_UNSAFE_ONE {
|
||||
// Thread safety: Although this function is protected by a mutex so perhaps
|
||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
||||
// Thread safety: Although this function is protected by a mutex so
|
||||
// perhaps in the future we can allow tracing in separate threads,
|
||||
// vcdp->flush() assumes call from single thread
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
for (VcdVec::const_iterator it = singleton().s_vcdVecp.begin();
|
||||
it != singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
}
|
||||
@@ -102,7 +105,7 @@ protected:
|
||||
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {}
|
||||
~VerilatedVcdCallInfo() {}
|
||||
};
|
||||
|
||||
@@ -523,7 +526,8 @@ void VerilatedVcd::module(const std::string& name) {
|
||||
|
||||
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
if (!code) { VL_FATAL_MT(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
|
||||
if (!code) { VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Internal: internal trace problem, code 0 is illegal"); }
|
||||
|
||||
int bits = ((msb>lsb)?(msb-lsb):(lsb-msb))+1;
|
||||
int codesNeeded = 1+int(bits/32);
|
||||
@@ -644,10 +648,12 @@ void VerilatedVcd::addCallback(
|
||||
{
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__
|
||||
+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedVcdCallInfo* vci = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
|
||||
VerilatedVcdCallInfo* vci
|
||||
= new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
|
||||
m_callbacks.push_back(vci);
|
||||
}
|
||||
|
||||
|
||||
+34
-19
@@ -134,17 +134,24 @@ private:
|
||||
// cppcheck-suppress functionConst
|
||||
void dumpDone();
|
||||
inline void printCode(vluint32_t code) {
|
||||
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
|
||||
*m_writep++ = static_cast<char>((code)%94+33);
|
||||
*m_writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
code--;
|
||||
*m_writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
}
|
||||
static std::string stringCode(vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) out += static_cast<char>((code/94)%94+33);
|
||||
return out + static_cast<char>((code)%94+33);
|
||||
out += static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
code--;
|
||||
out += static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -155,7 +162,7 @@ public:
|
||||
|
||||
// ACCESSORS
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB=rolloverMB; };
|
||||
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB=rolloverMB; }
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
@@ -261,7 +268,8 @@ public:
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval,
|
||||
const vluint32_t newtri, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
|
||||
*m_writep++='b';
|
||||
@@ -272,7 +280,8 @@ public:
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
m_sigs_oldvalp[code+word*2] = newvalp[word];
|
||||
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
|
||||
@@ -312,7 +321,8 @@ public:
|
||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullBit(code, newval);
|
||||
}
|
||||
@@ -342,17 +352,20 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval,
|
||||
const vluint32_t newtri) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit(code, newval, newtri);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval,
|
||||
const vluint32_t newtri, int bits) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
@@ -361,7 +374,8 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval,
|
||||
const vluint32_t newtri, int bits) {
|
||||
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
@@ -370,7 +384,8 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
||||
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
||||
@@ -425,7 +440,7 @@ public:
|
||||
/// "cat" to be used to combine the header plus any number of data files.
|
||||
void openNext(bool incFilename=true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); }
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); };
|
||||
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); }
|
||||
/// Close dump
|
||||
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
@@ -447,7 +462,7 @@ public:
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; };
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -47,7 +47,8 @@ public:
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
# if (SYSTEMC_VERSION>=20060505)
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an annoying "to turn off" message.
|
||||
// Turning off the message just for this still results in an
|
||||
// annoying "to turn off" message.
|
||||
sc_time t1sec(1,SC_SEC);
|
||||
if (t1sec.to_default_time_units()!=0) {
|
||||
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
|
||||
@@ -69,7 +70,8 @@ public:
|
||||
# if (SYSTEMC_VERSION>20011000)
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# else
|
||||
// VCD files must have integer timestamps, so we write all times in increments of time_resolution
|
||||
// VCD files must have integer timestamps, so we write all times in
|
||||
// increments of time_resolution
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# endif
|
||||
}
|
||||
|
||||
+578
-534
File diff suppressed because it is too large
Load Diff
+95
-86
@@ -16,11 +16,11 @@
|
||||
/// \file
|
||||
/// \brief Verilator: Common include for OS portability (verilated & verilator)
|
||||
///
|
||||
/// This header is used by both the Verilator source code (run on the
|
||||
/// build and host system), and the Verilated output (run on the target
|
||||
/// system). Code needed by only the host system goes into
|
||||
/// config_build.h.in, code needed by Verilated code only goes into
|
||||
/// verilated.h, and code needed by both goes here (verilatedos.h).
|
||||
/// This header is used by both the Verilator source code (run on the
|
||||
/// build and host system), and the Verilated output (run on the target
|
||||
/// system). Code needed by only the host system goes into
|
||||
/// config_build.h.in, code needed by Verilated code only goes into
|
||||
/// verilated.h, and code needed by both goes here (verilatedos.h).
|
||||
///
|
||||
/// Code available from: http://www.veripool.org/verilator
|
||||
///
|
||||
@@ -36,12 +36,10 @@
|
||||
#ifdef __GNUC__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__ ((always_inline))
|
||||
# define VL_ATTR_COLD __attribute__ ((cold))
|
||||
# define VL_ATTR_HOT __attribute__ ((hot))
|
||||
# define VL_ATTR_NORETURN __attribute__ ((noreturn))
|
||||
# ifdef _WIN32
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) // GCC with MS runtime will fool the print arg checker
|
||||
# else
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
|
||||
# endif
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
|
||||
# define VL_ATTR_PURE __attribute__ ((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__ ((unused))
|
||||
# define VL_FUNC __func__
|
||||
@@ -58,8 +56,8 @@
|
||||
# define VL_EXCLUDES(x) __attribute__ ((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__ ((scoped_lockable))
|
||||
# endif
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define VL_UNREACHABLE __builtin_unreachable();
|
||||
# define VL_PREFETCH_RD(p) __builtin_prefetch((p),0)
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p),1)
|
||||
@@ -69,51 +67,57 @@
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALIGNED
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
|
||||
#endif
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||
#endif
|
||||
#ifndef VL_ATTR_COLD
|
||||
# define VL_ATTR_COLD ///< Function is rarely executed
|
||||
#endif
|
||||
#ifndef VL_ATTR_HOT
|
||||
# define VL_ATTR_HOT ///< Function is highly executed
|
||||
#endif
|
||||
#ifndef VL_ATTR_NORETURN
|
||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||
#endif
|
||||
#ifndef VL_ATTR_PRINTF
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
|
||||
#endif
|
||||
#ifndef VL_ATTR_PURE
|
||||
# define VL_ATTR_PURE ///< Function is pure (and thus also VL_MT_SAFE)
|
||||
# define VL_ATTR_PURE ///< Function is pure (and thus also VL_MT_SAFE)
|
||||
#endif
|
||||
#ifndef VL_ATTR_UNUSED
|
||||
# define VL_ATTR_UNUSED ///< Function that may be never used
|
||||
# define VL_ATTR_UNUSED ///< Function that may be never used
|
||||
#endif
|
||||
#ifndef VL_FUNC
|
||||
# define VL_FUNC "__func__" ///< Name of current function for error macros
|
||||
# define VL_FUNC "__func__" ///< Name of current function for error macros
|
||||
#endif
|
||||
#ifndef VL_CAPABILITY
|
||||
# define VL_ACQUIRE(...) ///< Function requires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function aquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if aquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if aquired a shared capability (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE(...) ///< Function requires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function aquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if aquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if aquired a shared capability (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
#endif
|
||||
#ifndef VL_LIKELY
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
#endif
|
||||
#ifndef VL_UNREACHABLE
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RD
|
||||
# define VL_PREFETCH_RD(p) ///< Prefetch data with read intent
|
||||
# define VL_PREFETCH_RD(p) ///< Prefetch data with read intent
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RW
|
||||
# define VL_PREFETCH_RW(p) ///< Prefetch data with read/write intent
|
||||
# define VL_PREFETCH_RW(p) ///< Prefetch data with read/write intent
|
||||
#endif
|
||||
|
||||
#ifdef VL_THREADED
|
||||
@@ -124,12 +128,12 @@
|
||||
# else
|
||||
# error "Unsupported compiler for VL_THREADED: No thread-local declarator"
|
||||
# endif
|
||||
# define VL_THREAD_LOCAL thread_local ///< Use new C++ static local thread
|
||||
# define VL_THREAD_LOCAL thread_local ///< Use new C++ static local thread
|
||||
#else
|
||||
# define VL_THREAD_LOCAL ///< Use new C++ static local thread
|
||||
# define VL_THREAD_LOCAL ///< Use new C++ static local thread
|
||||
#endif
|
||||
#define VL_THREAD ///< Deprecated
|
||||
#define VL_STATIC_OR_THREAD static ///< Deprecated
|
||||
#define VL_THREAD ///< Deprecated
|
||||
#define VL_STATIC_OR_THREAD static ///< Deprecated
|
||||
|
||||
#define VL_PURE ///< Comment tag that Function is pure (and thus also VL_MT_SAFE)
|
||||
#define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED
|
||||
@@ -138,18 +142,18 @@
|
||||
#define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, protected to make sure single-caller
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define VL_ULL(c) (c##ui64) ///< Add appropriate suffix to 64-bit constant
|
||||
# define VL_ULL(c) (c##ui64) ///< Add appropriate suffix to 64-bit constant
|
||||
#else
|
||||
# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant
|
||||
# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant
|
||||
#endif
|
||||
|
||||
// This is not necessarily the same as #UL, depending on what the IData typedef is.
|
||||
#define VL_UL(c) (static_cast<IData>(c##UL)) ///< Add appropriate suffix to 32-bit constant
|
||||
#define VL_UL(c) (static_cast<IData>(c##UL)) ///< Add appropriate suffix to 32-bit constant
|
||||
|
||||
#if defined(VL_CPPCHECK) || defined(__clang_analyzer__)
|
||||
# define VL_DANGLING(v)
|
||||
#else
|
||||
# define VL_DANGLING(v) do { (v) = NULL; } while(0) ///< After e.g. delete, set variable to NULL to indicate must not use later
|
||||
# define VL_DANGLING(v) do { (v) = NULL; } while(0) ///< After e.g. delete, set variable to NULL to indicate must not use later
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
@@ -180,7 +184,7 @@
|
||||
// Optimization
|
||||
|
||||
#ifndef VL_INLINE_OPT
|
||||
# define VL_INLINE_OPT ///< "inline" if compiling all objects in single compiler run
|
||||
# define VL_INLINE_OPT ///< "inline" if compiling all objects in single compiler run
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
@@ -202,42 +206,47 @@
|
||||
//=========================================================================
|
||||
// Basic integer types
|
||||
|
||||
#ifdef __MINGW32__
|
||||
# define __USE_MINGW_ANSI_STDIO 1 // Force old MinGW (GCC 5 and older) to use C99 formats
|
||||
# define __STDC_FORMAT_MACROS 1 // Otherwise MinGW doesn't get PRId64 for fstapi.c
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
|
||||
# include <stdint.h>
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
typedef unsigned short int vluint16_t; ///< 16-bit unsigned type
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
typedef unsigned short int vluint16_t; ///< 16-bit unsigned type
|
||||
# if defined(__uint32_t_defined) || defined(___int32_t_defined) // Newer Cygwin uint32_t in stdint.h as an unsigned int
|
||||
typedef int32_t vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit unsigned type
|
||||
# else // Older Cygwin has long==uint32_t
|
||||
typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef long vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned long vluint32_t; ///< 32-bit unsigned type
|
||||
typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef long vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned long vluint32_t; ///< 32-bit unsigned type
|
||||
# endif
|
||||
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
# else
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
# endif
|
||||
|
||||
#elif defined(_WIN32) && defined(_MSC_VER)
|
||||
|
||||
typedef unsigned __int8 uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int16 uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int32 uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int8 vluint8_t; ///< 8-bit unsigned type
|
||||
typedef unsigned __int16 vluint16_t; ///< 16-bit unsigned type
|
||||
typedef signed __int32 vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned __int32 vluint32_t; ///< 32-bit unsigned type
|
||||
typedef signed __int64 vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned __int64 vluint64_t; ///< 64-bit unsigned type
|
||||
typedef unsigned __int8 uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int16 uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int32 uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int8 vluint8_t; ///< 8-bit unsigned type
|
||||
typedef unsigned __int16 vluint16_t; ///< 16-bit unsigned type
|
||||
typedef signed __int32 vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned __int32 vluint32_t; ///< 32-bit unsigned type
|
||||
typedef signed __int64 vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned __int64 vluint64_t; ///< 64-bit unsigned type
|
||||
|
||||
# ifndef _SSIZE_T_DEFINED
|
||||
# ifdef _WIN64
|
||||
@@ -253,16 +262,16 @@ typedef signed __int32 ssize_t; ///< signed size_t; returned fro
|
||||
# include <stdint.h> // Linux and most flavors
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef uint8_t vluint8_t; ///< 32-bit unsigned type
|
||||
typedef uint16_t vluint16_t; ///< 32-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit signed type
|
||||
typedef uint8_t vluint8_t; ///< 32-bit unsigned type
|
||||
typedef uint16_t vluint16_t; ///< 32-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit signed type
|
||||
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
# else
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -270,10 +279,10 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
// Printing printf/scanf formats
|
||||
// Alas cinttypes isn't that standard yet
|
||||
|
||||
#ifdef _WIN32
|
||||
# define __STDC_FORMAT_MACROS 1 // Otherwise MinGW doesn't get PRId64 for fstapi.c
|
||||
// Use Microsoft-specific format specifiers for Microsoft Visual C++ only
|
||||
#ifdef _MSC_VER
|
||||
# define VL_PRI64 "I64"
|
||||
#else // Linux or compliant Unix flavors
|
||||
#else // use standard C99 format specifiers
|
||||
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
|
||||
# define VL_PRI64 "l"
|
||||
# else
|
||||
@@ -305,11 +314,11 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
//=========================================================================
|
||||
// Integer size macros
|
||||
|
||||
#define VL_BYTESIZE 8 ///< Bits in a byte
|
||||
#define VL_SHORTSIZE 16 ///< Bits in a short
|
||||
#define VL_WORDSIZE 32 ///< Bits in a word
|
||||
#define VL_QUADSIZE 64 ///< Bits in a quadword
|
||||
#define VL_WORDSIZE_LOG2 5 ///< log2(VL_WORDSIZE)
|
||||
#define VL_BYTESIZE 8 ///< Bits in a byte
|
||||
#define VL_SHORTSIZE 16 ///< Bits in a short
|
||||
#define VL_WORDSIZE 32 ///< Bits in a word
|
||||
#define VL_QUADSIZE 64 ///< Bits in a quadword
|
||||
#define VL_WORDSIZE_LOG2 5 ///< log2(VL_WORDSIZE)
|
||||
|
||||
/// Bytes this number of bits needs (1 bit=1 byte)
|
||||
#define VL_BYTES_I(nbits) (((nbits)+(VL_BYTESIZE-1))/VL_BYTESIZE)
|
||||
@@ -327,21 +336,21 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
//=========================================================================
|
||||
// Verilated function size macros
|
||||
|
||||
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
|
||||
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
|
||||
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
|
||||
//=========================================================================
|
||||
// Base macros
|
||||
|
||||
#define VL_SIZEBITS_I (VL_WORDSIZE-1) ///< Bit mask for bits in a word
|
||||
#define VL_SIZEBITS_Q (VL_QUADSIZE-1) ///< Bit mask for bits in a quad
|
||||
#define VL_SIZEBITS_I (VL_WORDSIZE-1) ///< Bit mask for bits in a word
|
||||
#define VL_SIZEBITS_Q (VL_QUADSIZE-1) ///< Bit mask for bits in a quad
|
||||
|
||||
/// Mask for words with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) \
|
||||
? ((1U << ((nbits) & VL_SIZEBITS_I) )-1) : ~0)
|
||||
? ((1U << ((nbits) & VL_SIZEBITS_I) )-1) : ~0)
|
||||
/// Mask for quads with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_Q(nbits) (((nbits) & VL_SIZEBITS_Q) \
|
||||
? ((VL_ULL(1) << ((nbits) & VL_SIZEBITS_Q) )-VL_ULL(1)) : VL_ULL(~0))
|
||||
? ((VL_ULL(1) << ((nbits) & VL_SIZEBITS_Q) )-VL_ULL(1)) : VL_ULL(~0))
|
||||
#define VL_BITWORD_I(bit) ((bit)/VL_WORDSIZE) ///< Word number for a wide quantity
|
||||
#define VL_BITBIT_I(bit) ((bit)&VL_SIZEBITS_I) ///< Bit number for a bit in a long
|
||||
#define VL_BITBIT_Q(bit) ((bit)&VL_SIZEBITS_Q) ///< Bit number for a bit in a quad
|
||||
|
||||
+2
-2
@@ -302,7 +302,7 @@ serial mode; that is a possible area for improvement.)
|
||||
|
||||
=item C<Wave Scheduling>
|
||||
|
||||
To allow the verilated model to run in parallel with the testbench, it
|
||||
To allow the Verilated model to run in parallel with the testbench, it
|
||||
might be nice to support "wave" scheduling, in which work on a cycle begins
|
||||
before eval() is called or continues after eval() returns. For now all
|
||||
work on a cycle happens during the eval() call, leaving Verilator's threads
|
||||
@@ -826,7 +826,7 @@ level. For example after inlining:
|
||||
|
||||
The textual name of this node AST type (always in capitals). Many of these
|
||||
correspond directly to Verilog entities (for example C<MODULE> and
|
||||
C<TASK>), but others are internal to Verialtor (for example C<NETLIST> and
|
||||
C<TASK>), but others are internal to Verilator (for example C<NETLIST> and
|
||||
C<BASICDTYPE>).
|
||||
|
||||
=item Address of the node
|
||||
|
||||
+18
-18
@@ -23,9 +23,9 @@ our $Debug;
|
||||
our $Opt_Unsup;
|
||||
|
||||
if (! GetOptions (
|
||||
#"help" => \&usage,
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"unsup!" => \$Opt_Unsup, # Ignore unsupported
|
||||
#"help" => \&usage,
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"unsup!" => \$Opt_Unsup, # Ignore unsupported
|
||||
)) {
|
||||
die "usage();"
|
||||
}
|
||||
@@ -59,24 +59,24 @@ sub prep {
|
||||
my $rule = "";
|
||||
my $skip = 1;
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
if ($skip == 1) {
|
||||
next if $line !~ /%token/;
|
||||
$skip = 2;
|
||||
}
|
||||
if ($skip == 1) {
|
||||
next if $line !~ /%token/;
|
||||
$skip = 2;
|
||||
}
|
||||
|
||||
if ($Opt_Unsup) {
|
||||
$line =~ s!//UNSUP!|!g;
|
||||
$line =~ s!(\s+)\|(\s+)!$1$2!g; # As "UNSUP" often replaces "|"
|
||||
}
|
||||
if ($Opt_Unsup) {
|
||||
$line =~ s!//UNSUP!|!g;
|
||||
$line =~ s!(\s+)\|(\s+)!$1$2!g; # As "UNSUP" often replaces "|"
|
||||
}
|
||||
|
||||
# %type<foo>
|
||||
$line =~ s/^(%\S+)<(\S+)>/$1/;
|
||||
# rule<foo>
|
||||
$line =~ s/^([a-zA-Z0-9_]+)<\S+>:/$1:/;
|
||||
# Productions
|
||||
$line =~ s/[ \t]{[^}]*?}/\t{}/g;
|
||||
# %type<foo>
|
||||
$line =~ s/^(%\S+)<(\S+)>/$1/;
|
||||
# rule<foo>
|
||||
$line =~ s/^([a-zA-Z0-9_]+)<\S+>:/$1:/;
|
||||
# Productions
|
||||
$line =~ s/[ \t]{[^}]*?}/\t{}/g;
|
||||
|
||||
$fho->print($line);
|
||||
$fho->print($line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-20
@@ -32,27 +32,27 @@ foreach my $line (<STDIN>) {
|
||||
$line =~ s!\s+! !g;
|
||||
next if $line eq '';
|
||||
if ($line =~ m!^\%\%!) {
|
||||
$body++;
|
||||
$body++;
|
||||
} elsif ($body == 1) {
|
||||
$rule .= $line;
|
||||
if ($line =~ m!^\s*;\s*$!) {
|
||||
#print "Rule: $rule\n";
|
||||
($rule =~ /^([a-zA-Z0-9_]+)(<\S+>)?:(.*)$/) or die "%Error: No rule name: $1\n";
|
||||
my $rulename = $1; my $preaction = $3;
|
||||
$declared{$rulename} = $lineno;
|
||||
$preaction =~ s/\{[^\}]*\}/ /g;
|
||||
#print "RULEN $rulename PA $preaction\n" if $Debug;
|
||||
$rule = '';
|
||||
foreach my $ref (split /\s+/, $preaction) {
|
||||
next if $ref !~ /^[a-zA-Z]/;
|
||||
next if $ref eq $rulename;
|
||||
if (!$used{$ref} && $declared{$ref}) {
|
||||
print " %Warning: $lineno: $ref used by $rulename after declaration\n";
|
||||
}
|
||||
$used{$ref} = $lineno;
|
||||
print " ref $ref\n" if $Debug;
|
||||
}
|
||||
}
|
||||
$rule .= $line;
|
||||
if ($line =~ m!^\s*;\s*$!) {
|
||||
#print "Rule: $rule\n";
|
||||
($rule =~ /^([a-zA-Z0-9_]+)(<\S+>)?:(.*)$/) or die "%Error: No rule name: $1\n";
|
||||
my $rulename = $1; my $preaction = $3;
|
||||
$declared{$rulename} = $lineno;
|
||||
$preaction =~ s/\{[^\}]*\}/ /g;
|
||||
#print "RULEN $rulename PA $preaction\n" if $Debug;
|
||||
$rule = '';
|
||||
foreach my $ref (split /\s+/, $preaction) {
|
||||
next if $ref !~ /^[a-zA-Z]/;
|
||||
next if $ref eq $rulename;
|
||||
if (!$used{$ref} && $declared{$ref}) {
|
||||
print " %Warning: $lineno: $ref used by $rulename after declaration\n";
|
||||
}
|
||||
$used{$ref} = $lineno;
|
||||
print " ref $ref\n" if $Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-36
@@ -24,11 +24,11 @@ $Debug = 0;
|
||||
my $opt_filename;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
if (! GetOptions (
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ sub debug {
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!$opt_filename) {
|
||||
$opt_filename = $param;
|
||||
$opt_filename = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,30 +64,30 @@ sub dotread {
|
||||
my $header = 1;
|
||||
my $vnum = 0;
|
||||
while (defined (my $line = $fh->getline)) {
|
||||
if ($line =~ /^\t([a-zA-Z0-9_]+)\t(.*)$/) {
|
||||
next if $1 eq 'nTITLE';
|
||||
$header = 0;
|
||||
$Vertexes{$1} = {num => $vnum++,
|
||||
line => $line,
|
||||
name => $1,};
|
||||
}
|
||||
elsif ($line =~ /^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s+(.*)$/) {
|
||||
my $from=$1; my $to=$2;
|
||||
my $weight = 1; $weight = $1 if $line =~ /weight=(\d+)/;
|
||||
my $cutable = undef; $cutable = $1 if $line =~ /style=(\S+)/;
|
||||
my $edge = {num => $vnum++,
|
||||
line => $line,
|
||||
weight => $weight,
|
||||
cutable => $cutable,
|
||||
from => $from,
|
||||
to => $to,};
|
||||
push @Edges, $edge;
|
||||
$Edges{$from}{$to} = $edge;
|
||||
}
|
||||
elsif ($header) {
|
||||
push @Header, $line;
|
||||
print "IGNORE: $line";
|
||||
}
|
||||
if ($line =~ /^\t([a-zA-Z0-9_]+)\t(.*)$/) {
|
||||
next if $1 eq 'nTITLE';
|
||||
$header = 0;
|
||||
$Vertexes{$1} = {num => $vnum++,
|
||||
line => $line,
|
||||
name => $1,};
|
||||
}
|
||||
elsif ($line =~ /^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s+(.*)$/) {
|
||||
my $from=$1; my $to=$2;
|
||||
my $weight = 1; $weight = $1 if $line =~ /weight=(\d+)/;
|
||||
my $cutable = undef; $cutable = $1 if $line =~ /style=(\S+)/;
|
||||
my $edge = {num => $vnum++,
|
||||
line => $line,
|
||||
weight => $weight,
|
||||
cutable => $cutable,
|
||||
from => $from,
|
||||
to => $to,};
|
||||
push @Edges, $edge;
|
||||
$Edges{$from}{$to} = $edge;
|
||||
}
|
||||
elsif ($header) {
|
||||
push @Header, $line;
|
||||
print "IGNORE: $line";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,14 +100,14 @@ sub cwrite {
|
||||
$fh->print("void V3GraphTestImport::dotImport() {\n");
|
||||
$fh->print(" DfaGraph* gp = &m_graph;\n");
|
||||
foreach my $ver (sort {$a->{num} <=> $b->{num}} (values %Vertexes)) {
|
||||
$fh->printf(" V3GraphTestVertex* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n",
|
||||
$ver->{name}, $ver->{name}, $ver->{name});
|
||||
$fh->printf(" V3GraphTestVertex* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n",
|
||||
$ver->{name}, $ver->{name}, $ver->{name});
|
||||
}
|
||||
$fh->print("\n");
|
||||
foreach my $edge (@Edges) {
|
||||
$fh->printf(" new V3GraphEdge(gp, %s, %s, %s, %s);\n",
|
||||
$edge->{from}, $edge->{to},
|
||||
$edge->{weight}, $edge->{cutable}?"true":"false");
|
||||
$fh->printf(" new V3GraphEdge(gp, %s, %s, %s, %s);\n",
|
||||
$edge->{from}, $edge->{to},
|
||||
$edge->{weight}, $edge->{cutable}?"true":"false");
|
||||
}
|
||||
$fh->print("}\n");
|
||||
}
|
||||
|
||||
+35
-35
@@ -26,12 +26,12 @@ my $opt_filename;
|
||||
my $opt_circle;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
if (! GetOptions (
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
"circle=s" => \$opt_circle,
|
||||
)) {
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
"circle=s" => \$opt_circle,
|
||||
)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ sub debug {
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!$opt_filename) {
|
||||
$opt_filename = $param;
|
||||
$opt_filename = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,16 +68,16 @@ sub dotread {
|
||||
my $fh = IO::File->new($filename) or die "%Error: $! $filename,";
|
||||
my $header = 1;
|
||||
while (defined (my $line = $fh->getline)) {
|
||||
if ($line =~ /^\t([a-zA-Z0-9_]+)\t(.*)$/) {
|
||||
$header = 0;
|
||||
$Vertexes{$1} = $2;
|
||||
}
|
||||
elsif ($line =~ /^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s+(.*)$/) {
|
||||
$Edges{$1}{$2} = $3;
|
||||
}
|
||||
elsif ($header) {
|
||||
push @Header, $line;
|
||||
}
|
||||
if ($line =~ /^\t([a-zA-Z0-9_]+)\t(.*)$/) {
|
||||
$header = 0;
|
||||
$Vertexes{$1} = $2;
|
||||
}
|
||||
elsif ($line =~ /^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s+(.*)$/) {
|
||||
$Edges{$1}{$2} = $3;
|
||||
}
|
||||
elsif ($header) {
|
||||
push @Header, $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ sub dotread {
|
||||
|
||||
sub simplify {
|
||||
foreach my $ver (sort (keys %Vertexes)) {
|
||||
$Vertexes{$ver} = _simplify($Vertexes{$ver});
|
||||
$Vertexes{$ver} = _simplify($Vertexes{$ver});
|
||||
}
|
||||
foreach my $v1 (sort (keys %Edges)) {
|
||||
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
|
||||
$Edges{$v1}{$v2} = _simplify($Edges{$v1}{$v2});
|
||||
}
|
||||
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
|
||||
$Edges{$v1}{$v2} = _simplify($Edges{$v1}{$v2});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,15 +103,15 @@ sub _simplify {
|
||||
|
||||
sub dotwrite {
|
||||
foreach my $line (@Header) {
|
||||
print "$line";
|
||||
print "$line";
|
||||
}
|
||||
foreach my $ver (sort (keys %Vertexes)) {
|
||||
print "\t$ver\t$Vertexes{$ver}\n";
|
||||
print "\t$ver\t$Vertexes{$ver}\n";
|
||||
}
|
||||
foreach my $v1 (sort (keys %Edges)) {
|
||||
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
|
||||
print "\t$v1 -> $v2\t$Edges{$v1}{$v2}\n";
|
||||
}
|
||||
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
|
||||
print "\t$v1 -> $v2\t$Edges{$v1}{$v2}\n";
|
||||
}
|
||||
}
|
||||
print "}\n";
|
||||
}
|
||||
@@ -125,15 +125,15 @@ sub circle {
|
||||
_circle_recurse($node, 1);
|
||||
|
||||
foreach my $ver (keys %Vertexes) {
|
||||
if (!$User{$ver}) {
|
||||
delete $Vertexes{$ver};
|
||||
delete $Edges{$ver};
|
||||
}
|
||||
if (!$User{$ver}) {
|
||||
delete $Vertexes{$ver};
|
||||
delete $Edges{$ver};
|
||||
}
|
||||
}
|
||||
foreach my $v1 (sort (keys %Edges)) {
|
||||
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
|
||||
if (!$Vertexes{$v2}) { delete $Edges{$v1}{$v2}; }
|
||||
}
|
||||
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
|
||||
if (!$Vertexes{$v2}) { delete $Edges{$v1}{$v2}; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ sub _circle_recurse {
|
||||
$User2{$node} = 1;
|
||||
my $r = 0;
|
||||
foreach my $v2 (keys %{$Edges{$node}}) {
|
||||
$r |= _circle_recurse($v2,$level++)||0;
|
||||
$r |= _circle_recurse($v2,$level++)||0;
|
||||
}
|
||||
$User{$node} = 1 if $r;
|
||||
$User2{$node} = 2;
|
||||
|
||||
+22
-19
@@ -87,27 +87,30 @@ sub find_edits {
|
||||
my $hunk = $editlines->{$lineno}{hunk};
|
||||
$hunk_lastlines{$hunk} ||= $lineno if $editlines->{$lineno}{user_edit};
|
||||
}
|
||||
# Expand to include }'s that finish basic block
|
||||
foreach my $hunk (keys %hunk_lastlines) {
|
||||
while (my $lineno = $hunk_lastlines{$hunk}) {
|
||||
++$lineno;
|
||||
if (($editlines->{$lineno}{line} || "")
|
||||
=~ /^[+ ]\s+}[ \t};]*$/) {
|
||||
$hunk_lastlines{$hunk} = $lineno;
|
||||
} else {
|
||||
last;
|
||||
|
||||
if ($Opt_Widen) {
|
||||
# Expand to include }'s that finish basic block
|
||||
foreach my $hunk (keys %hunk_lastlines) {
|
||||
while (my $lineno = $hunk_lastlines{$hunk}) {
|
||||
++$lineno;
|
||||
if (($editlines->{$lineno}{line} || "")
|
||||
=~ /^[+ ]\s+}[ \t};]*$/) {
|
||||
$hunk_lastlines{$hunk} = $lineno;
|
||||
} else {
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Expand to always untabify at least 3 lines (so that future diff will
|
||||
# have non-tabs within a edit hunk distance
|
||||
foreach my $hunk (keys %hunk_firstlines) {
|
||||
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
|
||||
--$hunk_firstlines{$hunk};
|
||||
++$hunk_lastlines{$hunk};
|
||||
}
|
||||
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
|
||||
++$hunk_lastlines{$hunk};
|
||||
# Expand to always untabify at least 3 lines (so that future diff will
|
||||
# have non-tabs within a edit hunk distance
|
||||
foreach my $hunk (keys %hunk_firstlines) {
|
||||
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
|
||||
--$hunk_firstlines{$hunk};
|
||||
++$hunk_lastlines{$hunk};
|
||||
}
|
||||
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
|
||||
++$hunk_lastlines{$hunk};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+51
-51
@@ -18,11 +18,11 @@ our $Opt_Jobs = calc_jobs();
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions (
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"<>" => sub { die "%Error: Unknown parameter: $_[0]\n"; },
|
||||
"stage=i" => \$Opt_Stage,
|
||||
"j=i" => \$Opt_Jobs,
|
||||
if (! GetOptions(
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"<>" => sub { die "%Error: Unknown parameter: $_[0]\n"; },
|
||||
"stage=i" => \$Opt_Stage,
|
||||
"j=i" => \$Opt_Jobs,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'install_test --help'\n";
|
||||
}
|
||||
@@ -46,51 +46,51 @@ sub test {
|
||||
my $testdirn= $srcdir."/test_regress/obj_dir/install_test_testn";
|
||||
|
||||
if ($Opt_Stage <= 0) {
|
||||
run("/bin/rm -rf $blddir");
|
||||
run("/bin/mkdir -p $blddir");
|
||||
run("cd $blddir && $srcdir/configure --prefix $prefix");
|
||||
run("cd $blddir && make -j $Opt_Jobs");
|
||||
run("/bin/rm -rf $blddir");
|
||||
run("/bin/mkdir -p $blddir");
|
||||
run("cd $blddir && $srcdir/configure --prefix $prefix");
|
||||
run("cd $blddir && make -j $Opt_Jobs");
|
||||
}
|
||||
|
||||
# Install it under the prefix
|
||||
if ($Opt_Stage <= 1) {
|
||||
run("/bin/rm -rf $prefix");
|
||||
run("/bin/mkdir -p $prefix");
|
||||
run("cd $blddir && make install");
|
||||
run("test -e $prefix/share/man/man1/verilator.1");
|
||||
run("/bin/rm -rf $prefix");
|
||||
run("/bin/mkdir -p $prefix");
|
||||
run("cd $blddir && make install");
|
||||
run("test -e $prefix/share/man/man1/verilator.1");
|
||||
run("test -e $prefix/share/verilator/examples/tracing_c/Makefile");
|
||||
run("test -e $prefix/share/verilator/include/verilated.h");
|
||||
run("test -e $prefix/bin/verilator");
|
||||
run("test -e $prefix/bin/verilator_bin");
|
||||
run("test -e $prefix/bin/verilator_bin_dbg");
|
||||
run("test -e $prefix/share/verilator/include/verilated.h");
|
||||
run("test -e $prefix/bin/verilator");
|
||||
run("test -e $prefix/bin/verilator_bin");
|
||||
run("test -e $prefix/bin/verilator_bin_dbg");
|
||||
run("test -e $prefix/bin/verilator_gantt");
|
||||
run("test -e $prefix/bin/verilator_profcfunc");
|
||||
run("test -e $prefix/bin/verilator_profcfunc");
|
||||
}
|
||||
|
||||
# Run a test using just the path
|
||||
if ($Opt_Stage <= 2) {
|
||||
my $dir = $testdirp;
|
||||
run("/bin/rm -rf $dir");
|
||||
run("/bin/mkdir -p $dir");
|
||||
my $bin1 = $prefix."/bin";
|
||||
my $bin2 = $prefix."/share/bin";
|
||||
write_verilog($dir);
|
||||
run("cd $dir && PATH=$bin1:$bin2:\$PATH verilator --cc foo.v --exe foo.cpp");
|
||||
run("cd $dir/obj_dir && PATH=$bin1:$bin2:\$PATH make -f Vfoo.mk");
|
||||
run("cd $dir && PATH=$bin1:$bin2:\$PATH obj_dir/Vfoo");
|
||||
my $dir = $testdirp;
|
||||
run("/bin/rm -rf $dir");
|
||||
run("/bin/mkdir -p $dir");
|
||||
my $bin1 = $prefix."/bin";
|
||||
my $bin2 = $prefix."/share/bin";
|
||||
write_verilog($dir);
|
||||
run("cd $dir && PATH=$bin1:$bin2:\$PATH verilator --cc foo.v --exe foo.cpp");
|
||||
run("cd $dir/obj_dir && PATH=$bin1:$bin2:\$PATH make -f Vfoo.mk");
|
||||
run("cd $dir && PATH=$bin1:$bin2:\$PATH obj_dir/Vfoo");
|
||||
}
|
||||
|
||||
# Run a test using exact path to binary
|
||||
if ($Opt_Stage <= 3) {
|
||||
my $dir = $testdirn;
|
||||
run("/bin/rm -rf $dir");
|
||||
run("/bin/mkdir -p $dir");
|
||||
write_verilog($dir);
|
||||
my $bin1 = $prefix."/bin";
|
||||
my $bin2 = $prefix."/share/bin";
|
||||
run("cd $dir && $bin1/verilator --cc foo.v --exe foo.cpp");
|
||||
run("cd $dir/obj_dir && make -f Vfoo.mk");
|
||||
run("cd $dir/obj_dir && ./Vfoo");
|
||||
my $dir = $testdirn;
|
||||
run("/bin/rm -rf $dir");
|
||||
run("/bin/mkdir -p $dir");
|
||||
write_verilog($dir);
|
||||
my $bin1 = $prefix."/bin";
|
||||
my $bin2 = $prefix."/share/bin";
|
||||
run("cd $dir && $bin1/verilator --cc foo.v --exe foo.cpp");
|
||||
run("cd $dir/obj_dir && make -f Vfoo.mk");
|
||||
run("cd $dir/obj_dir && ./Vfoo");
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 9) {
|
||||
@@ -102,29 +102,29 @@ sub write_verilog {
|
||||
my $dir = shift;
|
||||
IO::File->new(">$dir/foo.v")->print('module t; initial begin $display("HELLO WORLD"); $finish; end endmodule'."\n");
|
||||
my $fh = IO::File->new(">$dir/foo.cpp");
|
||||
$fh->print('#include "Vfoo.h"' ,"\n");
|
||||
$fh->print('unsigned int main_time = 0;' ,"\n");
|
||||
$fh->print('#include "Vfoo.h"' ,"\n");
|
||||
$fh->print('unsigned int main_time = 0;' ,"\n");
|
||||
$fh->print('double sc_time_stamp() {' ,"\n");
|
||||
$fh->print(' return main_time;' ,"\n");
|
||||
$fh->print('}' ,"\n");
|
||||
$fh->print('int main() {' ,"\n");
|
||||
$fh->print(' Vfoo *top = new Vfoo;' ,"\n");
|
||||
$fh->print(' return main_time;' ,"\n");
|
||||
$fh->print('}' ,"\n");
|
||||
$fh->print('int main() {' ,"\n");
|
||||
$fh->print(' Vfoo *top = new Vfoo;' ,"\n");
|
||||
$fh->print(' while (!Verilated::gotFinish()) {',"\n");
|
||||
$fh->print(' top->eval();' ,"\n");
|
||||
$fh->print(' main_time++;' ,"\n");
|
||||
$fh->print(' }' ,"\n");
|
||||
$fh->print(' top->final();' ,"\n");
|
||||
$fh->print('}' ,"\n");
|
||||
$fh->print(' }' ,"\n");
|
||||
$fh->print(' top->final();' ,"\n");
|
||||
$fh->print('}' ,"\n");
|
||||
}
|
||||
|
||||
sub cleanenv {
|
||||
foreach my $var (keys %ENV) {
|
||||
if ($var eq "VERILATOR_ROOT"
|
||||
if ($var eq "VERILATOR_ROOT"
|
||||
|| $var eq "VERILATOR_INCLUDE"
|
||||
|| $var eq "VERILATOR_NO_OPT_BUILD") {
|
||||
print "unset $var # Was '$ENV{$var}'\n";
|
||||
delete $ENV{$var}
|
||||
}
|
||||
print "unset $var # Was '$ENV{$var}'\n";
|
||||
delete $ENV{$var}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +132,8 @@ sub cleanenv {
|
||||
|
||||
sub calc_jobs {
|
||||
my $ok = eval "
|
||||
use Unix::Processors;
|
||||
return Unix::Processors->new->max_online;
|
||||
use Unix::Processors;
|
||||
return Unix::Processors->new->max_online;
|
||||
";
|
||||
$ok && !$@ or return 1;
|
||||
print "driver.pl: Found $ok cores, using -j ",$ok+1,"\n" if $Debug;
|
||||
|
||||
+143
-143
@@ -23,11 +23,11 @@ $Debug = 0;
|
||||
my $opt_filename;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
if (! GetOptions (
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
@@ -51,9 +51,9 @@ sub debug {
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!$opt_filename) {
|
||||
$opt_filename = $param;
|
||||
$opt_filename = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,45 +64,45 @@ sub vread {
|
||||
my $fh = IO::File->new($filename) or die "%Error: $! $filename,";
|
||||
my $lasthier="";
|
||||
$Tree = {
|
||||
op => 'NETLIST',
|
||||
t => [[],[],[],[],[],],
|
||||
op => 'NETLIST',
|
||||
t => [[],[],[],[],[],],
|
||||
};
|
||||
my @stack;
|
||||
$stack[1] = $Tree;
|
||||
while (defined (my $line = $fh->getline)) {
|
||||
if ($line =~ /^\s+(\S+):\s+(\S+)\s+0x\S+\s+{(\d+)}\s+w(\d+)\s+(.*)$/) {
|
||||
my $hier = $1;
|
||||
my $op = $2;
|
||||
my $lineno = $3;
|
||||
my $width = $4;
|
||||
my $etc = $5;
|
||||
if ($line =~ /^\s+(\S+):\s+(\S+)\s+0x\S+\s+{(\d+)}\s+w(\d+)\s+(.*)$/) {
|
||||
my $hier = $1;
|
||||
my $op = $2;
|
||||
my $lineno = $3;
|
||||
my $width = $4;
|
||||
my $etc = $5;
|
||||
|
||||
$etc =~ s/__DOT__/./g;
|
||||
$etc =~ s/__PVT__//g;
|
||||
$etc =~ s/__DOT__/./g;
|
||||
$etc =~ s/__PVT__//g;
|
||||
|
||||
my $self = {
|
||||
op => $op,
|
||||
#width => $width,
|
||||
#lineno => $lineno,
|
||||
#line => $line,
|
||||
etc => $etc,
|
||||
args => [split(/[ \t]+/,$etc)],
|
||||
t => [[],[],[],[],[],],
|
||||
};
|
||||
my $self = {
|
||||
op => $op,
|
||||
#width => $width,
|
||||
#lineno => $lineno,
|
||||
#line => $line,
|
||||
etc => $etc,
|
||||
args => [split(/[ \t]+/,$etc)],
|
||||
t => [[],[],[],[],[],],
|
||||
};
|
||||
|
||||
my @hiers = (1,split(/:/,$hier));
|
||||
my $depth = $#hiers+1;
|
||||
my $newchild = $hiers[$#hiers];
|
||||
my @hiers = (1,split(/:/,$hier));
|
||||
my $depth = $#hiers+1;
|
||||
my $newchild = $hiers[$#hiers];
|
||||
|
||||
#print "DD $depth $newchild $op\n";
|
||||
#print "DD $depth $newchild $op\n";
|
||||
|
||||
push @{$stack[$depth-1]->{t}[$newchild]}, $self;
|
||||
$stack[$depth] = $self;
|
||||
push @{$stack[$depth-1]->{t}[$newchild]}, $self;
|
||||
$stack[$depth] = $self;
|
||||
|
||||
$lasthier = $hier;
|
||||
#print " $lasthier\n";
|
||||
#print Dumper($Tree);
|
||||
}
|
||||
$lasthier = $hier;
|
||||
#print " $lasthier\n";
|
||||
#print Dumper($Tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,125 +168,125 @@ sub nl { p "\n"," "x$Indent; }
|
||||
|
||||
sub gentree {
|
||||
%OpMap = (
|
||||
'NULLNODE' => sub { "" },
|
||||
'NETLIST' => sub { nl;t1;t2;t3;t4;t5; },
|
||||
'ACTIVE' => sub { p "always_act @(";t1;p ") begin";indentInc;nl;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'ADD' => sub { p1;p " + ";p2; },
|
||||
'ALWAYS' => sub { p "always @(";t1;p ") begin";indentInc;nl;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'ALWAYSPOST' => sub { p "ALWAYSPOST what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'AND' => sub { p1;p " & ";p2; },
|
||||
'ARRAYSEL' => sub { t1;p "[";t2;p "]"; },
|
||||
'ASSIGN' => sub { t2;p " = ";t1;p ";";nl; },
|
||||
'ASSIGNDLY' => sub { t2;p " <= ";t1;p ";";nl; },
|
||||
'ASSIGNPOST' => sub { p "ASSIGNPOST what{";t1;p " = ";t2;p ";";nl; },
|
||||
'ASSIGNPRE' => sub { p "ASSIGNPRE what{";t1;p " = ";t2;p ";";nl; },
|
||||
'ASSIGNW' => sub { p "assign ";t2;p " = ";t1;p ";";nl; },
|
||||
'ATTROF' => sub { p "ATTROF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'BEGIN' => sub { p "begin";indentInc;nl;t1;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'BITSEL' => sub { t1;local $Avoid_Hex=1; p "[";t2;p "]"; },
|
||||
'CASE' => sub { p "CASE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CASEITEM' => sub { p "CASEITEM what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CAST' => sub { p "CAST what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CCALL' => sub { p "CCALL what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CELL' => sub { a1;p " ";a7;p " (/*CELL*/);"; nl; },
|
||||
'CFUNC' => sub { p "CFUNC what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CHANGEDET' => sub { p "CHANGEDET what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CINCLUDE' => sub { p "CINCLUDE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'COMMENT' => sub { p "//COMMENT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl;nl; },
|
||||
'CONCAT' => sub { p "{";p1;p ",";p2;p "}"; },
|
||||
'CONDITIONAL' => sub { p1;p " ? ";p2;p " : ";p3; },
|
||||
'CONST' => sub { p_const(); },
|
||||
'COVER' => sub { p "COVER what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CRETURN' => sub { p "CRETURN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CSTMT' => sub { p "CSTMT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'DEFPARAM' => sub { p "defparam ";p1;p " = ";p2;p ";";nl; },
|
||||
'DISPLAY' => sub { p '$write("';p1;p "\",";p2;p3;p4;p5;p ");";nl; },
|
||||
'DIV' => sub { p1;p " / ";p2; },
|
||||
'EQ' => sub { p1;p " == ";p2; },
|
||||
'EQCASE' => sub { p1;p " === ";p2; },
|
||||
'EXTEND' => sub { t1; },
|
||||
'EXTRACT' => sub { t1;local $Avoid_Hex=1; p "[";t2;p ":";t3;p "]"; },
|
||||
'FINISH' => sub { p '$finish;';nl },
|
||||
'FOR' => sub { p "for (";p1;p ",";p2;p ",";p3;p ") begin";indentInc;nl;p4;p5;indentDec;p "end";nl; },
|
||||
'FUNC' => sub { p "FUNC what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'FUNCREF' => sub { p "FUNCREF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'GT' => sub { p1;p " > ";p2; },
|
||||
'GTE' => sub { p1;p " >= ";p2; },
|
||||
'IF' => sub { p "if (";p1;p ") begin";indentInc;nl;t2;indentDec;if (exists3) {p "end else begin";indentInc;nl;t3;indentDec;} p "end"; nl; },
|
||||
'INITARRAY' => sub { p "INITARRAY what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'INITIAL' => sub { p "initial begin";indentInc;nl;t1;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'LOGAND' => sub { p1;p " && ";p2; },
|
||||
'LOGNOT' => sub { p1;p " || ";p2; },
|
||||
'LOGOR' => sub { p "!";p1; },
|
||||
'LT' => sub { p1;p " < ";p2; },
|
||||
'LTE' => sub { p1;p " <= ";p2; },
|
||||
'MODDIV' => sub { p1;p " % ";p2; },
|
||||
'MODULE' => sub { p "module ";a1;p " (/*AUTOARG*/);";indentInc;nl;t1;t2;t3;t4;t5;indentDec;nl;p "endmodule";nl; },
|
||||
'MUL' => sub { p1;p " * ";p2; },
|
||||
'NEQ' => sub { p1;p " != ";p2; },
|
||||
'NEQCASE' => sub { p1;p " !== ";p2; },
|
||||
'NOT' => sub { p " ~";p1; },
|
||||
'OR' => sub { p1;p " | ";p2; },
|
||||
'PIN' => sub { p ";p ";p1;p " (";p2;p "),";nl; },
|
||||
'PORT' => sub { p ""; },
|
||||
'PRAGMA' => sub { p "PRAGMA what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'RAND' => sub { p '$rand'; },
|
||||
'RANGE' => sub { t1; local $Avoid_Hex=1; p "[";t2;p ":";t3;p "]"; },
|
||||
'REDAND' => sub { p "&(";p1;p ")"; },
|
||||
'REDOR' => sub { p "|(";p1;p ")"; },
|
||||
'REDXNOR' => sub { p "~|(";p1;p ")"; },
|
||||
'REDXOR' => sub { p "~^(";p1;p ")"; },
|
||||
'REPLICATE' => sub { p "{";p1;p "{";p2;p "}}"; },
|
||||
'SCCTOR' => sub { p "SCCTOR what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCHDR' => sub { p "SCHDR what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCIMP' => sub { p "SCIMP what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCINT' => sub { p "SCINT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCOPE' => sub { t1;t2;t3;t4;t5; },
|
||||
'SENITEM' => sub { a1;p " ";t1; },
|
||||
'SENTREE' => sub { t1;t2;t3;t4;t5; },
|
||||
'SHIFTL' => sub { p1;p " << ";p2; },
|
||||
'SHIFTR' => sub { p1;p " >> ";p2; },
|
||||
'STOP' => sub { p '$stop;';nl; },
|
||||
'SUB' => sub { p1;p " - ";p2; },
|
||||
'TASK' => sub { p "TASK what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'TASKREF' => sub { p "TASKREF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'TEXT' => sub { p "TEXT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'TIME' => sub { p '$time'; },
|
||||
'TOPSCOPE' => sub { t1;t2;t3;t4;t5; },
|
||||
'TRACE' => sub { p "TRACE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'UCFUNC' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ")"; },
|
||||
'UCSTMT' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ");";nl; },
|
||||
'NEGATE' => sub { p " -";p1; },
|
||||
'VAR' => sub { p_var(); },
|
||||
'VARPIN' => sub { p "VARPIN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'VARREF' => sub { a1; },
|
||||
'VARSCOPE' => sub { },
|
||||
'WHILE' => sub { t1; p "while (";t2;p ") begin";indentInc;nl;t3;t4;indentDec;p "end";nl; },
|
||||
'WORDSEL' => sub { p1;p "[";p2;p ":";p3;p "]"; },
|
||||
'XNOR' => sub { p1;p " ~^ ";p2; },
|
||||
'XOR' => sub { p1;p " ^";p2; },
|
||||
'NULLNODE' => sub { "" },
|
||||
'NETLIST' => sub { nl;t1;t2;t3;t4;t5; },
|
||||
'ACTIVE' => sub { p "always_act @(";t1;p ") begin";indentInc;nl;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'ADD' => sub { p1;p " + ";p2; },
|
||||
'ALWAYS' => sub { p "always @(";t1;p ") begin";indentInc;nl;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'ALWAYSPOST' => sub { p "ALWAYSPOST what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'AND' => sub { p1;p " & ";p2; },
|
||||
'ARRAYSEL' => sub { t1;p "[";t2;p "]"; },
|
||||
'ASSIGN' => sub { t2;p " = ";t1;p ";";nl; },
|
||||
'ASSIGNDLY' => sub { t2;p " <= ";t1;p ";";nl; },
|
||||
'ASSIGNPOST' => sub { p "ASSIGNPOST what{";t1;p " = ";t2;p ";";nl; },
|
||||
'ASSIGNPRE' => sub { p "ASSIGNPRE what{";t1;p " = ";t2;p ";";nl; },
|
||||
'ASSIGNW' => sub { p "assign ";t2;p " = ";t1;p ";";nl; },
|
||||
'ATTROF' => sub { p "ATTROF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'BEGIN' => sub { p "begin";indentInc;nl;t1;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'BITSEL' => sub { t1;local $Avoid_Hex=1; p "[";t2;p "]"; },
|
||||
'CASE' => sub { p "CASE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CASEITEM' => sub { p "CASEITEM what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CAST' => sub { p "CAST what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CCALL' => sub { p "CCALL what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CELL' => sub { a1;p " ";a7;p " (/*CELL*/);"; nl; },
|
||||
'CFUNC' => sub { p "CFUNC what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CHANGEDET' => sub { p "CHANGEDET what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CINCLUDE' => sub { p "CINCLUDE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'COMMENT' => sub { p "//COMMENT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl;nl; },
|
||||
'CONCAT' => sub { p "{";p1;p ",";p2;p "}"; },
|
||||
'CONDITIONAL' => sub { p1;p " ? ";p2;p " : ";p3; },
|
||||
'CONST' => sub { p_const(); },
|
||||
'COVER' => sub { p "COVER what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CRETURN' => sub { p "CRETURN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'CSTMT' => sub { p "CSTMT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'DEFPARAM' => sub { p "defparam ";p1;p " = ";p2;p ";";nl; },
|
||||
'DISPLAY' => sub { p '$write("';p1;p "\",";p2;p3;p4;p5;p ");";nl; },
|
||||
'DIV' => sub { p1;p " / ";p2; },
|
||||
'EQ' => sub { p1;p " == ";p2; },
|
||||
'EQCASE' => sub { p1;p " === ";p2; },
|
||||
'EXTEND' => sub { t1; },
|
||||
'EXTRACT' => sub { t1;local $Avoid_Hex=1; p "[";t2;p ":";t3;p "]"; },
|
||||
'FINISH' => sub { p '$finish;';nl },
|
||||
'FOR' => sub { p "for (";p1;p ",";p2;p ",";p3;p ") begin";indentInc;nl;p4;p5;indentDec;p "end";nl; },
|
||||
'FUNC' => sub { p "FUNC what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'FUNCREF' => sub { p "FUNCREF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'GT' => sub { p1;p " > ";p2; },
|
||||
'GTE' => sub { p1;p " >= ";p2; },
|
||||
'IF' => sub { p "if (";p1;p ") begin";indentInc;nl;t2;indentDec;if (exists3) {p "end else begin";indentInc;nl;t3;indentDec;} p "end"; nl; },
|
||||
'INITARRAY' => sub { p "INITARRAY what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'INITIAL' => sub { p "initial begin";indentInc;nl;t1;t2;t3;t4;t5;indentDec;p "end";nl; },
|
||||
'LOGAND' => sub { p1;p " && ";p2; },
|
||||
'LOGNOT' => sub { p1;p " || ";p2; },
|
||||
'LOGOR' => sub { p "!";p1; },
|
||||
'LT' => sub { p1;p " < ";p2; },
|
||||
'LTE' => sub { p1;p " <= ";p2; },
|
||||
'MODDIV' => sub { p1;p " % ";p2; },
|
||||
'MODULE' => sub { p "module ";a1;p " (/*AUTOARG*/);";indentInc;nl;t1;t2;t3;t4;t5;indentDec;nl;p "endmodule";nl; },
|
||||
'MUL' => sub { p1;p " * ";p2; },
|
||||
'NEQ' => sub { p1;p " != ";p2; },
|
||||
'NEQCASE' => sub { p1;p " !== ";p2; },
|
||||
'NOT' => sub { p " ~";p1; },
|
||||
'OR' => sub { p1;p " | ";p2; },
|
||||
'PIN' => sub { p ";p ";p1;p " (";p2;p "),";nl; },
|
||||
'PORT' => sub { p ""; },
|
||||
'PRAGMA' => sub { p "PRAGMA what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'RAND' => sub { p '$rand'; },
|
||||
'RANGE' => sub { t1; local $Avoid_Hex=1; p "[";t2;p ":";t3;p "]"; },
|
||||
'REDAND' => sub { p "&(";p1;p ")"; },
|
||||
'REDOR' => sub { p "|(";p1;p ")"; },
|
||||
'REDXNOR' => sub { p "~|(";p1;p ")"; },
|
||||
'REDXOR' => sub { p "~^(";p1;p ")"; },
|
||||
'REPLICATE' => sub { p "{";p1;p "{";p2;p "}}"; },
|
||||
'SCCTOR' => sub { p "SCCTOR what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCHDR' => sub { p "SCHDR what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCIMP' => sub { p "SCIMP what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCINT' => sub { p "SCINT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'SCOPE' => sub { t1;t2;t3;t4;t5; },
|
||||
'SENITEM' => sub { a1;p " ";t1; },
|
||||
'SENTREE' => sub { t1;t2;t3;t4;t5; },
|
||||
'SHIFTL' => sub { p1;p " << ";p2; },
|
||||
'SHIFTR' => sub { p1;p " >> ";p2; },
|
||||
'STOP' => sub { p '$stop;';nl; },
|
||||
'SUB' => sub { p1;p " - ";p2; },
|
||||
'TASK' => sub { p "TASK what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'TASKREF' => sub { p "TASKREF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'TEXT' => sub { p "TEXT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'TIME' => sub { p '$time'; },
|
||||
'TOPSCOPE' => sub { t1;t2;t3;t4;t5; },
|
||||
'TRACE' => sub { p "TRACE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'UCFUNC' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ")"; },
|
||||
'UCSTMT' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ");";nl; },
|
||||
'NEGATE' => sub { p " -";p1; },
|
||||
'VAR' => sub { p_var(); },
|
||||
'VARPIN' => sub { p "VARPIN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
|
||||
'VARREF' => sub { a1; },
|
||||
'VARSCOPE' => sub { },
|
||||
'WHILE' => sub { t1; p "while (";t2;p ") begin";indentInc;nl;t3;t4;indentDec;p "end";nl; },
|
||||
'WORDSEL' => sub { p1;p "[";p2;p ":";p3;p "]"; },
|
||||
'XNOR' => sub { p1;p " ~^ ";p2; },
|
||||
'XOR' => sub { p1;p " ^";p2; },
|
||||
);
|
||||
}
|
||||
|
||||
sub p_var {
|
||||
my $self = $Code_Self;
|
||||
if ($self->{etc} =~ /\[I\]/) {
|
||||
print "input";
|
||||
print "input";
|
||||
} elsif ($self->{etc} =~ /\[O\]/) {
|
||||
print "output";
|
||||
print "output";
|
||||
} else {
|
||||
print "reg";
|
||||
print "reg";
|
||||
}
|
||||
p "\t";
|
||||
{
|
||||
local $Avoid_Hex=1;
|
||||
t1;
|
||||
local $Avoid_Hex=1;
|
||||
t1;
|
||||
}
|
||||
p "\t";
|
||||
a1;
|
||||
if (exists2()) {
|
||||
p " = ";
|
||||
t2;
|
||||
p " = ";
|
||||
t2;
|
||||
}
|
||||
p ";";
|
||||
nl;
|
||||
@@ -295,10 +295,10 @@ sub p_var {
|
||||
sub p_const {
|
||||
my $v = $Code_Self->{args}[0];
|
||||
if ($v =~ /\?32\?h(.*)$/
|
||||
|| ($Avoid_Hex && $v =~ /^[0-9?]*h(.*)$/)) {
|
||||
print hex $1;
|
||||
|| ($Avoid_Hex && $v =~ /^[0-9?]*h(.*)$/)) {
|
||||
print hex $1;
|
||||
} else {
|
||||
print $v;
|
||||
print $v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+232
-225
@@ -21,12 +21,12 @@
|
||||
//
|
||||
// Note this can be called multiple times.
|
||||
// Create a IACTIVE(initial), SACTIVE(combo)
|
||||
// ALWAYS: Remove any-edges from sense list
|
||||
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
|
||||
// Else fold into SACTIVE(sequent).
|
||||
// OPTIMIZE: When support async clocks, fold into that active if possible
|
||||
// INITIAL: Move into IACTIVE
|
||||
// WIRE: Move into SACTIVE(combo)
|
||||
// ALWAYS: Remove any-edges from sense list
|
||||
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
|
||||
// Else fold into SACTIVE(sequent).
|
||||
// OPTIMIZE: When support async clocks, fold into that active if possible
|
||||
// INITIAL: Move into IACTIVE
|
||||
// WIRE: Move into SACTIVE(combo)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -56,66 +56,68 @@ class ActiveNamer : public ActiveBaseVisitor {
|
||||
private:
|
||||
typedef std::map<string,AstActive*> ActiveNameMap;
|
||||
// STATE
|
||||
AstScope* m_scopep; // Current scope to add statement to
|
||||
AstActive* m_iActivep; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
|
||||
AstScope* m_scopep; // Current scope to add statement to
|
||||
AstActive* m_iActivep; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
|
||||
|
||||
SenTreeSet m_activeSens; // Sen lists for each active we've made
|
||||
SenTreeSet m_activeSens; // Sen lists for each active we've made
|
||||
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
|
||||
ActiveMap m_activeMap; // Map sentree to active, for folding.
|
||||
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
|
||||
m_scopep->addActivep(nodep);
|
||||
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
|
||||
m_scopep->addActivep(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) {
|
||||
m_scopep = nodep;
|
||||
m_iActivep = NULL;
|
||||
m_cActivep = NULL;
|
||||
m_scopep = nodep;
|
||||
m_iActivep = NULL;
|
||||
m_cActivep = NULL;
|
||||
m_activeSens.clear();
|
||||
m_activeMap.clear();
|
||||
iterateChildren(nodep);
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) {
|
||||
// Simplify sensitivity list
|
||||
V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep);
|
||||
// Simplify sensitivity list
|
||||
V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
// Empty visitors, speed things up
|
||||
virtual void visit(AstNodeStmt* nodep) { }
|
||||
//--------------------
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
// Default: Just iterate
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// METHODS
|
||||
public:
|
||||
AstScope* scopep() { return m_scopep; }
|
||||
AstActive* getCActive(FileLine* fl) {
|
||||
if (!m_cActivep) {
|
||||
m_cActivep = new AstActive(fl, "combo",
|
||||
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Combo())));
|
||||
m_cActivep->sensesStorep(m_cActivep->sensesp());
|
||||
addActive(m_cActivep);
|
||||
}
|
||||
return m_cActivep;
|
||||
if (!m_cActivep) {
|
||||
m_cActivep = new AstActive(
|
||||
fl, "combo",
|
||||
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
|
||||
m_cActivep->sensesStorep(m_cActivep->sensesp());
|
||||
addActive(m_cActivep);
|
||||
}
|
||||
return m_cActivep;
|
||||
}
|
||||
AstActive* getIActive(FileLine* fl) {
|
||||
if (!m_iActivep) {
|
||||
m_iActivep = new AstActive(fl, "initial",
|
||||
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Initial())));
|
||||
m_iActivep->sensesStorep(m_iActivep->sensesp());
|
||||
addActive(m_iActivep);
|
||||
}
|
||||
return m_iActivep;
|
||||
if (!m_iActivep) {
|
||||
m_iActivep = new AstActive(
|
||||
fl, "initial",
|
||||
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
|
||||
m_iActivep->sensesStorep(m_iActivep->sensesp());
|
||||
addActive(m_iActivep);
|
||||
}
|
||||
return m_iActivep;
|
||||
}
|
||||
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
||||
// Return a sentree in this scope that matches given sense list.
|
||||
// Return a sentree in this scope that matches given sense list.
|
||||
|
||||
AstActive* activep = NULL;
|
||||
AstActive* activep = NULL;
|
||||
AstSenTree* activeSenp = m_activeSens.find(sensesp);
|
||||
if (activeSenp) {
|
||||
ActiveMap::iterator it = m_activeMap.find(activeSenp);
|
||||
@@ -123,26 +125,26 @@ public:
|
||||
activep = it->second;
|
||||
}
|
||||
|
||||
// Not found, form a new one
|
||||
if (!activep) {
|
||||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||
activep = new AstActive(fl, "sequent", newsenp);
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
UINFO(8," New ACTIVE "<<activep<<endl);
|
||||
// Form the sensitivity list
|
||||
addActive(activep);
|
||||
// Not found, form a new one
|
||||
if (!activep) {
|
||||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||
activep = new AstActive(fl, "sequent", newsenp);
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
UINFO(8," New ACTIVE "<<activep<<endl);
|
||||
// Form the sensitivity list
|
||||
addActive(activep);
|
||||
m_activeMap[newsenp] = activep;
|
||||
m_activeSens.add(newsenp);
|
||||
// Note actives may have also been added above in the Active visitor
|
||||
}
|
||||
return activep;
|
||||
// Note actives may have also been added above in the Active visitor
|
||||
}
|
||||
return activep;
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
ActiveNamer() {
|
||||
m_scopep = NULL;
|
||||
m_iActivep = NULL;
|
||||
m_cActivep = NULL;
|
||||
m_scopep = NULL;
|
||||
m_iActivep = NULL;
|
||||
m_cActivep = NULL;
|
||||
}
|
||||
virtual ~ActiveNamer() {}
|
||||
void main(AstScope* nodep) {
|
||||
@@ -157,55 +159,55 @@ class ActiveDlyVisitor : public ActiveBaseVisitor {
|
||||
public:
|
||||
enum CheckType { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
|
||||
private:
|
||||
CheckType m_check; // Combo logic or other
|
||||
AstNode* m_alwaysp; // Always we're under
|
||||
AstNode* m_assignp; // In assign
|
||||
CheckType m_check; // Combo logic or other
|
||||
AstNode* m_alwaysp; // Always we're under
|
||||
AstNode* m_assignp; // In assign
|
||||
// VISITORS
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
if (m_check != CT_SEQ) {
|
||||
// Convert to a non-delayed assignment
|
||||
UINFO(5," ASSIGNDLY "<<nodep<<endl);
|
||||
if (m_check == CT_INITIAL) {
|
||||
if (m_check != CT_SEQ) {
|
||||
// Convert to a non-delayed assignment
|
||||
UINFO(5," ASSIGNDLY "<<nodep<<endl);
|
||||
if (m_check == CT_INITIAL) {
|
||||
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial"
|
||||
" or final block; suggest blocking assignments (=).");
|
||||
} else if (m_check == CT_LATCH) {
|
||||
// Suppress. Shouldn't matter that the interior of the latch races
|
||||
} else {
|
||||
} else if (m_check == CT_LATCH) {
|
||||
// Suppress. Shouldn't matter that the interior of the latch races
|
||||
} else {
|
||||
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
||||
" (non flop or latch) block; suggest blocking assignments (=).");
|
||||
}
|
||||
}
|
||||
AstNode* newp = new AstAssign(nodep->fileline(),
|
||||
nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
nodep->replaceWith(newp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssign* nodep) {
|
||||
if (m_check == CT_SEQ) {
|
||||
AstNode* las = m_assignp;
|
||||
m_assignp = nodep;
|
||||
if (m_check == CT_SEQ) {
|
||||
AstNode* las = m_assignp;
|
||||
m_assignp = nodep;
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_assignp = las;
|
||||
}
|
||||
m_assignp = las;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
AstVar* varp=nodep->varp();
|
||||
if (m_check == CT_SEQ
|
||||
&& m_assignp
|
||||
&& !varp->isUsedLoopIdx() // Ignore loop indicies
|
||||
&& !varp->isTemp()
|
||||
) {
|
||||
// Allow turning off warnings on the always, or the variable also
|
||||
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
) {
|
||||
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
||||
nodep->v3warn(BLKSEQ,"Blocking assignments (=) in sequential (flop or latch) block; suggest delayed assignments (<=).");
|
||||
}
|
||||
}
|
||||
AstVar* varp = nodep->varp();
|
||||
if (m_check == CT_SEQ
|
||||
&& m_assignp
|
||||
&& !varp->isUsedLoopIdx() // Ignore loop indicies
|
||||
&& !varp->isTemp()
|
||||
) {
|
||||
// Allow turning off warnings on the always, or the variable also
|
||||
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
) {
|
||||
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
||||
nodep->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block; suggest delayed assignments (<=).");
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
@@ -214,9 +216,9 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
ActiveDlyVisitor(AstNode* nodep, CheckType check) {
|
||||
m_alwaysp = nodep;
|
||||
m_check = check;
|
||||
m_assignp = NULL;
|
||||
m_alwaysp = nodep;
|
||||
m_check = check;
|
||||
m_assignp = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveDlyVisitor() {}
|
||||
@@ -229,183 +231,188 @@ class ActiveVisitor : public ActiveBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Each call to V3Const::constify
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
|
||||
// STATE
|
||||
ActiveNamer m_namer; // Tracking of active names
|
||||
AstCFunc* m_scopeFinalp; // Final function for this scope
|
||||
bool m_itemCombo; // Found a SenItem combo
|
||||
bool m_itemSequent; // Found a SenItem sequential
|
||||
ActiveNamer m_namer; // Tracking of active names
|
||||
AstCFunc* m_scopeFinalp; // Final function for this scope
|
||||
bool m_itemCombo; // Found a SenItem combo
|
||||
bool m_itemSequent; // Found a SenItem sequential
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) {
|
||||
// Create required actives and add to scope
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
// Clear last scope's names, and collect this scope's existing names
|
||||
m_namer.main(nodep);
|
||||
m_scopeFinalp = NULL;
|
||||
// Create required actives and add to scope
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
// Clear last scope's names, and collect this scope's existing names
|
||||
m_namer.main(nodep);
|
||||
m_scopeFinalp = NULL;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4," INITIAL "<<nodep<<endl);
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4," INITIAL "<<nodep<<endl);
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," COVERTOGGLE "<<nodep<<endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," COVERTOGGLE "<<nodep<<endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) {
|
||||
// Relink to CFUNC for the final
|
||||
UINFO(4," FINAL "<<nodep<<endl);
|
||||
if (!nodep->bodysp()) { // Empty, Kill it.
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_scopeFinalp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
m_scopeFinalp->formCallTree(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
m_namer.scopep()->addActivep(m_scopeFinalp);
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName()));
|
||||
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
// Relink to CFUNC for the final
|
||||
UINFO(4," FINAL "<<nodep<<endl);
|
||||
if (!nodep->bodysp()) { // Empty, Kill it.
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(
|
||||
nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(),
|
||||
m_namer.scopep());
|
||||
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_scopeFinalp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
m_scopeFinalp->formCallTree(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
m_namer.scopep()->addActivep(m_scopeFinalp);
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName()));
|
||||
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp
|
||||
&& oldsensesp->sensesp()
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp
|
||||
&& oldsensesp->sensesp()
|
||||
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
// Never executing. Kill it.
|
||||
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read sensitivitues
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
// Read sensitivitues
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
iterateAndNextNull(oldsensesp);
|
||||
bool combo = m_itemCombo;
|
||||
bool sequent = m_itemSequent;
|
||||
bool combo = m_itemCombo;
|
||||
bool sequent = m_itemSequent;
|
||||
|
||||
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
|
||||
if (combo && sequent) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
|
||||
}
|
||||
sequent = false;
|
||||
}
|
||||
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
|
||||
if (combo && sequent) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
|
||||
}
|
||||
sequent = false;
|
||||
}
|
||||
|
||||
AstActive* wantactivep = NULL;
|
||||
if (combo && !sequent) {
|
||||
// Combo: Relink to ACTIVE(combo)
|
||||
wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
} else {
|
||||
// Sequential: Build a ACTIVE(name)
|
||||
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
|
||||
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
||||
// Summarize a long list of combo inputs as just "combo"
|
||||
#ifndef __COVERITY__ // Else dead code on next line.
|
||||
if (combo) oldsensesp->addSensesp
|
||||
(new AstSenItem(nodep->fileline(),AstSenItem::Combo()));
|
||||
AstActive* wantactivep = NULL;
|
||||
if (combo && !sequent) {
|
||||
// Combo: Relink to ACTIVE(combo)
|
||||
wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
} else {
|
||||
// Sequential: Build a ACTIVE(name)
|
||||
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
|
||||
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
||||
// Summarize a long list of combo inputs as just "combo"
|
||||
#ifndef __COVERITY__ // Else dead code on next line.
|
||||
if (combo) oldsensesp->addSensesp
|
||||
(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
||||
#endif
|
||||
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
|
||||
}
|
||||
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
|
||||
}
|
||||
|
||||
// Delete sensitivity list
|
||||
if (oldsensesp) {
|
||||
oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp);
|
||||
}
|
||||
// Delete sensitivity list
|
||||
if (oldsensesp) {
|
||||
oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp);
|
||||
}
|
||||
|
||||
// Move node to new active
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
// Move node to new active
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
|
||||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
|
||||
} else {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
|
||||
}
|
||||
}
|
||||
else if (!combo && sequent) {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
|
||||
}
|
||||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
|
||||
} else {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
|
||||
}
|
||||
}
|
||||
else if (!combo && sequent) {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4," ALW "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4," ALW "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout, " Alw: ");
|
||||
|
||||
if (!nodep->bodysp()) {
|
||||
// Empty always. Kill it.
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
|
||||
if (!nodep->bodysp()) {
|
||||
// Empty always. Kill it.
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4," ALWPub "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
|
||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4," ALWPub "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout, " Alw: ");
|
||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||
}
|
||||
virtual void visit(AstSenGate* nodep) {
|
||||
AstSenItem* subitemp = nodep->sensesp();
|
||||
if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_POSEDGE
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
|
||||
nodep->v3fatalSrc("Strange activity type under SenGate");
|
||||
}
|
||||
AstSenItem* subitemp = nodep->sensesp();
|
||||
if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_POSEDGE
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
|
||||
nodep->v3fatalSrc("Strange activity type under SenGate");
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
|
||||
m_itemCombo = true;
|
||||
// Delete the sensitivity
|
||||
// We'll add it as a generic COMBO SenItem in a moment.
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->varrefp()) {
|
||||
// V3LinkResolve should have cleaned most of these up
|
||||
if (!nodep->varrefp()->width1()) nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
||||
<<nodep->varrefp()->prettyName());
|
||||
m_itemSequent = true;
|
||||
nodep->varrefp()->varp()->usedClock(true);
|
||||
}
|
||||
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
|
||||
m_itemCombo = true;
|
||||
// Delete the sensitivity
|
||||
// We'll add it as a generic COMBO SenItem in a moment.
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->varrefp()) {
|
||||
// V3LinkResolve should have cleaned most of these up
|
||||
if (!nodep->varrefp()->width1()) {
|
||||
nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
||||
<<nodep->varrefp()->prettyName());
|
||||
}
|
||||
m_itemSequent = true;
|
||||
nodep->varrefp()->varp()->usedClock(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty visitors, speed things up
|
||||
@@ -418,9 +425,9 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit ActiveVisitor(AstNetlist* nodep) {
|
||||
m_scopeFinalp = NULL;
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
m_scopeFinalp = NULL;
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveVisitor() {}
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void activeAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+62
-58
@@ -21,9 +21,9 @@
|
||||
//
|
||||
// Note this can be called multiple times.
|
||||
// Across all ACTIVES
|
||||
// SenTrees are now under each ACTIVE statement, we want them global:
|
||||
// Find SenTree in under global TopScope, or create it there
|
||||
// Move SenTree the global SenTree
|
||||
// SenTrees are now under each ACTIVE statement, we want them global:
|
||||
// Find SenTree in under global TopScope, or create it there
|
||||
// Move SenTree the global SenTree
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -43,91 +43,95 @@ class ActiveTopVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist
|
||||
// AstNode::user() bool. True if processed
|
||||
// AstNode::user() bool. True if processed
|
||||
// Each call to V3Const::constify
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstTopScope* m_topscopep; // Top scope for adding sentrees under
|
||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
AstTopScope* m_topscopep; // Top scope for adding sentrees under
|
||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
m_topscopep = nodep;
|
||||
m_finder.main(m_topscopep);
|
||||
m_topscopep = nodep;
|
||||
m_finder.main(m_topscopep);
|
||||
iterateChildren(nodep);
|
||||
m_topscopep = NULL;
|
||||
m_topscopep = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
if (!sensesp) nodep->v3fatalSrc("NULL");
|
||||
if (sensesp->sensesp()
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
if (!sensesp) nodep->v3fatalSrc("NULL");
|
||||
if (sensesp->sensesp()
|
||||
&& VN_IS(sensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
// Copy combo tree to settlement tree with duplicated statements
|
||||
if (sensesp->hasCombo()) {
|
||||
AstSenTree* newsentreep
|
||||
// Never executing. Kill it.
|
||||
if (sensesp->sensesp()->nextp()) {
|
||||
nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
// Copy combo tree to settlement tree with duplicated statements
|
||||
if (sensesp->hasCombo()) {
|
||||
AstSenTree* newsentreep
|
||||
= new AstSenTree(nodep->fileline(),
|
||||
new AstSenItem (nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
|
||||
newp->sensesStorep(newsentreep);
|
||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||
nodep->addNextHere(newp);
|
||||
}
|
||||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
|
||||
UINFO(4," lookdone\n");
|
||||
if (wantp != sensesp) {
|
||||
// Move the active's contents to the other active
|
||||
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
|
||||
if (nodep->sensesStorep()) {
|
||||
if (sensesp != nodep->sensesStorep()) nodep->v3fatalSrc("sensesStore should have been deleted earlier if different");
|
||||
sensesp->unlinkFrBack();
|
||||
// There may be other references to same sense tree,
|
||||
// we'll be removing all references when we get to them,
|
||||
// but don't dangle our pointer yet!
|
||||
pushDeletep(sensesp);
|
||||
}
|
||||
nodep->sensesp(wantp);
|
||||
}
|
||||
// No need to do statements under it, they're already moved.
|
||||
new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
|
||||
newp->sensesStorep(newsentreep);
|
||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||
nodep->addNextHere(newp);
|
||||
}
|
||||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
|
||||
UINFO(4," lookdone\n");
|
||||
if (wantp != sensesp) {
|
||||
// Move the active's contents to the other active
|
||||
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
|
||||
if (nodep->sensesStorep()) {
|
||||
if (sensesp != nodep->sensesStorep()) {
|
||||
nodep->v3fatalSrc("sensesStore should have been deleted earlier if different");
|
||||
}
|
||||
sensesp->unlinkFrBack();
|
||||
// There may be other references to same sense tree,
|
||||
// we'll be removing all references when we get to them,
|
||||
// but don't dangle our pointer yet!
|
||||
pushDeletep(sensesp);
|
||||
}
|
||||
nodep->sensesp(wantp);
|
||||
}
|
||||
// No need to do statements under it, they're already moved.
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been deleted");
|
||||
nodep->v3fatalSrc("Node should have been deleted");
|
||||
}
|
||||
// Empty visitors, speed things up
|
||||
virtual void visit(AstNodeMath* nodep) {}
|
||||
@@ -139,7 +143,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit ActiveTopVisitor(AstNetlist* nodep) {
|
||||
m_topscopep = NULL;
|
||||
m_topscopep = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveTopVisitor() {}
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void activeTopAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+205
-200
@@ -37,46 +37,46 @@ class AssertVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE/TYPES
|
||||
// Cleared on netlist
|
||||
// AstNode::user() -> bool. True if processed
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstNode::user() -> bool. True if processed
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Last module
|
||||
AstBegin* m_beginp; // Last begin
|
||||
AstNodeModule* m_modp; // Last module
|
||||
AstBegin* m_beginp; // Last begin
|
||||
unsigned m_modPastNum; // Module past numbering
|
||||
V3Double0 m_statAsCover; // Statistic tracking
|
||||
V3Double0 m_statAsPsl; // Statistic tracking
|
||||
V3Double0 m_statAsFull; // Statistic tracking
|
||||
V3Double0 m_statAsSV; // Statistic tracking
|
||||
V3Double0 m_statAsCover; // Statistic tracking
|
||||
V3Double0 m_statAsPsl; // Statistic tracking
|
||||
V3Double0 m_statAsFull; // Statistic tracking
|
||||
V3Double0 m_statAsSV; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
|
||||
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
|
||||
+":"+cvtToStr(nodep->fileline()->lineno())
|
||||
+": Assertion failed in %m"
|
||||
+((message != "")?": ":"")+message
|
||||
+"\n");
|
||||
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
|
||||
+":"+cvtToStr(nodep->fileline()->lineno())
|
||||
+": Assertion failed in %m"
|
||||
+((message != "")?": ":"")+message
|
||||
+"\n");
|
||||
}
|
||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||
nodep->displayType(AstDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
// cppcheck-suppress nullPointer
|
||||
AstNode* timenewp = new AstTime(nodep->fileline());
|
||||
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
}
|
||||
nodep->fmtp()->addExprsp(timenewp);
|
||||
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
nodep->displayType(AstDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
// cppcheck-suppress nullPointer
|
||||
AstNode* timenewp = new AstTime(nodep->fileline());
|
||||
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
}
|
||||
nodep->fmtp()->addExprsp(timenewp);
|
||||
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
}
|
||||
|
||||
AstNode* newIfAssertOn(AstNode* nodep) {
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstNode* newp
|
||||
AstNode* newp
|
||||
= new AstIf(fl,
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
@@ -84,46 +84,47 @@ private:
|
||||
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
|
||||
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
|
||||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||
AstNode* bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(),
|
||||
AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||
AstNode* bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop(nodep->fileline()));
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
AstNode* newFireAssert(AstNode* nodep, const string& message) {
|
||||
AstNode* bodysp = newFireAssertUnchecked(nodep, message);
|
||||
bodysp = newIfAssertOn(bodysp);
|
||||
return bodysp;
|
||||
bodysp = newIfAssertOn(bodysp);
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
void newPslAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep,
|
||||
AstNode* stmtsp, const string& message) {
|
||||
propp->unlinkFrBack();
|
||||
sentreep->unlinkFrBack();
|
||||
if (stmtsp) stmtsp->unlinkFrBack();
|
||||
//
|
||||
AstNode* bodysp = NULL;
|
||||
bool selfDestruct = false;
|
||||
AstNode* stmtsp, const string& message) {
|
||||
propp->unlinkFrBack();
|
||||
sentreep->unlinkFrBack();
|
||||
if (stmtsp) stmtsp->unlinkFrBack();
|
||||
//
|
||||
AstNode* bodysp = NULL;
|
||||
bool selfDestruct = false;
|
||||
AstIf* ifp = NULL;
|
||||
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
|
||||
++m_statAsCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBack();
|
||||
if (message!="") covincp->declp()->comment(message);
|
||||
bodysp = covincp;
|
||||
}
|
||||
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBack();
|
||||
if (message!="") covincp->declp()->comment(message);
|
||||
bodysp = covincp;
|
||||
}
|
||||
|
||||
if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp);
|
||||
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
|
||||
@@ -148,163 +149,167 @@ private:
|
||||
// IEEE says simulator ignores these
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
|
||||
AstNode* newp = new AstAlways(nodep->fileline(),
|
||||
VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
||||
// Install it
|
||||
if (selfDestruct) {
|
||||
// Delete it after making the tree. This way we can tell the user
|
||||
// if it wasn't constructed nicely or has other errors without needing --coverage.
|
||||
newp->deleteTree();
|
||||
nodep->unlinkFrBack();
|
||||
} else {
|
||||
nodep->replaceWith(newp);
|
||||
}
|
||||
// Bye
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
// Install it
|
||||
if (selfDestruct) {
|
||||
// Delete it after making the tree. This way we can tell the user
|
||||
// if it wasn't constructed nicely or has other errors without needing --coverage.
|
||||
newp->deleteTree();
|
||||
nodep->unlinkFrBack();
|
||||
} else {
|
||||
nodep->replaceWith(newp);
|
||||
}
|
||||
// Bye
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
|
||||
void newVAssertion(AstVAssert* nodep, AstNode* propp) {
|
||||
propp->unlinkFrBackWithNext();
|
||||
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
|
||||
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
|
||||
//
|
||||
propp->unlinkFrBackWithNext();
|
||||
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
|
||||
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
|
||||
//
|
||||
if (VN_IS(nodep, VAssert)) {
|
||||
if (passsp) passsp = newIfAssertOn(passsp);
|
||||
if (failsp) failsp = newIfAssertOn(failsp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
if (passsp) passsp = newIfAssertOn(passsp);
|
||||
if (failsp) failsp = newIfAssertOn(failsp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
|
||||
AstIf* ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
|
||||
AstNode* newp = ifp;
|
||||
AstNode* newp = ifp;
|
||||
if (VN_IS(nodep, VAssert)) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
//
|
||||
// Install it
|
||||
nodep->replaceWith(newp);
|
||||
// Bye
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
//
|
||||
// Install it
|
||||
nodep->replaceWith(newp);
|
||||
// Bye
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstIf* nodep) {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
AstNodeIf* ifp = nodep;
|
||||
AstNode* propp = NULL;
|
||||
bool hasDefaultElse = false;
|
||||
do {
|
||||
// If this statement ends with 'else if', then nextIf will point to the
|
||||
// nextIf statement. Otherwise it will be null.
|
||||
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
AstNodeIf* ifp = nodep;
|
||||
AstNode* propp = NULL;
|
||||
bool hasDefaultElse = false;
|
||||
do {
|
||||
// If this statement ends with 'else if', then nextIf will point to the
|
||||
// nextIf statement. Otherwise it will be null.
|
||||
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||
iterateAndNextNull(ifp->condp());
|
||||
|
||||
// Recurse into the true case.
|
||||
// Recurse into the true case.
|
||||
iterateAndNextNull(ifp->ifsp());
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
iterateAndNextNull(ifp->elsesp());
|
||||
}
|
||||
}
|
||||
|
||||
// Build a bitmask of the true predicates
|
||||
AstNode* predp = ifp->condp()->cloneTree(false);
|
||||
if (propp) {
|
||||
propp = new AstConcat(nodep->fileline(), predp, propp);
|
||||
} else {
|
||||
propp = predp;
|
||||
}
|
||||
// Build a bitmask of the true predicates
|
||||
AstNode* predp = ifp->condp()->cloneTree(false);
|
||||
if (propp) {
|
||||
propp = new AstConcat(nodep->fileline(), predp, propp);
|
||||
} else {
|
||||
propp = predp;
|
||||
}
|
||||
|
||||
// Record if this ends with an 'else' that does not have an if
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
hasDefaultElse = true;
|
||||
}
|
||||
// Record if this ends with an 'else' that does not have an if
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
hasDefaultElse = true;
|
||||
}
|
||||
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode *newifp = nodep->cloneTree(false);
|
||||
bool allow_none = nodep->unique0Pragma();
|
||||
AstNode *newifp = nodep->cloneTree(false);
|
||||
bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||
|
||||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
|
||||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* checkifp = new AstIf(nodep->fileline(),
|
||||
new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"),
|
||||
newifp);
|
||||
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
} else {
|
||||
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//========== Case assertions
|
||||
virtual void visit(AstCase* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default=false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) has_default=true;
|
||||
}
|
||||
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||
// Simply need to add a default if there isn't one already
|
||||
++m_statAsFull;
|
||||
if (!has_default) {
|
||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
|
||||
newFireAssert(nodep, "synthesis full_case, but non-match found")));
|
||||
}
|
||||
}
|
||||
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
// Need to check that one, and only one of the case items match at any moment
|
||||
// If there's a default, we allow none to match, else exactly one must match
|
||||
++m_statAsFull;
|
||||
if (!has_default && !nodep->itemsp()) {
|
||||
// Not parallel, but harmlessly so.
|
||||
} else {
|
||||
AstNode* propp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
AstNode* onep;
|
||||
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
||||
onep = AstEqWild::newTyped(itemp->fileline(),
|
||||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
} else {
|
||||
onep = AstEq::newTyped(icondp->fileline(),
|
||||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
}
|
||||
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
|
||||
else propp = onep;
|
||||
}
|
||||
}
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) has_default = true;
|
||||
}
|
||||
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||
// Simply need to add a default if there isn't one already
|
||||
++m_statAsFull;
|
||||
if (!has_default) {
|
||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
|
||||
newFireAssert(nodep, "synthesis full_case, but non-match found")));
|
||||
}
|
||||
}
|
||||
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
// Need to check that one, and only one of the case items match at any moment
|
||||
// If there's a default, we allow none to match, else exactly one must match
|
||||
++m_statAsFull;
|
||||
if (!has_default && !nodep->itemsp()) {
|
||||
// Not parallel, but harmlessly so.
|
||||
} else {
|
||||
AstNode* propp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp();
|
||||
icondp!=NULL; icondp=icondp->nextp()) {
|
||||
AstNode* onep;
|
||||
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
||||
onep = AstEqWild::newTyped(itemp->fileline(),
|
||||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
} else {
|
||||
onep = AstEq::newTyped(icondp->fileline(),
|
||||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
}
|
||||
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
|
||||
else propp = onep;
|
||||
}
|
||||
}
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||
|
||||
bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* ohot = (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
|
||||
bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* ohot
|
||||
= (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* ifp = new AstIf(nodep->fileline(),
|
||||
new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
|
||||
NULL);
|
||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->addNotParallelp(ifp);
|
||||
}
|
||||
}
|
||||
}
|
||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->addNotParallelp(ifp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//========== Past
|
||||
@@ -341,46 +346,46 @@ private:
|
||||
//========== Statements
|
||||
virtual void visit(AstDisplay* nodep) {
|
||||
iterateChildren(nodep);
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
||||
replaceDisplay(nodep, "-Info");
|
||||
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
|
||||
replaceDisplay(nodep, "%%Warning");
|
||||
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|
||||
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
|
||||
replaceDisplay(nodep, "%%Error");
|
||||
}
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
||||
replaceDisplay(nodep, "-Info");
|
||||
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
|
||||
replaceDisplay(nodep, "%%Warning");
|
||||
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|
||||
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
|
||||
replaceDisplay(nodep, "%%Error");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
|
||||
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
|
||||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
|
||||
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstVAssert* nodep) {
|
||||
iterateChildren(nodep);
|
||||
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
|
||||
++m_statAsSV;
|
||||
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
|
||||
++m_statAsSV;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_modp = nodep;
|
||||
m_modPastNum = 0;
|
||||
//
|
||||
//
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_modp = NULL;
|
||||
// Reset defaults
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
AstBegin* lastp = m_beginp;
|
||||
{
|
||||
m_beginp = nodep;
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
AstBegin* lastp = m_beginp;
|
||||
{
|
||||
m_beginp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_beginp = lastp;
|
||||
}
|
||||
m_beginp = lastp;
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
@@ -389,17 +394,17 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertVisitor(AstNetlist* nodep) {
|
||||
m_beginp = NULL;
|
||||
m_modp = NULL;
|
||||
m_beginp = NULL;
|
||||
m_modp = NULL;
|
||||
m_modPastNum = 0;
|
||||
// Process
|
||||
// Process
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertVisitor() {
|
||||
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
|
||||
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
|
||||
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
|
||||
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
|
||||
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
|
||||
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
|
||||
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
|
||||
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void assertAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+46
-46
@@ -18,7 +18,7 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// Pre steps:
|
||||
// Attach clocks to each assertion
|
||||
// Attach clocks to each assertion
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
@@ -51,37 +51,37 @@ private:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep) {
|
||||
// Create sentree based on clocked or default clock
|
||||
// Return NULL for always
|
||||
AstSenTree* newp = NULL;
|
||||
// Create sentree based on clocked or default clock
|
||||
// Return NULL for always
|
||||
AstSenTree* newp = NULL;
|
||||
AstNodeSenItem* senip = m_senip;
|
||||
if (!senip) senip = m_seniDefaultp;
|
||||
if (!senip) senip = m_seniAlwaysp;
|
||||
if (!senip) {
|
||||
nodep->v3error("Unsupported: Unclocked assertion");
|
||||
newp = new AstSenTree(nodep->fileline(), NULL);
|
||||
} else {
|
||||
newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
|
||||
}
|
||||
return newp;
|
||||
if (!senip) {
|
||||
nodep->v3error("Unsupported: Unclocked assertion");
|
||||
newp = new AstSenTree(nodep->fileline(), NULL);
|
||||
} else {
|
||||
newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
void clearAssertInfo() {
|
||||
m_senip = NULL;
|
||||
m_senip = NULL;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
//========== Statements
|
||||
virtual void visit(AstClocking* nodep) {
|
||||
UINFO(8," CLOCKING"<<nodep<<endl);
|
||||
// Store the new default clock, reset on new module
|
||||
m_seniDefaultp = nodep->sensesp();
|
||||
// Trash it, keeping children
|
||||
if (nodep->bodysp()) {
|
||||
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
UINFO(8," CLOCKING"<<nodep<<endl);
|
||||
// Store the new default clock, reset on new module
|
||||
m_seniDefaultp = nodep->sensesp();
|
||||
// Trash it, keeping children
|
||||
if (nodep->bodysp()) {
|
||||
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
@@ -93,12 +93,12 @@ private:
|
||||
}
|
||||
|
||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
clearAssertInfo();
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
clearAssertInfo();
|
||||
// Find PslClocking's burried under nodep->exprsp
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
}
|
||||
virtual void visit(AstPast* nodep) {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
@@ -108,26 +108,26 @@ private:
|
||||
virtual void visit(AstPslClocked* nodep) {
|
||||
// No need to iterate the body, once replace will get iterated
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip) {
|
||||
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||
}
|
||||
// Block is the new expression to evaluate
|
||||
AstNode* blockp = nodep->propp()->unlinkFrBack();
|
||||
if (nodep->disablep()) {
|
||||
blockp = new AstAnd(nodep->disablep()->fileline(),
|
||||
new AstNot(nodep->disablep()->fileline(),
|
||||
nodep->disablep()->unlinkFrBack()),
|
||||
blockp);
|
||||
}
|
||||
// Unlink and just keep a pointer to it, convert to sentree as needed
|
||||
m_senip = nodep->sensesp();
|
||||
nodep->replaceWith(blockp);
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
if (m_senip) {
|
||||
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||
}
|
||||
// Block is the new expression to evaluate
|
||||
AstNode* blockp = nodep->propp()->unlinkFrBack();
|
||||
if (nodep->disablep()) {
|
||||
blockp = new AstAnd(nodep->disablep()->fileline(),
|
||||
new AstNot(nodep->disablep()->fileline(),
|
||||
nodep->disablep()->unlinkFrBack()),
|
||||
blockp);
|
||||
}
|
||||
// Unlink and just keep a pointer to it, convert to sentree as needed
|
||||
m_senip = nodep->sensesp();
|
||||
nodep->replaceWith(blockp);
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_seniDefaultp = NULL;
|
||||
// Reset defaults
|
||||
m_seniDefaultp = NULL;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
iterateChildren(nodep);
|
||||
@@ -136,10 +136,10 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPreVisitor(AstNetlist* nodep) {
|
||||
m_seniDefaultp = NULL;
|
||||
m_seniDefaultp = NULL;
|
||||
m_seniAlwaysp = NULL;
|
||||
clearAssertInfo();
|
||||
// Process
|
||||
clearAssertInfo();
|
||||
// Process
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertPreVisitor() {}
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void assertPreAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+379
-371
File diff suppressed because it is too large
Load Diff
+943
-926
File diff suppressed because it is too large
Load Diff
@@ -30,4 +30,4 @@
|
||||
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
|
||||
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+227
-217
@@ -57,7 +57,7 @@ void AstNodeVarRef::cloneRelink() {
|
||||
}
|
||||
|
||||
int AstNodeSel::bitConst() const {
|
||||
AstConst* constp=VN_CAST(bitp(), Const);
|
||||
AstConst* constp = VN_CAST(bitp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
@@ -66,24 +66,29 @@ void AstNodeClassDType::repairMemberCache() {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name())!=m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
|
||||
else m_members.insert(make_pair(itemp->name(), itemp));
|
||||
else m_members.insert(make_pair(itemp->name(), itemp));
|
||||
}
|
||||
}
|
||||
|
||||
const char* AstNodeClassDType::broken() const {
|
||||
vl_unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
|
||||
if (VL_UNLIKELY(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: "<<it->first);
|
||||
return "member broken";
|
||||
}
|
||||
if (VL_UNLIKELY(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: "<<it->first);
|
||||
return "member broken";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs,
|
||||
const V3Number& rhs, const V3Number& ths) {
|
||||
if (lhs.isNeqZero()) out.opAssign(rhs); else out.opAssign(ths);
|
||||
}
|
||||
|
||||
int AstBasicDType::widthAlignBytes() const {
|
||||
if (width()<=8) return 1;
|
||||
else if (width()<=16) return 2;
|
||||
@@ -116,37 +121,37 @@ int AstNodeClassDType::widthAlignBytes() const {
|
||||
|
||||
AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstEqD(fl, lhsp, rhsp);
|
||||
return new AstEqD(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstEq(fl, lhsp, rhsp);
|
||||
return new AstEq(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstGteD(fl, lhsp, rhsp);
|
||||
return new AstGteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstGteS(fl, lhsp, rhsp);
|
||||
return new AstGteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstGte(fl, lhsp, rhsp);
|
||||
return new AstGte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstLteD(fl, lhsp, rhsp);
|
||||
return new AstLteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstLteS(fl, lhsp, rhsp);
|
||||
return new AstLteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstLte(fl, lhsp, rhsp);
|
||||
return new AstLte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstEqD(fl, lhsp, rhsp);
|
||||
return new AstEqD(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstEqWild(fl, lhsp, rhsp);
|
||||
return new AstEqWild(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +173,12 @@ bool AstVar::isScBv() const {
|
||||
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
|
||||
}
|
||||
bool AstVar::isScUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
|
||||
return ((isSc() && v3Global.opt.pinsScUint()
|
||||
&& width() >= 2 && width() <= 64) && !isScBv());
|
||||
}
|
||||
bool AstVar::isScBigUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv());
|
||||
return ((isSc() && v3Global.opt.pinsScBigUint()
|
||||
&& width() >= 65 && width() <= 512) && !isScBv());
|
||||
}
|
||||
|
||||
void AstVar::combineType(AstVarType type) {
|
||||
@@ -208,7 +215,7 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
||||
if (forReturn) named=false;
|
||||
if (forReturn) named = false;
|
||||
if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument");
|
||||
string otype;
|
||||
AstBasicDType* bdtypep = basicp();
|
||||
@@ -279,21 +286,21 @@ string AstVar::vlEnumType() const {
|
||||
AstBasicDType* bdtypep = basicp();
|
||||
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
|
||||
if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
|
||||
return "VLVT_PTR";
|
||||
return "VLVT_PTR";
|
||||
} else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
|
||||
return "VLVT_PTR";
|
||||
return "VLVT_PTR";
|
||||
} else if (strtype) {
|
||||
arg += "VLVT_STRING";
|
||||
arg += "VLVT_STRING";
|
||||
} else if (widthMin() <= 8) {
|
||||
arg += "VLVT_UINT8";
|
||||
arg += "VLVT_UINT8";
|
||||
} else if (widthMin() <= 16) {
|
||||
arg += "VLVT_UINT16";
|
||||
arg += "VLVT_UINT16";
|
||||
} else if (widthMin() <= VL_WORDSIZE) {
|
||||
arg += "VLVT_UINT32";
|
||||
arg += "VLVT_UINT32";
|
||||
} else if (isQuad()) {
|
||||
arg += "VLVT_UINT64";
|
||||
arg += "VLVT_UINT64";
|
||||
} else if (isWide()) {
|
||||
arg += "VLVT_WDATA";
|
||||
arg += "VLVT_WDATA";
|
||||
}
|
||||
// else return "VLVT_UNKNOWN"
|
||||
return arg;
|
||||
@@ -339,28 +346,28 @@ string AstVar::vlPropInit() const {
|
||||
out += ", "+cvtToStr(adtypep->declRange().left())+", "+cvtToStr(adtypep->declRange().right());
|
||||
dtp = adtypep->subDTypep();
|
||||
}
|
||||
else break; // AstBasicDType - nothing below
|
||||
else break; // AstBasicDType - nothing below
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||
if (forReturn) named=false;
|
||||
if (forReturn) named = false;
|
||||
string arg;
|
||||
if (isWide() && isReadOnly()) arg += "const ";
|
||||
if (widthMin() == 1) {
|
||||
arg += "bool";
|
||||
arg += "bool";
|
||||
} else if (widthMin() <= VL_WORDSIZE) {
|
||||
arg += "uint32_t";
|
||||
arg += "uint32_t";
|
||||
} else if (isWide()) {
|
||||
arg += "uint32_t"; // []'s added later
|
||||
arg += "uint32_t"; // []'s added later
|
||||
} else {
|
||||
arg += "vluint64_t";
|
||||
arg += "vluint64_t";
|
||||
}
|
||||
if (isWide()) {
|
||||
if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead");
|
||||
arg += " (& "+name();
|
||||
arg += ")["+cvtToStr(widthWords())+"]";
|
||||
if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead");
|
||||
arg += " (& "+name();
|
||||
arg += ")["+cvtToStr(widthWords())+"]";
|
||||
} else {
|
||||
if (!forReturn && (isWritable()
|
||||
|| direction().isRefOrConstRef())) arg += "&";
|
||||
@@ -370,25 +377,25 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||
}
|
||||
|
||||
string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
if (forReturn) named=false;
|
||||
if (forReturn) named = false;
|
||||
string arg;
|
||||
if (isDpiOpenArray()) {
|
||||
arg = "const svOpenArrayHandle";
|
||||
} else if (!basicp()) {
|
||||
arg = "UNKNOWN";
|
||||
} else if (basicp()->keyword().isDpiBitVal()) {
|
||||
if (widthMin() == 1) {
|
||||
arg = "unsigned char";
|
||||
if (widthMin() == 1) {
|
||||
arg = "unsigned char";
|
||||
if (!forReturn && isWritable()) arg += "*";
|
||||
} else {
|
||||
if (forReturn) {
|
||||
arg = "svBitVecVal";
|
||||
} else if (isReadOnly()) {
|
||||
arg = "const svBitVecVal*";
|
||||
} else {
|
||||
arg = "svBitVecVal*";
|
||||
}
|
||||
}
|
||||
arg = "const svBitVecVal*";
|
||||
} else {
|
||||
arg = "svBitVecVal*";
|
||||
}
|
||||
}
|
||||
} else if (basicp()->keyword().isDpiLogicVal()) {
|
||||
if (widthMin() == 1) {
|
||||
arg = "unsigned char";
|
||||
@@ -403,9 +410,9 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
arg = basicp()->keyword().dpiType();
|
||||
if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) {
|
||||
arg = "unsigned "+arg;
|
||||
arg = basicp()->keyword().dpiType();
|
||||
if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) {
|
||||
arg = "unsigned "+arg;
|
||||
}
|
||||
if (!forReturn && isWritable()) arg += "*";
|
||||
}
|
||||
@@ -415,23 +422,23 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
|
||||
string AstVar::scType() const {
|
||||
if (isScBigUint()) {
|
||||
return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
} else if (isScUint()) {
|
||||
return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
} else if (isScBv()) {
|
||||
return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
} else if (widthMin() == 1) {
|
||||
return "bool";
|
||||
return "bool";
|
||||
} else if (widthMin() <= VL_WORDSIZE) {
|
||||
if (widthMin() <= 8 && v3Global.opt.pinsUint8()) {
|
||||
return "uint8_t";
|
||||
} else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) {
|
||||
return "uint16_t";
|
||||
} else {
|
||||
return "uint32_t";
|
||||
}
|
||||
if (widthMin() <= 8 && v3Global.opt.pinsUint8()) {
|
||||
return "uint8_t";
|
||||
} else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) {
|
||||
return "uint16_t";
|
||||
} else {
|
||||
return "uint32_t";
|
||||
}
|
||||
} else {
|
||||
return "vluint64_t";
|
||||
return "vluint64_t";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,18 +447,18 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
||||
// Historically sc variables are identified by a variable
|
||||
// attribute. TODO it would better be a data type attribute.
|
||||
if (AstVar* anodep = VN_CAST(nodep, Var)) {
|
||||
if (anodep->isSc()) return anodep;
|
||||
else return NULL;
|
||||
if (anodep->isSc()) return anodep;
|
||||
else return NULL;
|
||||
}
|
||||
else if (VN_IS(nodep, VarRef)) {
|
||||
if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp();
|
||||
else return NULL;
|
||||
else return NULL;
|
||||
}
|
||||
else if (VN_IS(nodep, ArraySel)) {
|
||||
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
|
||||
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
|
||||
if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
|
||||
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
|
||||
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
|
||||
if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -473,58 +480,58 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
||||
// ref order: a[1][2][3][4]
|
||||
// Created as: reg [4] a [1][2][3];
|
||||
// *or* reg a [1][2][3][4];
|
||||
// // The bit select is optional; used only if "leftover" []'s
|
||||
// SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a))))
|
||||
// DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3))))
|
||||
// *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT))))
|
||||
// SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0
|
||||
// // The bit select is optional; used only if "leftover" []'s
|
||||
// SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a))))
|
||||
// DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3))))
|
||||
// *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT))))
|
||||
// SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0
|
||||
// TODO this function should be removed in favor of recursing the dtype(),
|
||||
// as that allows for more complicated data types.
|
||||
int dim = 0;
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if ((dim++)==dimension) {
|
||||
return dtypep;
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
if ((dim++)==dimension) {
|
||||
return dtypep;
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
// AstBasicDType - nothing below, return null
|
||||
if (adtypep->isRanged()) {
|
||||
if ((dim++) == dimension) {
|
||||
return adtypep;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// AstBasicDType - nothing below, return null
|
||||
if (adtypep->isRanged()) {
|
||||
if ((dim++) == dimension) {
|
||||
return adtypep;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
|
||||
if (adtypep->packed()) {
|
||||
if ((dim++) == dimension) {
|
||||
return adtypep;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// Node no ->next in loop; use continue where necessary
|
||||
break;
|
||||
if (adtypep->packed()) {
|
||||
if ((dim++) == dimension) {
|
||||
return adtypep;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// Node no ->next in loop; use continue where necessary
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t AstNodeDType::arrayUnpackedElements() {
|
||||
uint32_t entries=1;
|
||||
uint32_t entries = 1;
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
entries *= adtypep->elementsConst();
|
||||
dtypep = adtypep->subDTypep();
|
||||
}
|
||||
else {
|
||||
// AstBasicDType - nothing below, 1
|
||||
break;
|
||||
}
|
||||
entries *= adtypep->elementsConst();
|
||||
dtypep = adtypep->subDTypep();
|
||||
}
|
||||
else {
|
||||
// AstBasicDType - nothing below, 1
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
@@ -534,17 +541,17 @@ std::pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
uint32_t packed = 0;
|
||||
uint32_t unpacked = 0;
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if (VN_IS(adtypep, PackArrayDType)) packed++;
|
||||
else unpacked++;
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else unpacked++;
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (includeBasic && adtypep->isRanged()) packed++;
|
||||
}
|
||||
break;
|
||||
if (includeBasic && adtypep->isRanged()) packed++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return make_pair(packed, unpacked);
|
||||
}
|
||||
@@ -553,27 +560,28 @@ int AstNodeDType::widthPow2() const {
|
||||
// I.e. width 30 returns 32, width 32 returns 32.
|
||||
uint32_t width = this->width();
|
||||
for (int p2=30; p2>=0; p2--) {
|
||||
if (width > (1UL<<p2)) return (1UL<<(p2+1));
|
||||
if (width > (1UL<<p2)) return (1UL<<(p2+1));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
|
||||
AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
|
||||
// Else AstArraySel etc; search for the base
|
||||
while (nodep) {
|
||||
if (VN_IS(nodep, ArraySel)) { nodep=VN_CAST(nodep, ArraySel)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, Sel)) { nodep=VN_CAST(nodep, Sel)->fromp(); continue; }
|
||||
// AstNodeSelPre stashes the associated variable under an ATTROF of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
|
||||
else if (VN_IS(nodep, AttrOf)) { nodep=VN_CAST(nodep, AttrOf)->fromp(); continue; }
|
||||
if (VN_IS(nodep, ArraySel)) { nodep = VN_CAST(nodep, ArraySel)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, Sel)) { nodep = VN_CAST(nodep, Sel)->fromp(); continue; }
|
||||
// AstNodeSelPre stashes the associated variable under an ATTROF
|
||||
// of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
|
||||
else if (VN_IS(nodep, AttrOf)) { nodep = VN_CAST(nodep, AttrOf)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, NodePreSel)) {
|
||||
if (VN_CAST(nodep, NodePreSel)->attrp()) {
|
||||
nodep=VN_CAST(nodep, NodePreSel)->attrp();
|
||||
} else {
|
||||
nodep=VN_CAST(nodep, NodePreSel)->lhsp();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else break;
|
||||
nodep = VN_CAST(nodep, NodePreSel)->attrp();
|
||||
} else {
|
||||
nodep = VN_CAST(nodep, NodePreSel)->lhsp();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
return nodep;
|
||||
}
|
||||
@@ -598,7 +606,7 @@ string AstScope::nameDotless() const {
|
||||
string out = shortName();
|
||||
string::size_type pos;
|
||||
while ((pos = out.find('.')) != string::npos) {
|
||||
out.replace(pos, 1, "__");
|
||||
out.replace(pos, 1, "__");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -606,29 +614,29 @@ string AstScope::nameDotless() const {
|
||||
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
out += textp->text();
|
||||
}
|
||||
// TOP will be replaced by top->name()
|
||||
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
|
||||
if (out.substr(0,7) == "__DOT__") out.replace(0,7,"");
|
||||
if (out.substr(0,1) == ".") out.replace(0,1,"");
|
||||
if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, "");
|
||||
if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, "");
|
||||
if (out.substr(0, 1) == ".") out.replace(0, 1, "");
|
||||
return AstNode::prettyName(out);
|
||||
}
|
||||
|
||||
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
out += textp->text();
|
||||
}
|
||||
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
|
||||
if (out.substr(0,7) == "__DOT__") out.replace(0,7,"");
|
||||
if (out.substr(0,1) == ".") out.replace(0,1,"");
|
||||
if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, "");
|
||||
if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, "");
|
||||
if (out.substr(0, 1) == ".") out.replace(0, 1, "");
|
||||
string::size_type pos;
|
||||
while ((pos = out.find('.')) != string::npos) {
|
||||
out.replace(pos, 1, "__");
|
||||
out.replace(pos, 1, "__");
|
||||
}
|
||||
while ((pos=out.find("__DOT__")) != string::npos) {
|
||||
out.replace(pos, 7, "__");
|
||||
while ((pos = out.find("__DOT__")) != string::npos) {
|
||||
out.replace(pos, 7, "__");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -636,28 +644,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
bool AstSenTree::hasClocked() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isClocked()) return true;
|
||||
if (senp->isClocked()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isSettle()) return true;
|
||||
if (senp->isSettle()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasInitial() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isInitial()) return true;
|
||||
if (senp->isInitial()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isCombo()) return true;
|
||||
if (senp->isCombo()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -666,20 +674,20 @@ void AstTypeTable::clearCache() {
|
||||
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
|
||||
// Just clear out the maps; the search functions will be used to rebuild the map
|
||||
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
m_basicps[i] = NULL;
|
||||
m_basicps[i] = NULL;
|
||||
}
|
||||
for (int isbit=0; isbit<_IDX0_MAX; ++isbit) {
|
||||
for (int numer=0; numer<AstNumeric::_ENUM_MAX; ++numer) {
|
||||
LogicMap& mapr = m_logicMap[isbit][numer];
|
||||
mapr.clear();
|
||||
}
|
||||
for (int numer=0; numer<AstNumeric::_ENUM_MAX; ++numer) {
|
||||
LogicMap& mapr = m_logicMap[isbit][numer];
|
||||
mapr.clear();
|
||||
}
|
||||
}
|
||||
m_detailedMap.clear();
|
||||
// Clear generic()'s so dead detection will work
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
bdtypep->generic(false);
|
||||
}
|
||||
bdtypep->generic(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,8 +696,8 @@ void AstTypeTable::repairCache() {
|
||||
clearCache();
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
(void)findInsertSameDType(bdtypep);
|
||||
}
|
||||
(void)findInsertSameDType(bdtypep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,12 +717,12 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd)
|
||||
}
|
||||
|
||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||
int width, int widthMin, AstNumeric numeric) {
|
||||
int width, int widthMin, AstNumeric numeric) {
|
||||
int idx = IDX0_LOGIC;
|
||||
if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC;
|
||||
else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT;
|
||||
else fl->v3fatalSrc("Bad kwd for findLogicBitDType");
|
||||
std::pair<int,int> widths = make_pair(width,widthMin);
|
||||
std::pair<int,int> widths = make_pair(width, widthMin);
|
||||
LogicMap& mapr = m_logicMap[idx][static_cast<int>(numeric)];
|
||||
LogicMap::const_iterator it = mapr.find(widths);
|
||||
if (it != mapr.end()) return it->second;
|
||||
@@ -727,12 +735,12 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
||||
if (newp != new1p) new1p->deleteTree();
|
||||
else addTypesp(newp);
|
||||
//
|
||||
mapr.insert(make_pair(widths,newp));
|
||||
mapr.insert(make_pair(widths, newp));
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||
VNumRange range, int widthMin, AstNumeric numeric) {
|
||||
VNumRange range, int widthMin, AstNumeric numeric) {
|
||||
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
|
||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||
if (newp != new1p) new1p->deleteTree();
|
||||
@@ -742,11 +750,11 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
||||
|
||||
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
||||
VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(),
|
||||
nodep->keyword(), nodep->nrange());
|
||||
nodep->keyword(), nodep->nrange());
|
||||
DetailedMap& mapr = m_detailedMap;
|
||||
DetailedMap::const_iterator it = mapr.find(key);
|
||||
if (it != mapr.end()) return it->second;
|
||||
mapr.insert(make_pair(key,nodep));
|
||||
mapr.insert(make_pair(key, nodep));
|
||||
nodep->generic(true);
|
||||
// No addTypesp; the upper function that called new() is responsible for adding
|
||||
return nodep;
|
||||
@@ -778,39 +786,41 @@ void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
|
||||
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
|
||||
// Special, as statements need to be put in different places
|
||||
// Belowp is how we came to recurse up to this point
|
||||
// Preconditions insert first just before themselves (the normal rule for other statement types)
|
||||
// Preconditions insert first just before themselves (the normal rule
|
||||
// for other statement types)
|
||||
if (belowp == precondsp()) {
|
||||
// Must have been first statement in precondsp list, so newp is new first statement
|
||||
belowp->addHereThisAsNext(newp);
|
||||
// Must have been first statement in precondsp list, so newp is new first statement
|
||||
belowp->addHereThisAsNext(newp);
|
||||
} else if (belowp == condp()) {
|
||||
// Goes before condition, IE in preconditions
|
||||
addPrecondsp(newp);
|
||||
// Goes before condition, IE in preconditions
|
||||
addPrecondsp(newp);
|
||||
} else if (belowp == bodysp()) {
|
||||
// Was first statement in body, so new front
|
||||
belowp->addHereThisAsNext(newp);
|
||||
// Was first statement in body, so new front
|
||||
belowp->addHereThisAsNext(newp);
|
||||
} else {
|
||||
belowp->v3fatalSrc("Doesn't look like this was really under the while");
|
||||
belowp->v3fatalSrc("Doesn't look like this was really under the while");
|
||||
}
|
||||
}
|
||||
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||
// Special, as statements need to be put in different places
|
||||
// Belowp is how we came to recurse up to this point
|
||||
// Preconditions insert first just before themselves (the normal rule for other statement types)
|
||||
// Preconditions insert first just before themselves (the normal rule
|
||||
// for other statement types)
|
||||
if (belowp == precondsp()) {
|
||||
// Next in precond list
|
||||
belowp->addNextHere(newp);
|
||||
// Next in precond list
|
||||
belowp->addNextHere(newp);
|
||||
} else if (belowp == condp()) {
|
||||
// Becomes first statement in body, body may have been empty
|
||||
if (bodysp()) {
|
||||
bodysp()->addHereThisAsNext(newp);
|
||||
} else {
|
||||
addBodysp(newp);
|
||||
}
|
||||
// Becomes first statement in body, body may have been empty
|
||||
if (bodysp()) {
|
||||
bodysp()->addHereThisAsNext(newp);
|
||||
} else {
|
||||
addBodysp(newp);
|
||||
}
|
||||
} else if (belowp == bodysp()) {
|
||||
// Next statement in body
|
||||
belowp->addNextHere(newp);
|
||||
// Next statement in body
|
||||
belowp->addNextHere(newp);
|
||||
} else {
|
||||
belowp->v3fatalSrc("Doesn't look like this was really under the while");
|
||||
belowp->v3fatalSrc("Doesn't look like this was really under the while");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,17 +840,17 @@ void AstNode::dump(std::ostream& str) {
|
||||
if (user5p()) str<<" u5="<<cvtToHex(user5p());
|
||||
if (hasDType()) {
|
||||
// Final @ so less likely to by accident read it as a nodep
|
||||
if (dtypep()==this) str<<" @dt="<<"this@";
|
||||
if (dtypep()==this) str<<" @dt="<<"this@";
|
||||
else str<<" @dt="<<cvtToHex(dtypep())<<"@";
|
||||
if (AstNodeDType* dtp = dtypep()) {
|
||||
dtp->dumpSmall(str);
|
||||
}
|
||||
} else { // V3Broken will throw an error
|
||||
if (AstNodeDType* dtp = dtypep()) {
|
||||
dtp->dumpSmall(str);
|
||||
}
|
||||
} else { // V3Broken will throw an error
|
||||
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<cvtToHex(dtypep());
|
||||
}
|
||||
if (name()!="") {
|
||||
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
|
||||
else str<<" "<<V3Number::quoteNameControls(name());
|
||||
else str<<" "<<V3OutFormatter::quoteNameControls(name());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,7 +960,7 @@ void AstNodeDType::dump(std::ostream& str) {
|
||||
if (generic()) str<<" [GENERIC]";
|
||||
if (AstNodeDType* dtp = virtRefDTypep()) {
|
||||
str<<" refdt="<<cvtToHex(dtp);
|
||||
dtp->dumpSmall(str);
|
||||
dtp->dumpSmall(str);
|
||||
}
|
||||
}
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) {
|
||||
@@ -961,7 +971,7 @@ void AstNodeDType::dumpSmall(std::ostream& str) {
|
||||
<<(isDouble()?"d":"")
|
||||
<<(isString()?"str":"");
|
||||
if (!isDouble() && !isString()) {
|
||||
str<<"w"<<(widthSized()?"":"u")<<width();
|
||||
str<<"w"<<(widthSized()?"":"u")<<width();
|
||||
}
|
||||
if (!widthSized()) str<<"/"<<widthMin();
|
||||
str<<")";
|
||||
@@ -995,8 +1005,8 @@ void AstPackageImport::dump(std::ostream& str) {
|
||||
void AstSel::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl"<<declRange()<<"]";
|
||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||
str<<" decl"<<declRange()<<"]";
|
||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||
}
|
||||
}
|
||||
void AstSliceSel::dump(std::ostream& str) {
|
||||
@@ -1013,37 +1023,37 @@ void AstMTaskBody::dump(std::ostream& str) {
|
||||
void AstTypeTable::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
if (AstBasicDType* subnodep=m_basicps[i]) {
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
if (AstBasicDType* subnodep = m_basicps[i]) {
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
|
||||
str<<" -> ";
|
||||
subnodep->dump(str);
|
||||
}
|
||||
str<<" -> ";
|
||||
subnodep->dump(str);
|
||||
}
|
||||
}
|
||||
for (int isbit=0; isbit<2; ++isbit) {
|
||||
for (int issigned=0; issigned<AstNumeric::_ENUM_MAX; ++issigned) {
|
||||
LogicMap& mapr = m_logicMap[isbit][issigned];
|
||||
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
for (int issigned=0; issigned<AstNumeric::_ENUM_MAX; ++issigned) {
|
||||
LogicMap& mapr = m_logicMap[isbit][issigned];
|
||||
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
std::stringstream nsstr;
|
||||
nsstr<<(isbit?"bw":"lw")
|
||||
<<it->first.first<<"/"<<it->first.second;
|
||||
nsstr<<(isbit?"bw":"lw")
|
||||
<<it->first.first<<"/"<<it->first.second;
|
||||
str<<"\t\t"<<std::setw(8)<<nsstr.str();
|
||||
if (issigned) str<<" s"; else str<<" u";
|
||||
str<<" -> ";
|
||||
dtypep->dump(str);
|
||||
}
|
||||
}
|
||||
if (issigned) str<<" s"; else str<<" u";
|
||||
str<<" -> ";
|
||||
dtypep->dump(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
DetailedMap& mapr = m_detailedMap;
|
||||
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
str<<"\t\tdetailed -> ";
|
||||
dtypep->dump(str);
|
||||
}
|
||||
DetailedMap& mapr = m_detailedMap;
|
||||
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
str<<"\t\tdetailed -> ";
|
||||
dtypep->dump(str);
|
||||
}
|
||||
}
|
||||
// Note get newline from caller too.
|
||||
}
|
||||
@@ -1152,8 +1162,8 @@ void AstBegin::dump(std::ostream& str) {
|
||||
void AstCoverDecl::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (this->dataDeclNullp()) {
|
||||
str<<" -> ";
|
||||
this->dataDeclNullp()->dump(str);
|
||||
str<<" -> ";
|
||||
this->dataDeclNullp()->dump(str);
|
||||
} else {
|
||||
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
|
||||
}
|
||||
@@ -1175,8 +1185,8 @@ void AstNodeText::dump(std::ostream& str) {
|
||||
string out = text();
|
||||
string::size_type pos;
|
||||
if ((pos = out.find('\n')) != string::npos) {
|
||||
out.erase(pos,out.length()-pos);
|
||||
out += "...";
|
||||
out.erase(pos, out.length()-pos);
|
||||
out += "...";
|
||||
}
|
||||
str<<" \""<<out<<"\"";
|
||||
}
|
||||
@@ -1189,8 +1199,8 @@ void AstCFile::dump(std::ostream& str) {
|
||||
void AstCCall::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (funcp()) {
|
||||
str<<" "<<funcp()->name()<<" => ";
|
||||
funcp()->dump(str);
|
||||
str<<" "<<funcp()->name()<<" => ";
|
||||
funcp()->dump(str);
|
||||
}
|
||||
}
|
||||
void AstCFunc::dump(std::ostream& str) {
|
||||
|
||||
+2173
-1923
File diff suppressed because it is too large
Load Diff
+151
-149
@@ -20,9 +20,9 @@
|
||||
// V3Begin's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Look for BEGINs
|
||||
// BEGIN(VAR...) -> VAR ... {renamed}
|
||||
// FOR -> WHILEs
|
||||
// Look for BEGINs
|
||||
// BEGIN(VAR...) -> VAR ... {renamed}
|
||||
// FOR -> WHILEs
|
||||
//
|
||||
// There are two scopes; named BEGINs change %m and variable scopes.
|
||||
// Unnamed BEGINs change only variable, not $display("%m") scope.
|
||||
@@ -47,17 +47,17 @@ class BeginState {
|
||||
private:
|
||||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// AstNodeFTask::user1 -> bool, 1=processed
|
||||
AstUser1InUse m_inuser1;
|
||||
bool m_anyFuncInBegin;
|
||||
// AstNodeFTask::user1 -> bool, 1=processed
|
||||
AstUser1InUse m_inuser1;
|
||||
bool m_anyFuncInBegin;
|
||||
public:
|
||||
BeginState() {
|
||||
m_anyFuncInBegin = false;
|
||||
m_anyFuncInBegin = false;
|
||||
}
|
||||
~BeginState() {}
|
||||
void userMarkChanged(AstNode* nodep) {
|
||||
nodep->user1(true);
|
||||
m_anyFuncInBegin = true;
|
||||
nodep->user1(true);
|
||||
m_anyFuncInBegin = true;
|
||||
}
|
||||
bool anyFuncInBegin() const { return m_anyFuncInBegin; }
|
||||
};
|
||||
@@ -67,159 +67,161 @@ public:
|
||||
class BeginVisitor : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
BeginState* m_statep; // Current global state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_repeatNum; // Repeat counter
|
||||
int m_ifDepth; // Current if depth
|
||||
BeginState* m_statep; // Current global state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_repeatNum; // Repeat counter
|
||||
int m_ifDepth; // Current if depth
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_repeatNum = 0;
|
||||
m_modp = nodep;
|
||||
m_repeatNum = 0;
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
// Rename it
|
||||
if (m_unnamedScope != "") {
|
||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||
m_statep->userMarkChanged(nodep);
|
||||
}
|
||||
// BEGIN wrapping a function rename that function, but don't affect the inside function's variables
|
||||
// We then restart with empty naming; so that any begin's inside the function will rename inside the function
|
||||
// Process children
|
||||
string oldScope = m_namedScope;
|
||||
string oldUnnamed = m_unnamedScope;
|
||||
{
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
m_ftaskp = nodep;
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
// Rename it
|
||||
if (m_unnamedScope != "") {
|
||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||
m_statep->userMarkChanged(nodep);
|
||||
}
|
||||
// BEGIN wrapping a function rename that function, but don't affect
|
||||
// the inside function's variables. We then restart with empty
|
||||
// naming; so that any begin's inside the function will rename
|
||||
// inside the function.
|
||||
// Process children
|
||||
string oldScope = m_namedScope;
|
||||
string oldUnnamed = m_unnamedScope;
|
||||
{
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
m_ftaskp = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
m_unnamedScope = oldUnnamed;
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
m_unnamedScope = oldUnnamed;
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
// Begin blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
string oldScope = m_namedScope;
|
||||
string oldUnnamed = m_unnamedScope;
|
||||
{
|
||||
UINFO(8,"nname "<<m_namedScope<<endl);
|
||||
if (nodep->name() != "") { // Else unneeded unnamed block
|
||||
// Create data for dotted variable resolution
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
string::size_type pos;
|
||||
while ((pos=dottedname.find("__DOT__")) != string::npos) {
|
||||
string ident = dottedname.substr(0,pos);
|
||||
dottedname = dottedname.substr(pos+strlen("__DOT__"));
|
||||
if (!nodep->unnamed()) {
|
||||
if (m_namedScope=="") m_namedScope = ident;
|
||||
else m_namedScope = m_namedScope + "__DOT__"+ident;
|
||||
}
|
||||
if (m_unnamedScope=="") m_unnamedScope = ident;
|
||||
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
|
||||
// Create CellInline for dotted var resolution
|
||||
if (!m_ftaskp) {
|
||||
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
||||
m_unnamedScope, "__BEGIN__");
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
}
|
||||
}
|
||||
}
|
||||
// Begin blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
string oldScope = m_namedScope;
|
||||
string oldUnnamed = m_unnamedScope;
|
||||
{
|
||||
UINFO(8,"nname "<<m_namedScope<<endl);
|
||||
if (nodep->name() != "") { // Else unneeded unnamed block
|
||||
// Create data for dotted variable resolution
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
string::size_type pos;
|
||||
while ((pos=dottedname.find("__DOT__")) != string::npos) {
|
||||
string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos+strlen("__DOT__"));
|
||||
if (!nodep->unnamed()) {
|
||||
if (m_namedScope=="") m_namedScope = ident;
|
||||
else m_namedScope = m_namedScope + "__DOT__"+ident;
|
||||
}
|
||||
if (m_unnamedScope=="") m_unnamedScope = ident;
|
||||
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
|
||||
// Create CellInline for dotted var resolution
|
||||
if (!m_ftaskp) {
|
||||
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
||||
m_unnamedScope, "__BEGIN__");
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remap var names and replace lower Begins
|
||||
// Remap var names and replace lower Begins
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
m_unnamedScope = oldUnnamed;
|
||||
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
m_unnamedScope = oldUnnamed;
|
||||
|
||||
// Cleanup
|
||||
AstNode* addsp = NULL;
|
||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
|
||||
}
|
||||
if (addsp) {
|
||||
nodep->replaceWith(addsp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
// Cleanup
|
||||
AstNode* addsp = NULL;
|
||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
|
||||
}
|
||||
if (addsp) {
|
||||
nodep->replaceWith(addsp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||
else m_modp->addStmtp(nodep);
|
||||
}
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||
else m_modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
UINFO(8," CELL "<<nodep<<endl);
|
||||
if (m_namedScope != "") {
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Rename it
|
||||
nodep->name(m_namedScope+"__DOT__"+nodep->name());
|
||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
UINFO(8," CELL "<<nodep<<endl);
|
||||
if (m_namedScope != "") {
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Rename it
|
||||
nodep->name(m_namedScope+"__DOT__"+nodep->name());
|
||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) {
|
||||
UINFO(9, " VARXREF "<<nodep<<endl);
|
||||
if (m_namedScope != "" && nodep->inlinedDots() == "") {
|
||||
nodep->inlinedDots(m_namedScope);
|
||||
UINFO(9, " rescope to "<<nodep<<endl);
|
||||
}
|
||||
UINFO(9, " VARXREF "<<nodep<<endl);
|
||||
if (m_namedScope != "" && nodep->inlinedDots() == "") {
|
||||
nodep->inlinedDots(m_namedScope);
|
||||
UINFO(9, " rescope to "<<nodep<<endl);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) {
|
||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||
// Similar code in V3Inline
|
||||
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
||||
if (m_namedScope != "") {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||
// Similar code in V3Inline
|
||||
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
||||
if (m_namedScope != "") {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) {
|
||||
// Don't need to fix path in coverage statements, they're not under
|
||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
// Don't need to fix path in coverage statements, they're not under
|
||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
||||
// Check IFDEPTH warning - could be in other transform files if desire
|
||||
int prevIfDepth = m_ifDepth;
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
|
||||
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
|
||||
m_ifDepth = -1;
|
||||
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
|
||||
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
||||
m_ifDepth = -1;
|
||||
}
|
||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
||||
// Check IFDEPTH warning - could be in other transform files if desire
|
||||
int prevIfDepth = m_ifDepth;
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
|
||||
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
|
||||
m_ifDepth = -1;
|
||||
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
|
||||
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
||||
m_ifDepth = -1;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
m_ifDepth = prevIfDepth;
|
||||
m_ifDepth = prevIfDepth;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
iterateChildren(nodep);
|
||||
@@ -227,11 +229,11 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
BeginVisitor(AstNetlist* nodep, BeginState* statep) {
|
||||
m_statep = statep;
|
||||
m_modp = NULL;
|
||||
m_ftaskp = NULL;
|
||||
m_repeatNum = 0;
|
||||
m_ifDepth = 0;
|
||||
m_statep = statep;
|
||||
m_modp = NULL;
|
||||
m_ftaskp = NULL;
|
||||
m_repeatNum = 0;
|
||||
m_ifDepth = 0;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginVisitor() {}
|
||||
@@ -244,29 +246,29 @@ class BeginRelinkVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
// AstNodeFTask::user1p // Node replaced, rename it
|
||||
// AstNodeFTask::user1p // Node replaced, rename it
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
if (nodep->taskp()->user1()) { // It was converted
|
||||
UINFO(9, " relinkFTask "<<nodep<<endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
if (nodep->taskp()->user1()) { // It was converted
|
||||
UINFO(9, " relinkFTask "<<nodep<<endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef "<<nodep<<endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef "<<nodep<<endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) {
|
||||
// May have changed cell names
|
||||
// TypeTable is always after all modules, so names are stable
|
||||
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
||||
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
||||
UINFO(8," rename to "<<nodep<<endl);
|
||||
// May have changed cell names
|
||||
// TypeTable is always after all modules, so names are stable
|
||||
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
||||
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
||||
UINFO(8," rename to "<<nodep<<endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void debeginAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+52
-52
@@ -18,12 +18,12 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// BRANCH TRANSFORMATIONS:
|
||||
// At each IF/(IF else).
|
||||
// Count underneath $display/$stop statements.
|
||||
// If more on if than else, this branch is unlikely, or vice-versa.
|
||||
// At each FTASKREF,
|
||||
// Count calls into the function
|
||||
// Then, if FTASK is called only once, add inline attribute
|
||||
// At each IF/(IF else).
|
||||
// Count underneath $display/$stop statements.
|
||||
// If more on if than else, this branch is unlikely, or vice-versa.
|
||||
// At each FTASKREF,
|
||||
// Count calls into the function
|
||||
// Then, if FTASK is called only once, add inline attribute
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -44,89 +44,89 @@ class BranchVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstFTask::user1() -> int. Number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstFTask::user1() -> int. Number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef std::vector<AstCFunc*> CFuncVec;
|
||||
|
||||
// STATE
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
int m_unlikely; // Excuses for branch likely not taken
|
||||
CFuncVec m_cfuncsp; // List of all tasks
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
int m_unlikely; // Excuses for branch likely not taken
|
||||
CFuncVec m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void reset() {
|
||||
m_likely = false;
|
||||
m_unlikely = false;
|
||||
m_likely = false;
|
||||
m_unlikely = false;
|
||||
}
|
||||
void checkUnlikely(AstNode* nodep) {
|
||||
if (nodep->isUnlikely()) {
|
||||
UINFO(4," UNLIKELY: "<<nodep<<endl);
|
||||
m_unlikely++;
|
||||
}
|
||||
if (nodep->isUnlikely()) {
|
||||
UINFO(4," UNLIKELY: "<<nodep<<endl);
|
||||
m_unlikely++;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
int lastLikely = m_likely;
|
||||
int lastUnlikely = m_unlikely;
|
||||
{
|
||||
// Do if
|
||||
reset();
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
int lastLikely = m_likely;
|
||||
int lastUnlikely = m_unlikely;
|
||||
{
|
||||
// Do if
|
||||
reset();
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
int ifLikely = m_likely;
|
||||
int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
reset();
|
||||
int ifLikely = m_likely;
|
||||
int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
reset();
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
int elseLikely = m_likely;
|
||||
int elseUnlikely = m_unlikely;
|
||||
// Compute
|
||||
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
|
||||
if (likeness>0) {
|
||||
nodep->branchPred(AstBranchPred::BP_LIKELY);
|
||||
} else if (likeness<0) {
|
||||
nodep->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
} // else leave unknown
|
||||
}
|
||||
m_likely = lastLikely;
|
||||
m_unlikely = lastUnlikely;
|
||||
int elseLikely = m_likely;
|
||||
int elseUnlikely = m_unlikely;
|
||||
// Compute
|
||||
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
|
||||
if (likeness>0) {
|
||||
nodep->branchPred(AstBranchPred::BP_LIKELY);
|
||||
} else if (likeness<0) {
|
||||
nodep->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
} // else leave unknown
|
||||
}
|
||||
m_likely = lastLikely;
|
||||
m_unlikely = lastUnlikely;
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
checkUnlikely(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void calc_tasks() {
|
||||
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
|
||||
AstCFunc* nodep = *it;
|
||||
if (!nodep->dontInline()) {
|
||||
nodep->isInline(true);
|
||||
}
|
||||
}
|
||||
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
|
||||
AstCFunc* nodep = *it;
|
||||
if (!nodep->dontInline()) {
|
||||
nodep->isInline(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit BranchVisitor(AstNetlist* nodep) {
|
||||
reset();
|
||||
reset();
|
||||
iterateChildren(nodep);
|
||||
calc_tasks();
|
||||
calc_tasks();
|
||||
}
|
||||
virtual ~BranchVisitor() {}
|
||||
};
|
||||
|
||||
+1
-1
@@ -35,4 +35,4 @@ public:
|
||||
static void branchAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+139
-118
@@ -20,8 +20,8 @@
|
||||
// V3Broken's Transformations:
|
||||
//
|
||||
// Entire netlist
|
||||
// Mark all nodes
|
||||
// Check all links point to marked nodes
|
||||
// Mark all nodes
|
||||
// Check all links point to marked nodes
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -47,125 +47,140 @@ private:
|
||||
// MEMBERS
|
||||
// For each node, we keep if it exists or not.
|
||||
typedef vl_unordered_map<const AstNode*,int> NodeMap; // Performance matters (when --debug)
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
// BITMASK
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
public:
|
||||
// METHODS
|
||||
static void deleted(const AstNode* nodep) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeDel: "<<cvtToHex(nodep)<<endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
|
||||
reinterpret_cast<const AstNode*>(nodep)
|
||||
->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted");
|
||||
}
|
||||
if (iter!=s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
if (iter!=s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
|
||||
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
|
||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
static void addNewed(const AstNode* nodep) {
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeNew: "<<cvtToHex(nodep)<<endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter!=s_nodes.end() || (iter->second & FLAG_ALLOCATED)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
}
|
||||
if (iter == s_nodes.end()) {
|
||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.insert(make_pair(nodep,flags));
|
||||
}
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter !=s_nodes.end() && (iter->second & FLAG_ALLOCATED)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
}
|
||||
if (iter == s_nodes.end()) {
|
||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.insert(make_pair(nodep, flags));
|
||||
}
|
||||
}
|
||||
static void setUnder(const AstNode* nodep, bool flag) {
|
||||
// Called by BrokenCheckVisitor when each node entered/exited
|
||||
if (!okIfLinkedTo(nodep)) return;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter!=s_nodes.end()) {
|
||||
iter->second &= ~FLAG_UNDER_NOW;
|
||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||
}
|
||||
// Called by BrokenCheckVisitor when each node entered/exited
|
||||
if (!okIfLinkedTo(nodep)) return;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter!=s_nodes.end()) {
|
||||
iter->second &= ~FLAG_UNDER_NOW;
|
||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||
}
|
||||
}
|
||||
static void addInTree(AstNode* nodep, bool linkable) {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
if (!linkable) return; // save some time, else the map will get huge!
|
||||
if (!linkable) return; // save some time, else the map will get huge!
|
||||
#endif
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) {
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
} else {
|
||||
if (!(iter->second & FLAG_ALLOCATED)) {
|
||||
} else {
|
||||
if (!(iter->second & FLAG_ALLOCATED)) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
}
|
||||
if (iter->second & FLAG_IN_TREE) {
|
||||
nodep->v3fatalSrc("AstNode is already in tree at another location");
|
||||
}
|
||||
}
|
||||
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.insert(make_pair(nodep,or_flags));
|
||||
} else {
|
||||
iter->second |= or_flags;
|
||||
}
|
||||
}
|
||||
if (iter->second & FLAG_IN_TREE) {
|
||||
nodep->v3fatalSrc("AstNode is already in tree at another location");
|
||||
}
|
||||
}
|
||||
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.insert(make_pair(nodep, or_flags));
|
||||
} else {
|
||||
iter->second |= or_flags;
|
||||
}
|
||||
}
|
||||
static bool isAllocated(const AstNode* nodep) {
|
||||
// Some generic node has a pointer to this node. Is it allocated?
|
||||
// Use this when might not be in tree; otherwise use okIfLinkedTo().
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
static bool okIfLinkedTo(const AstNode* nodep) {
|
||||
// Someone has a pointer to this node. Is it kosher?
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
// Some node in tree has a pointer to this node. Is it kosher?
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
#endif
|
||||
if (!(iter->second & FLAG_IN_TREE)) return false;
|
||||
if (!(iter->second & FLAG_LINKABLE)) return false;
|
||||
return true;
|
||||
if (!(iter->second & FLAG_IN_TREE)) return false;
|
||||
if (!(iter->second & FLAG_LINKABLE)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool okIfBelow(const AstNode* nodep) {
|
||||
// Must be linked to and below current node
|
||||
if (!okIfLinkedTo(nodep)) return false;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
||||
return true;
|
||||
// Must be linked to and below current node
|
||||
if (!okIfLinkedTo(nodep)) return false;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
||||
return true;
|
||||
}
|
||||
static void prepForTree() {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
s_nodes.clear();
|
||||
s_nodes.clear();
|
||||
#endif
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
it->second &= ~FLAG_IN_TREE;
|
||||
it->second &= ~FLAG_LINKABLE;
|
||||
}
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
it->second &= ~FLAG_IN_TREE;
|
||||
it->second &= ~FLAG_LINKABLE;
|
||||
}
|
||||
}
|
||||
static void doneWithTree() {
|
||||
for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
if ((it->second & FLAG_ALLOCATED)
|
||||
&& !(it->second & FLAG_IN_TREE)
|
||||
&& !(it->second & FLAG_LEAKED)
|
||||
&& (it->first->backp() ? backs==1 : backs==0)) {
|
||||
// Use only AstNode::dump instead of the virtual one, as there
|
||||
// may be varp() and other cross links that are bad.
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
if ((it->second & FLAG_ALLOCATED)
|
||||
&& !(it->second & FLAG_IN_TREE)
|
||||
&& !(it->second & FLAG_LEAKED)
|
||||
&& (it->first->backp() ? backs==1 : backs==0)) {
|
||||
// Use only AstNode::dump instead of the virtual one, as there
|
||||
// may be varp() and other cross links that are bad.
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
// When get this message, find what forgot to delete the
|
||||
// node by running GDB, where for node "<e###>" use:
|
||||
// watch AstNode::s_editCntGbl==####
|
||||
// run
|
||||
// bt
|
||||
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||
AstNode* rawp = const_cast<AstNode*>
|
||||
(static_cast<const AstNode*>(it->first));
|
||||
rawp->AstNode::dump(std::cerr);
|
||||
std::cerr<<endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -190,16 +205,16 @@ class BrokenMarkVisitor : public AstNVisitor {
|
||||
// Mark every node in the tree
|
||||
private:
|
||||
// NODE STATE
|
||||
// Nothing! // This may be called deep inside other routines
|
||||
// // so userp and friends may not be used
|
||||
// Nothing! // This may be called deep inside other routines
|
||||
// // so userp and friends may not be used
|
||||
// METHODS
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
processAndIterate(nodep);
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -215,17 +230,17 @@ public:
|
||||
class BrokenCheckVisitor : public AstNVisitor {
|
||||
private:
|
||||
void checkWidthMin(const AstNode* nodep) {
|
||||
if (nodep->width() != nodep->widthMin()
|
||||
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
|
||||
nodep->v3fatalSrc("Width != WidthMin");
|
||||
}
|
||||
if (nodep->width() != nodep->widthMin()
|
||||
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
|
||||
nodep->v3fatalSrc("Width != WidthMin");
|
||||
}
|
||||
}
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::setUnder(nodep,true);
|
||||
if (const char* whyp=nodep->broken()) {
|
||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||
}
|
||||
if (nodep->dtypep()) {
|
||||
BrokenTable::setUnder(nodep, true);
|
||||
if (const char* whyp=nodep->broken()) {
|
||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||
}
|
||||
if (nodep->dtypep()) {
|
||||
if (!nodep->dtypep()->brokeExists()) {
|
||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "
|
||||
<<cvtToHex(nodep->dtypep()));
|
||||
@@ -233,31 +248,34 @@ private:
|
||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "
|
||||
<<cvtToHex(nodep->dtypep()));
|
||||
}
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype on node with hasDType(): "<<nodep->prettyTypeName());
|
||||
} else {
|
||||
if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
}
|
||||
if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed");
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
if (!nodep->dtypep()) nodep->v3fatalSrc(
|
||||
"No dtype on node with hasDType(): "<<nodep->prettyTypeName());
|
||||
} else {
|
||||
if (nodep->dtypep()) nodep->v3fatalSrc(
|
||||
"DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
}
|
||||
if (nodep->getChildDTypep()) nodep->v3fatalSrc(
|
||||
"childDTypep() non-null on node after should have removed");
|
||||
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
BrokenTable::setUnder(nodep,false);
|
||||
BrokenTable::setUnder(nodep, false);
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
processAndIterate(nodep);
|
||||
if (v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
processAndIterate(nodep);
|
||||
if (v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
|
||||
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
|
||||
}
|
||||
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
processAndIterate(nodep);
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -274,15 +292,15 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
//UINFO(9,__FUNCTION__<<": "<<endl);
|
||||
static bool inBroken = false;
|
||||
if (inBroken) {
|
||||
// A error called by broken can recurse back into broken; avoid this
|
||||
UINFO(1,"Broken called under broken, skipping recursion.\n");
|
||||
// A error called by broken can recurse back into broken; avoid this
|
||||
UINFO(1,"Broken called under broken, skipping recursion.\n");
|
||||
} else {
|
||||
inBroken = true;
|
||||
BrokenTable::prepForTree();
|
||||
BrokenMarkVisitor mvisitor (nodep);
|
||||
BrokenCheckVisitor cvisitor (nodep);
|
||||
BrokenTable::doneWithTree();
|
||||
inBroken = false;
|
||||
inBroken = true;
|
||||
BrokenTable::prepForTree();
|
||||
BrokenMarkVisitor mvisitor (nodep);
|
||||
BrokenCheckVisitor cvisitor (nodep);
|
||||
BrokenTable::doneWithTree();
|
||||
inBroken = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,3 +310,6 @@ void V3Broken::addNewed(AstNode* nodep) {
|
||||
void V3Broken::deleted(AstNode* nodep) {
|
||||
BrokenTable::deleted(nodep);
|
||||
}
|
||||
bool V3Broken::isAllocated(AstNode* nodep) {
|
||||
return BrokenTable::isAllocated(nodep);
|
||||
}
|
||||
|
||||
+2
-1
@@ -34,6 +34,7 @@ public:
|
||||
static void brokenAll(AstNetlist* nodep);
|
||||
static void addNewed(AstNode* nodep);
|
||||
static void deleted(AstNode* nodep);
|
||||
static bool isAllocated(AstNode* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+79
-74
@@ -18,14 +18,14 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3CCtors's Transformations:
|
||||
// Iterates over all modules and
|
||||
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
|
||||
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
|
||||
// For each variable that needs reset, add a AstCReset node.
|
||||
// Iterates over all modules and
|
||||
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
|
||||
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
|
||||
// For each variable that needs reset, add a AstCReset node.
|
||||
//
|
||||
// For primary inputs, add _eval_debug_assertions.
|
||||
// For primary inputs, add _eval_debug_assertions.
|
||||
//
|
||||
// This transformation honors outputSplitCFuncs.
|
||||
// This transformation honors outputSplitCFuncs.
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
@@ -43,59 +43,60 @@
|
||||
|
||||
class V3CCtorsVisitor {
|
||||
private:
|
||||
string m_basename;
|
||||
string m_argsp;
|
||||
string m_callargsp;
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_tlFuncp; // Top level function being built
|
||||
AstCFunc* m_funcp; // Current function
|
||||
int m_numStmts; // Number of statements output
|
||||
int m_funcNum; // Function number being built
|
||||
string m_basename;
|
||||
string m_argsp;
|
||||
string m_callargsp;
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_tlFuncp; // Top level function being built
|
||||
AstCFunc* m_funcp; // Current function
|
||||
int m_numStmts; // Number of statements output
|
||||
int m_funcNum; // Function number being built
|
||||
|
||||
public:
|
||||
void add(AstNode* nodep) {
|
||||
if (v3Global.opt.outputSplitCFuncs()
|
||||
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_funcp = NULL;
|
||||
}
|
||||
if (!m_funcp) {
|
||||
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum), NULL, "void");
|
||||
m_funcp->isStatic(false);
|
||||
m_funcp->declPrivate(true);
|
||||
m_funcp->slow(true);
|
||||
m_funcp->argTypes(m_argsp);
|
||||
m_modp->addStmtp(m_funcp);
|
||||
if (v3Global.opt.outputSplitCFuncs()
|
||||
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_funcp = NULL;
|
||||
}
|
||||
if (!m_funcp) {
|
||||
m_funcp = new AstCFunc(m_modp->fileline(),
|
||||
m_basename+"_"+cvtToStr(++m_funcNum), NULL, "void");
|
||||
m_funcp->isStatic(false);
|
||||
m_funcp->declPrivate(true);
|
||||
m_funcp->slow(true);
|
||||
m_funcp->argTypes(m_argsp);
|
||||
m_modp->addStmtp(m_funcp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
|
||||
callp->argTypes(m_callargsp);
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
|
||||
callp->argTypes(m_callargsp);
|
||||
|
||||
m_tlFuncp->addStmtsp(callp);
|
||||
m_numStmts = 0;
|
||||
}
|
||||
m_funcp->addStmtsp(nodep);
|
||||
m_numStmts += 1;
|
||||
m_tlFuncp->addStmtsp(callp);
|
||||
m_numStmts = 0;
|
||||
}
|
||||
m_funcp->addStmtsp(nodep);
|
||||
m_numStmts += 1;
|
||||
}
|
||||
|
||||
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename,
|
||||
const string& argsp="", const string& callargsp="",
|
||||
const string& stmt="") {
|
||||
m_basename = basename;
|
||||
m_argsp = argsp;
|
||||
m_callargsp = callargsp;
|
||||
m_modp = nodep;
|
||||
m_numStmts = 0;
|
||||
m_funcNum = 0;
|
||||
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
|
||||
m_tlFuncp->declPrivate(true);
|
||||
m_tlFuncp->isStatic(false);
|
||||
m_tlFuncp->slow(true);
|
||||
m_tlFuncp->argTypes(m_argsp);
|
||||
if (stmt != "") {
|
||||
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
m_funcp = m_tlFuncp;
|
||||
m_modp->addStmtp(m_tlFuncp);
|
||||
const string& stmt="") {
|
||||
m_basename = basename;
|
||||
m_argsp = argsp;
|
||||
m_callargsp = callargsp;
|
||||
m_modp = nodep;
|
||||
m_numStmts = 0;
|
||||
m_funcNum = 0;
|
||||
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
|
||||
m_tlFuncp->declPrivate(true);
|
||||
m_tlFuncp->isStatic(false);
|
||||
m_tlFuncp->slow(true);
|
||||
m_tlFuncp->argTypes(m_argsp);
|
||||
if (stmt != "") {
|
||||
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
m_funcp = m_tlFuncp;
|
||||
m_modp->addStmtp(m_tlFuncp);
|
||||
}
|
||||
~V3CCtorsVisitor() {}
|
||||
private:
|
||||
@@ -116,36 +117,38 @@ void V3CCtors::evalAsserts() {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryInish() && !varp->isSc()) {
|
||||
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel(varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), varp->widthWords()-1));
|
||||
}
|
||||
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
V3Number num (varp->fileline(), storedWidth, value);
|
||||
newp = new AstAnd(varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), num));
|
||||
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
|
||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel(
|
||||
varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), varp->widthWords()-1));
|
||||
}
|
||||
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
newp = new AstAnd(varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
||||
storedWidth, value));
|
||||
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
|
||||
new AstCStmt(varp->fileline(),
|
||||
"Verilated::overWidthError(\""+varp->prettyName()+"\");"));
|
||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
newp = ifp;
|
||||
funcp->addStmtsp(newp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
newp = ifp;
|
||||
funcp->addStmtsp(newp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V3CCtors::cctorsAll() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
evalAsserts();
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp();
|
||||
modp; modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
|
||||
@@ -153,14 +156,16 @@ void V3CCtors::cctorsAll() {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef()
|
||||
&& !varp->noReset()) {
|
||||
var_reset.add(new AstCReset(varp->fileline(), new AstVarRef(varp->fileline(), varp, true)));
|
||||
var_reset.add(new AstCReset(varp->fileline(),
|
||||
new AstVarRef(varp->fileline(), varp, true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsVisitor configure_coverage
|
||||
(modp, "_configure_coverage", EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
|
||||
(modp, "_configure_coverage",
|
||||
EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
|
||||
"if (0 && vlSymsp && first) {} // Prevent unused\n");
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||
|
||||
+1
-1
@@ -37,4 +37,4 @@ private:
|
||||
};
|
||||
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+342
-332
@@ -20,20 +20,21 @@
|
||||
// V3Case's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
|
||||
// Need __en in changed list if a signal is on the LHS of a assign
|
||||
// Cases:
|
||||
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
|
||||
// (AND item1 mask))
|
||||
// (other items))
|
||||
// body
|
||||
// Or, converts to a if/else tree.
|
||||
// FUTURES:
|
||||
// Large 16+ bit tables with constants and no masking (address muxes)
|
||||
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
|
||||
// Need __en in changed list if a signal is on the LHS of a assign
|
||||
// Cases:
|
||||
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
|
||||
// (AND item1 mask))
|
||||
// (other items))
|
||||
// body
|
||||
// Or, converts to a if/else tree.
|
||||
// FUTURES:
|
||||
// Large 16+ bit tables with constants and no masking (address muxes)
|
||||
// Enter all into std::multimap, sort by value and use a tree of < and == compares.
|
||||
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
|
||||
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
|
||||
// Ignoring mask, check each value is unique (using std::multimap as above?)
|
||||
// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.)
|
||||
// Each branch is then mask-and-compare operation (IE
|
||||
// <000000001_000000000 at midpoint.)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -48,60 +49,62 @@
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CaseLintVisitor : public AstNVisitor {
|
||||
private:
|
||||
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
|
||||
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstNodeCase* nodep) {
|
||||
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
|
||||
nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
// Detect multiple defaults
|
||||
bool hitDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
nodep->v3error("Multiple default statements in case statement.");
|
||||
}
|
||||
hitDefault = true;
|
||||
}
|
||||
}
|
||||
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
// Detect multiple defaults
|
||||
bool hitDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
nodep->v3error("Multiple default statements in case statement.");
|
||||
}
|
||||
hitDefault = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for X/Z in non-casex statements
|
||||
{
|
||||
m_caseExprp = nodep;
|
||||
// Check for X/Z in non-casex statements
|
||||
{
|
||||
m_caseExprp = nodep;
|
||||
iterate(nodep->exprp());
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
iterateAndNextNull(itemp->condsp());
|
||||
}
|
||||
m_caseExprp = NULL;
|
||||
}
|
||||
}
|
||||
m_caseExprp = NULL;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConst* nodep) {
|
||||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
|
||||
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
|
||||
} else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) {
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
} else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez()
|
||||
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
||||
if (nodep->num().isUnknown()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
|
||||
}
|
||||
}
|
||||
if (nodep->num().isUnknown()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
iterateChildren(nodep);
|
||||
@@ -109,7 +112,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) {
|
||||
m_caseExprp = NULL;
|
||||
m_caseExprp = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseLintVisitor() {}
|
||||
@@ -122,340 +125,347 @@ class CaseVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Cleared each Case
|
||||
// AstIf::user3() -> bool. Set true to indicate clone not needed
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstIf::user3() -> bool. Set true to indicate clone not needed
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
V3Double0 m_statCaseFast; // Statistic tracking
|
||||
V3Double0 m_statCaseSlow; // Statistic tracking
|
||||
V3Double0 m_statCaseFast; // Statistic tracking
|
||||
V3Double0 m_statCaseSlow; // Statistic tracking
|
||||
|
||||
// Per-CASE
|
||||
int m_caseWidth; // Width of valueItems
|
||||
int m_caseItems; // Number of caseItem unique values
|
||||
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
|
||||
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
|
||||
int m_caseWidth; // Width of valueItems
|
||||
int m_caseItems; // Number of caseItem unique values
|
||||
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
|
||||
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
bool opaque = false;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (icondp->isDouble()) opaque = true;
|
||||
int width = 0;
|
||||
bool opaque = false;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (icondp->isDouble()) opaque = true;
|
||||
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
|
||||
m_caseItems++;
|
||||
}
|
||||
}
|
||||
m_caseWidth = width;
|
||||
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false; // Too wide for analysis
|
||||
}
|
||||
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
||||
// Zero list of items for each value
|
||||
m_caseItems++;
|
||||
}
|
||||
}
|
||||
m_caseWidth = width;
|
||||
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false; // Too wide for analysis
|
||||
}
|
||||
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
||||
// Zero list of items for each value
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) m_valueItem[i] = NULL;
|
||||
// Now pick up the values for each assignment
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool bitched = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
//if (debug()>=9) icondp->dumpTree(cout," caseitem: ");
|
||||
// Now pick up the values for each assignment
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool bitched = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
//if (debug()>=9) icondp->dumpTree(cout, " caseitem: ");
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
V3Number nummask (itemp->fileline(), iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
uint32_t mask = nummask.toUInt();
|
||||
V3Number numval (itemp->fileline(), iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
uint32_t val = numval.toUInt();
|
||||
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
V3Number nummask (itemp, iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
uint32_t mask = nummask.toUInt();
|
||||
V3Number numval (itemp, iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
uint32_t val = numval.toUInt();
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<std::hex<<i<<")");
|
||||
bitched = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Defaults were moved to last in the caseitem list by V3LinkDot
|
||||
if (itemp->isDefault()) { // Case statement's default... Fill the table
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||
itemp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<<std::hex<<i<<")");
|
||||
bitched = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Defaults were moved to last in the caseitem list by V3LinkDot
|
||||
if (itemp->isDefault()) { // Case statement's default... Fill the table
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<std::hex<<i<<")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (m_caseItems <= 3) return false; // Not worth simplifing
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered (example pattern 0x"
|
||||
<<std::hex<<i<<")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (m_caseItems <= 3) return false; // Not worth simplifing
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
|
||||
AstNode* replaceCaseFastRecurse(AstNode* cexprp, int msb, uint32_t upperValue) {
|
||||
if (msb<0) {
|
||||
// There's no space for a IF. We know upperValue is thus down to a specific
|
||||
// exact value, so just return the tree value
|
||||
// Note can't clone here, as we're going to check for equivelence above
|
||||
return m_valueItem[upperValue];
|
||||
}
|
||||
else {
|
||||
// Make left and right subtrees
|
||||
// cexpr[msb:lsb] == 1
|
||||
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
|
||||
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
|
||||
if (msb<0) {
|
||||
// There's no space for a IF. We know upperValue is thus down to a specific
|
||||
// exact value, so just return the tree value
|
||||
// Note can't clone here, as we're going to check for equivelence above
|
||||
return m_valueItem[upperValue];
|
||||
}
|
||||
else {
|
||||
// Make left and right subtrees
|
||||
// cexpr[msb:lsb] == 1
|
||||
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
|
||||
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
|
||||
|
||||
if (tree0p == tree1p) {
|
||||
// Same logic on both sides, so we can just return one of 'em
|
||||
return tree0p;
|
||||
}
|
||||
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
|
||||
bool same = true;
|
||||
for (uint32_t a=upperValue,
|
||||
b=(upperValue|(1UL<<msb));
|
||||
a < (upperValue|(1UL<<msb));
|
||||
a++, b++) {
|
||||
if (m_valueItem[a] != m_valueItem[b]) { same=false; break; }
|
||||
}
|
||||
if (same) {
|
||||
tree1p->deleteTree(); VL_DANGLING(tree1p);
|
||||
return tree0p;
|
||||
}
|
||||
if (tree0p == tree1p) {
|
||||
// Same logic on both sides, so we can just return one of 'em
|
||||
return tree0p;
|
||||
}
|
||||
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
|
||||
bool same = true;
|
||||
for (uint32_t a=upperValue,
|
||||
b=(upperValue|(1UL<<msb));
|
||||
a < (upperValue|(1UL<<msb));
|
||||
a++, b++) {
|
||||
if (m_valueItem[a] != m_valueItem[b]) { same = false; break; }
|
||||
}
|
||||
if (same) {
|
||||
tree1p->deleteTree(); VL_DANGLING(tree1p);
|
||||
return tree0p;
|
||||
}
|
||||
|
||||
// Must have differing logic, so make a selection
|
||||
// Must have differing logic, so make a selection
|
||||
|
||||
// Case expressions can't be linked twice, so clone them
|
||||
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
|
||||
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
|
||||
// Case expressions can't be linked twice, so clone them
|
||||
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
|
||||
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
|
||||
|
||||
// Alternate scheme if we ever do multiple bits at a time:
|
||||
//V3Number nummask (cexprp->fileline(), cexprp->width(), (1UL<<msb));
|
||||
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
// new AstConst(cexprp->fileline(), nummask));
|
||||
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
msb, 1);
|
||||
AstNode* eqp = new AstNeq(cexprp->fileline(),
|
||||
new AstConst(cexprp->fileline(), 0),
|
||||
and1p);
|
||||
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
||||
ifp->user3(1); // So we don't bother to clone it
|
||||
return ifp;
|
||||
}
|
||||
// Alternate scheme if we ever do multiple bits at a time:
|
||||
//V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
|
||||
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
// new AstConst(cexprp->fileline(), nummask));
|
||||
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
msb, 1);
|
||||
AstNode* eqp = new AstNeq(cexprp->fileline(),
|
||||
new AstConst(cexprp->fileline(), 0),
|
||||
and1p);
|
||||
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
||||
ifp->user3(1); // So we don't bother to clone it
|
||||
return ifp;
|
||||
}
|
||||
}
|
||||
|
||||
void replaceCaseFast(AstCase* nodep) {
|
||||
// CASEx(cexpr,....
|
||||
// -> tree of IF(msb, IF(msb-1, 11, 10)
|
||||
// IF(msb-1, 01, 00))
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
// CASEx(cexpr,....
|
||||
// -> tree of IF(msb, IF(msb-1, 11, 10)
|
||||
// IF(msb-1, 01, 00))
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
|
||||
if (debug()>=9) {
|
||||
if (debug()>=9) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (AstNode* itemp = m_valueItem[i]) {
|
||||
if (AstNode* itemp = m_valueItem[i]) {
|
||||
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any assertions
|
||||
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
|
||||
// Handle any assertions
|
||||
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
|
||||
|
||||
AstNode::user3ClearTree();
|
||||
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
|
||||
// Case expressions can't be linked twice, so clone them
|
||||
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
|
||||
AstNode::user3ClearTree();
|
||||
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
|
||||
// Case expressions can't be linked twice, so clone them
|
||||
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
|
||||
|
||||
if (ifrootp) nodep->replaceWith(ifrootp);
|
||||
else nodep->unlinkFrBack();
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
||||
if (debug()>=9) ifrootp->dumpTree(cout," _simp: ");
|
||||
if (ifrootp) nodep->replaceWith(ifrootp);
|
||||
else nodep->unlinkFrBack();
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
||||
if (debug()>=9) ifrootp->dumpTree(cout, " _simp: ");
|
||||
}
|
||||
|
||||
void replaceCaseComplicated(AstCase* nodep) {
|
||||
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
|
||||
// -> IF((cexpr==icond1),istmts1,
|
||||
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
|
||||
// ,istmts2, istmts3
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
// We'll do this in two stages. First stage, convert the conditions to
|
||||
// the appropriate IF AND terms.
|
||||
if (debug()>=9) nodep->dumpTree(cout," _comp_IN: ");
|
||||
bool hadDefault = false;
|
||||
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
|
||||
// -> IF((cexpr==icond1),istmts1,
|
||||
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
|
||||
// ,istmts2, istmts3
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
// We'll do this in two stages. First stage, convert the conditions to
|
||||
// the appropriate IF AND terms.
|
||||
if (debug()>=9) nodep->dumpTree(cout, " _comp_IN: ");
|
||||
bool hadDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (!itemp->condsp()) {
|
||||
// Default clause. Just make true, we'll optimize it away later
|
||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
|
||||
hadDefault = true;
|
||||
} else {
|
||||
// Expressioned clause
|
||||
AstNode* icondNextp = NULL;
|
||||
AstNode* ifexprp = NULL; // If expression to test
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
|
||||
icondNextp = icondp->nextp();
|
||||
icondp->unlinkFrBack();
|
||||
if (!itemp->condsp()) {
|
||||
// Default clause. Just make true, we'll optimize it away later
|
||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
|
||||
hadDefault = true;
|
||||
} else {
|
||||
// Expressioned clause
|
||||
AstNode* icondNextp = NULL;
|
||||
AstNode* ifexprp = NULL; // If expression to test
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
|
||||
icondNextp = icondp->nextp();
|
||||
icondp->unlinkFrBack();
|
||||
|
||||
AstNode* condp = NULL; // Default is to use and1p/and2p
|
||||
AstNode* condp = NULL; // Default is to use and1p/and2p
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->lhsp()->unlinkFrBack());
|
||||
AstNode* bp = AstLte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->rhsp()->unlinkFrBack());
|
||||
condp = new AstAnd(itemp->fileline(), ap, bp);
|
||||
} else if (iconstp && iconstp->num().isFourState()
|
||||
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
||||
V3Number nummask (itemp->fileline(), iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
V3Number numval (itemp->fileline(), iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||
AstNode* and1p = cexprp->cloneTree(false);
|
||||
AstNode* and2p = icondp;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
}
|
||||
if (!ifexprp) {
|
||||
ifexprp = condp;
|
||||
} else {
|
||||
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
|
||||
}
|
||||
}
|
||||
// Replace expression in tree
|
||||
itemp->condsp(ifexprp);
|
||||
}
|
||||
}
|
||||
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
||||
if (!hadDefault) {
|
||||
// If there was no default, add a empty one, this greatly simplifies below code
|
||||
// and constant propagation will just eliminate it for us later.
|
||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
|
||||
NULL));
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout," _comp_COND: ");
|
||||
// Now build the IF statement tree
|
||||
// The tree can be quite huge. Pull ever group of 8 out, and make a OR tree.
|
||||
// This reduces the depth for the bottom elements, at the cost of some of the top elements.
|
||||
// If we ever have profiling data, we should pull out the most common item from here and
|
||||
// instead make it the first IF branch.
|
||||
int depth = 0;
|
||||
AstNode* grouprootp = NULL;
|
||||
AstIf* groupnextp = NULL;
|
||||
AstIf* itemnextp = NULL;
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->lhsp()->unlinkFrBack());
|
||||
AstNode* bp = AstLte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->rhsp()->unlinkFrBack());
|
||||
condp = new AstAnd(itemp->fileline(), ap, bp);
|
||||
} else if (iconstp && iconstp->num().isFourState()
|
||||
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
||||
V3Number nummask (itemp, iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
V3Number numval (itemp, iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||
AstNode* and1p = cexprp->cloneTree(false);
|
||||
AstNode* and2p = icondp;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
}
|
||||
if (!ifexprp) {
|
||||
ifexprp = condp;
|
||||
} else {
|
||||
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
|
||||
}
|
||||
}
|
||||
// Replace expression in tree
|
||||
itemp->condsp(ifexprp);
|
||||
}
|
||||
}
|
||||
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
||||
if (!hadDefault) {
|
||||
// If there was no default, add a empty one, this greatly simplifies below code
|
||||
// and constant propagation will just eliminate it for us later.
|
||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
|
||||
NULL));
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout, " _comp_COND: ");
|
||||
// Now build the IF statement tree
|
||||
// The tree can be quite huge. Pull ever group of 8 out, and make a OR tree.
|
||||
// This reduces the depth for the bottom elements, at the cost of
|
||||
// some of the top elements. If we ever have profiling data, we
|
||||
// should pull out the most common item from here and instead make
|
||||
// it the first IF branch.
|
||||
int depth = 0;
|
||||
AstNode* grouprootp = NULL;
|
||||
AstIf* groupnextp = NULL;
|
||||
AstIf* itemnextp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
{ // Prepare for next group
|
||||
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
||||
if (depth == 1) { // First group or starting new group
|
||||
itemnextp = NULL;
|
||||
AstIf* newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true), NULL, NULL);
|
||||
if (groupnextp) groupnextp->addElsesp(newp);
|
||||
else grouprootp = newp;
|
||||
groupnextp = newp;
|
||||
} else { // Continue group, modify if condition to OR in this new condition
|
||||
AstNode* condp = groupnextp->condp()->unlinkFrBack();
|
||||
groupnextp->condp(new AstOr(ifexprp->fileline(),
|
||||
condp,
|
||||
ifexprp->cloneTree(true)));
|
||||
}
|
||||
}
|
||||
{ // Make the new lower IF and attach in the tree
|
||||
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
|
||||
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
|
||||
itemexprp->deleteTree(); VL_DANGLING(itemexprp);
|
||||
itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
|
||||
}
|
||||
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
|
||||
if (itemnextp) itemnextp->addElsesp(newp);
|
||||
else groupnextp->addIfsp(newp); // First in a new group
|
||||
itemnextp = newp;
|
||||
}
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout," _comp_TREE: ");
|
||||
// Handle any assertions
|
||||
replaceCaseParallel(nodep, false);
|
||||
// Replace the CASE... with IF...
|
||||
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout," _new: ");
|
||||
if (grouprootp) nodep->replaceWith(grouprootp);
|
||||
else nodep->unlinkFrBack();
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
{ // Prepare for next group
|
||||
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
||||
if (depth == 1) { // First group or starting new group
|
||||
itemnextp = NULL;
|
||||
AstIf* newp = new AstIf(itemp->fileline(),
|
||||
ifexprp->cloneTree(true), NULL, NULL);
|
||||
if (groupnextp) groupnextp->addElsesp(newp);
|
||||
else grouprootp = newp;
|
||||
groupnextp = newp;
|
||||
} else { // Continue group, modify if condition to OR in this new condition
|
||||
AstNode* condp = groupnextp->condp()->unlinkFrBack();
|
||||
groupnextp->condp(new AstOr(ifexprp->fileline(),
|
||||
condp,
|
||||
ifexprp->cloneTree(true)));
|
||||
}
|
||||
}
|
||||
{ // Make the new lower IF and attach in the tree
|
||||
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
|
||||
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
|
||||
itemexprp->deleteTree(); VL_DANGLING(itemexprp);
|
||||
itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
|
||||
}
|
||||
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
|
||||
if (itemnextp) itemnextp->addElsesp(newp);
|
||||
else groupnextp->addIfsp(newp); // First in a new group
|
||||
itemnextp = newp;
|
||||
}
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout, " _comp_TREE: ");
|
||||
// Handle any assertions
|
||||
replaceCaseParallel(nodep, false);
|
||||
// Replace the CASE... with IF...
|
||||
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout, " _new: ");
|
||||
if (grouprootp) nodep->replaceWith(grouprootp);
|
||||
else nodep->unlinkFrBack();
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
|
||||
void replaceCaseParallel(AstCase* nodep, bool noOverlapsAllCovered) {
|
||||
// Take the notParallelp tree under the case statement created by V3Assert
|
||||
// If the statement was proven to have no overlaps and all cases covered, we're done with it.
|
||||
// Else, convert to a normal statement parallel with the case statement.
|
||||
if (nodep->notParallelp() && !noOverlapsAllCovered) {
|
||||
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(parp);
|
||||
}
|
||||
// Take the notParallelp tree under the case statement created by V3Assert
|
||||
// If the statement was proven to have no overlaps and all cases
|
||||
// covered, we're done with it.
|
||||
// Else, convert to a normal statement parallel with the case statement.
|
||||
if (nodep->notParallelp() && !noOverlapsAllCovered) {
|
||||
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(parp);
|
||||
}
|
||||
}
|
||||
|
||||
bool neverItem(AstCase* casep, AstConst* itemp) {
|
||||
// Xs in case or casez are impossible due to two state simulations
|
||||
if (casep->casex()) {
|
||||
} else if (casep->casez() || casep->caseInside()) {
|
||||
if (itemp->num().isUnknown()) return true;
|
||||
} else {
|
||||
if (itemp->num().isFourState()) return true;
|
||||
}
|
||||
return false;
|
||||
// Xs in case or casez are impossible due to two state simulations
|
||||
if (casep->casex()) {
|
||||
} else if (casep->casez() || casep->caseInside()) {
|
||||
if (itemp->num().isUnknown()) return true;
|
||||
} else {
|
||||
if (itemp->num().isFourState()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCase* nodep) {
|
||||
V3Case::caseLint(nodep);
|
||||
V3Case::caseLint(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
// we can make a tree of statements to avoid extra comparisons
|
||||
++m_statCaseFast;
|
||||
replaceCaseFast(nodep); VL_DANGLING(nodep);
|
||||
} else {
|
||||
++m_statCaseSlow;
|
||||
replaceCaseComplicated(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout, " case_old: ");
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
// we can make a tree of statements to avoid extra comparisons
|
||||
++m_statCaseFast;
|
||||
replaceCaseFast(nodep); VL_DANGLING(nodep);
|
||||
} else {
|
||||
++m_statCaseSlow;
|
||||
replaceCaseComplicated(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
@@ -468,15 +478,15 @@ public:
|
||||
explicit CaseVisitor(AstNetlist* nodep) {
|
||||
m_caseWidth = 0;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
|
||||
m_valueItem[i] = NULL;
|
||||
}
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseVisitor() {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -35,4 +35,4 @@ public:
|
||||
static void caseLint(AstNodeCase* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+72
-72
@@ -20,24 +20,24 @@
|
||||
// V3Cast's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// For each math operator, if above operator requires 32 bits,
|
||||
// and this isn't, cast to 32 bits.
|
||||
// Likewise for 64 bit operators.
|
||||
// For each math operator, if above operator requires 32 bits,
|
||||
// and this isn't, cast to 32 bits.
|
||||
// Likewise for 64 bit operators.
|
||||
//
|
||||
// C++ rules:
|
||||
// Integral promotions allow conversion to larger int. Unsigned is only
|
||||
// used if a int would not fit the value.
|
||||
// Integral promotions allow conversion to larger int. Unsigned is only
|
||||
// used if a int would not fit the value.
|
||||
//
|
||||
// Bools converts to int, not unsigned.
|
||||
// Bools converts to int, not unsigned.
|
||||
//
|
||||
// Most operations return unsigned if either operand is unsigned.
|
||||
// Most operations return unsigned if either operand is unsigned.
|
||||
//
|
||||
// Unsignedness can be lost on results of the below operations, as they
|
||||
// may need the sign bit for proper operation:
|
||||
// /, %, /=, %=, <, <=, >, or >=
|
||||
// Unsignedness can be lost on results of the below operations, as they
|
||||
// may need the sign bit for proper operation:
|
||||
// /, %, /=, %=, <, <=, >, or >=
|
||||
//
|
||||
// Signed values are always sign extended on promotion or right shift,
|
||||
// even if assigning to a unsigned.
|
||||
// Signed values are always sign extended on promotion or right shift,
|
||||
// even if assigning to a unsigned.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -58,8 +58,8 @@ class CastVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNode::user() // bool. Indicates node is of known size
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstNode::user() // bool. Indicates node is of known size
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
|
||||
@@ -67,97 +67,97 @@ private:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4," NeedCast "<<nodep<<endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
UINFO(4," NeedCast "<<nodep<<endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||
relinkHandle.relink(castp);
|
||||
//if (debug()>8) castp->dumpTree(cout,"-castins: ");
|
||||
//
|
||||
insureLower32Cast(castp);
|
||||
nodep->user1(1); // Now must be of known size
|
||||
relinkHandle.relink(castp);
|
||||
//if (debug()>8) castp->dumpTree(cout, "-castins: ");
|
||||
//
|
||||
insureLower32Cast(castp);
|
||||
nodep->user1(1); // Now must be of known size
|
||||
}
|
||||
int castSize(AstNode* nodep) {
|
||||
if (nodep->isQuad()) return VL_QUADSIZE;
|
||||
else if (nodep->width()<=8) return 8;
|
||||
else if (nodep->width()<=16) return 16;
|
||||
else return VL_WORDSIZE;
|
||||
if (nodep->isQuad()) return VL_QUADSIZE;
|
||||
else if (nodep->width()<=8) return 8;
|
||||
else if (nodep->width()<=16) return 16;
|
||||
else return VL_WORDSIZE;
|
||||
}
|
||||
void insureCast(AstNode* nodep) {
|
||||
if (castSize(nodep->backp()) != castSize(nodep)
|
||||
|| !nodep->user1()) {
|
||||
insertCast(nodep, castSize(nodep->backp()));
|
||||
}
|
||||
if (castSize(nodep->backp()) != castSize(nodep)
|
||||
|| !nodep->user1()) {
|
||||
insertCast(nodep, castSize(nodep->backp()));
|
||||
}
|
||||
}
|
||||
void insureLower32Cast(AstCCast* nodep) {
|
||||
// If we have uint64 = CAST(uint64(x)) then the upcasting
|
||||
// really needs to be CAST(uint64(CAST(uint32(x))).
|
||||
// Otherwise a (uint64)(a>b) would return wrong value, as
|
||||
// less than has undeterministic signedness.
|
||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
||||
// If we have uint64 = CAST(uint64(x)) then the upcasting
|
||||
// really needs to be CAST(uint64(CAST(uint32(x))).
|
||||
// Otherwise a (uint64)(a>b) would return wrong value, as
|
||||
// less than has undeterministic signedness.
|
||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
||||
&& !VN_IS(nodep->lhsp(), CCast)) {
|
||||
insertCast(nodep->lhsp(), VL_WORDSIZE);
|
||||
}
|
||||
insertCast(nodep->lhsp(), VL_WORDSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeUniop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1()
|
||||
| nodep->thsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1()
|
||||
| nodep->thsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
insureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstNegate* nodep) {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin()==1) {
|
||||
// We want to avoid a GCC "converting of negative value" warning
|
||||
// from our expansion of
|
||||
// out = {32{a<b}} => out = - (a<b)
|
||||
insertCast(nodep->lhsp(), castSize(nodep));
|
||||
} else {
|
||||
insureCast(nodep->lhsp());
|
||||
}
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin()==1) {
|
||||
// We want to avoid a GCC "converting of negative value" warning
|
||||
// from our expansion of
|
||||
// out = {32{a<b}} => out = - (a<b)
|
||||
insertCast(nodep->lhsp(), castSize(nodep));
|
||||
} else {
|
||||
insureCast(nodep->lhsp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (!nodep->lvalue()
|
||||
if (!nodep->lvalue()
|
||||
&& !VN_IS(nodep->backp(), CCast)
|
||||
&& VN_IS(nodep->backp(), NodeMath)
|
||||
&& !VN_IS(nodep->backp(), ArraySel)
|
||||
&& nodep->backp()->width()
|
||||
&& castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
// CData x=3; out = (QData)(x<<30);
|
||||
&& nodep->backp()->width()
|
||||
&& castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
// CData x=3; out = (QData)(x<<30);
|
||||
insertCast(nodep, castSize(nodep));
|
||||
}
|
||||
nodep->user1(1);
|
||||
}
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstConst* nodep) {
|
||||
// Constants are of unknown size if smaller than 33 bits, becase
|
||||
// we're too lazy to wrap every constant in the universe in
|
||||
// ((IData)#).
|
||||
nodep->user1(nodep->isQuad() || nodep->isWide());
|
||||
// Constants are of unknown size if smaller than 33 bits, becase
|
||||
// we're too lazy to wrap every constant in the universe in
|
||||
// ((IData)#).
|
||||
nodep->user1(nodep->isQuad() || nodep->isWide());
|
||||
}
|
||||
|
||||
// NOPs
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void castAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+427
-418
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void cdcAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+156
-146
@@ -20,13 +20,13 @@
|
||||
// V3Changed's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Each combo block
|
||||
// For each variable that comes from combo block and is generated AFTER a usage
|
||||
// Add __Vlast_{var} to local section, init to current value (just use array?)
|
||||
// Change = if any var != last.
|
||||
// If a signal is used as a clock in this module or any
|
||||
// module *below*, and it isn't a input to this module,
|
||||
// we need to indicate a new clock has been created.
|
||||
// Each combo block
|
||||
// For each variable that comes from combo block and is generated AFTER a usage
|
||||
// Add __Vlast_{var} to local section, init to current value (just use array?)
|
||||
// Change = if any var != last.
|
||||
// If a signal is used as a clock in this module or any
|
||||
// module *below*, and it isn't a input to this module,
|
||||
// we need to indicate a new clock has been created.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -47,56 +47,60 @@
|
||||
class ChangedState {
|
||||
public:
|
||||
// STATE
|
||||
AstNodeModule* m_topModp; // Top module
|
||||
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
||||
AstCFunc* m_chgFuncp; // Change function we're building
|
||||
AstCFunc* m_tlChgFuncp; // Top level change function we're building
|
||||
int m_numStmts; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum; // Number of change functions emitted
|
||||
AstNodeModule* m_topModp; // Top module
|
||||
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
||||
AstCFunc* m_chgFuncp; // Change function we're building
|
||||
AstCFunc* m_tlChgFuncp; // Top level change function we're building
|
||||
int m_numStmts; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum; // Number of change functions emitted
|
||||
|
||||
ChangedState() {
|
||||
m_topModp = NULL;
|
||||
m_chgFuncp = NULL;
|
||||
m_scopetopp = NULL;
|
||||
m_tlChgFuncp = NULL;
|
||||
m_numStmts = 0;
|
||||
m_funcNum = 0;
|
||||
m_topModp = NULL;
|
||||
m_chgFuncp = NULL;
|
||||
m_scopetopp = NULL;
|
||||
m_tlChgFuncp = NULL;
|
||||
m_numStmts = 0;
|
||||
m_funcNum = 0;
|
||||
}
|
||||
~ChangedState() {}
|
||||
|
||||
void maybeCreateChgFuncp() {
|
||||
// Don't create an extra function call if splitting is disabled
|
||||
if (!v3Global.opt.outputSplitCFuncs()) {
|
||||
m_chgFuncp = m_tlChgFuncp;
|
||||
return;
|
||||
}
|
||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_chgFuncp = new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), m_scopetopp, "QData");
|
||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_chgFuncp->symProlog(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_chgFuncp);
|
||||
// Don't create an extra function call if splitting is disabled
|
||||
if (!v3Global.opt.outputSplitCFuncs()) {
|
||||
m_chgFuncp = m_tlChgFuncp;
|
||||
return;
|
||||
}
|
||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_chgFuncp = new AstCFunc(m_scopetopp->fileline(),
|
||||
"_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData");
|
||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_chgFuncp->symProlog(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_chgFuncp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
||||
callp->argTypes("vlSymsp");
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
||||
callp->argTypes("vlSymsp");
|
||||
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
} else {
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
} else {
|
||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the evaluation if
|
||||
// any function returns true. This is likely what we want and is similar to the logic already in use
|
||||
// inside V3EmitC, however, it also means that verbose logging may miss to print change detect variables.
|
||||
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
|
||||
new AstLogOr(m_scopetopp->fileline(), callp,
|
||||
returnp->lhsp()->unlinkFrBack()));
|
||||
returnp->replaceWith(newp);
|
||||
returnp->deleteTree(); VL_DANGLING(returnp);
|
||||
}
|
||||
m_numStmts = 0;
|
||||
}
|
||||
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the
|
||||
// evaluation if any function returns true. This is likely what
|
||||
// we want and is similar to the logic already in use inside
|
||||
// V3EmitC, however, it also means that verbose logging may
|
||||
// miss to print change detect variables.
|
||||
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
|
||||
new AstLogOr(m_scopetopp->fileline(), callp,
|
||||
returnp->lhsp()->unlinkFrBack()));
|
||||
returnp->replaceWith(newp);
|
||||
returnp->deleteTree(); VL_DANGLING(returnp);
|
||||
}
|
||||
m_numStmts = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,82 +110,84 @@ public:
|
||||
class ChangedInsertVisitor : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
||||
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
|
||||
AstNode* m_varEqnp; // Original var's equation to get var value
|
||||
AstNode* m_newLvEqnp; // New var's equation to read value
|
||||
AstNode* m_newRvEqnp; // New var's equation to set value
|
||||
uint32_t m_detects; // # detects created
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
||||
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
|
||||
AstNode* m_varEqnp; // Original var's equation to get var value
|
||||
AstNode* m_newLvEqnp; // New var's equation to read value
|
||||
AstNode* m_newRvEqnp; // New var's equation to set value
|
||||
uint32_t m_detects; // # detects created
|
||||
|
||||
// CONSTANTS
|
||||
enum MiscConsts {
|
||||
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
||||
// Ok to increase this, but may result in much slower model
|
||||
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
||||
// Ok to increase this, but may result in much slower model
|
||||
};
|
||||
|
||||
void newChangeDet() {
|
||||
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<<cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||
<<" array indexes (probably with UNOPTFLAT warning suppressed): "<<m_vscp->prettyName()<<endl
|
||||
<<m_vscp->warnMore()
|
||||
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
|
||||
return;
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "
|
||||
<<cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||
<<" array indexes (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->prettyName()<<endl
|
||||
<<m_vscp->warnMore()
|
||||
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
|
||||
return;
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(),
|
||||
m_varEqnp->cloneTree(true),
|
||||
m_newRvEqnp->cloneTree(true), false);
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* initp = new AstAssign(m_vscp->fileline(),
|
||||
m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true));
|
||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||
EmitCBaseCounterVisitor visitor(initp);
|
||||
m_statep->m_numStmts += visitor.count();
|
||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||
EmitCBaseCounterVisitor visitor(initp);
|
||||
m_statep->m_numStmts += visitor.count();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType* nodep) {
|
||||
newChangeDet();
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstPackArrayDType* nodep) {
|
||||
newChangeDet();
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstUnpackArrayDType* nodep) {
|
||||
for (int index=0; index < nodep->elementsConst(); ++index) {
|
||||
AstNode* origVEp = m_varEqnp;
|
||||
AstNode* origNLEp = m_newLvEqnp;
|
||||
AstNode* origNREp = m_newRvEqnp;
|
||||
for (int index=0; index < nodep->elementsConst(); ++index) {
|
||||
AstNode* origVEp = m_varEqnp;
|
||||
AstNode* origNLEp = m_newLvEqnp;
|
||||
AstNode* origNREp = m_newRvEqnp;
|
||||
|
||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
||||
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
||||
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
|
||||
iterate(nodep->subDTypep()->skipRefp());
|
||||
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
|
||||
m_varEqnp = origVEp;
|
||||
m_newLvEqnp = origNLEp;
|
||||
m_newRvEqnp = origNREp;
|
||||
}
|
||||
m_varEqnp = origVEp;
|
||||
m_newLvEqnp = origNLEp;
|
||||
m_newRvEqnp = origNREp;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeClassDType* nodep) {
|
||||
if (nodep->packedUnsup()) {
|
||||
newChangeDet();
|
||||
} else {
|
||||
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-class-");
|
||||
if (nodep->packedUnsup()) {
|
||||
newChangeDet();
|
||||
} else {
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->varp()->prettyName());
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-general-");
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->varp()->prettyName());
|
||||
@@ -189,29 +195,31 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
||||
m_statep = statep;
|
||||
m_vscp = vscp;
|
||||
m_detects = 0;
|
||||
{
|
||||
AstVar* varp = m_vscp->varp();
|
||||
string newvarname = "__Vchglast__"+m_vscp->scopep()->nameDotless()+"__"+varp->shortName();
|
||||
// Create: VARREF(_last)
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
||||
m_statep->m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
m_statep = statep;
|
||||
m_vscp = vscp;
|
||||
m_detects = 0;
|
||||
{
|
||||
AstVar* varp = m_vscp->varp();
|
||||
string newvarname = ("__Vchglast__"+m_vscp->scopep()->nameDotless()
|
||||
+"__"+varp->shortName());
|
||||
// Create: VARREF(_last)
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP,
|
||||
newvarname, varp);
|
||||
m_statep->m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
|
||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
||||
}
|
||||
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
|
||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
||||
}
|
||||
iterate(vscp->dtypep()->skipRefp());
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
}
|
||||
virtual ~ChangedInsertVisitor() {}
|
||||
};
|
||||
@@ -223,61 +231,63 @@ class ChangedVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1() -> bool. True indicates processed
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstVarScope::user1() -> bool. True indicates processed
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
|
||||
ChangedInsertVisitor visitor (vscp, m_statep);
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<<vscp->prettyName());
|
||||
ChangedInsertVisitor visitor (vscp, m_statep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
if (nodep->isTop()) {
|
||||
m_statep->m_topModp = nodep;
|
||||
}
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
if (nodep->isTop()) {
|
||||
m_statep->m_topModp = nodep;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
UINFO(4," TS "<<nodep<<endl);
|
||||
// Clearing
|
||||
AstNode::user1ClearTree();
|
||||
// Create the change detection function
|
||||
AstScope* scopep = nodep->scopep();
|
||||
if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
UINFO(4," TS "<<nodep<<endl);
|
||||
// Clearing
|
||||
AstNode::user1ClearTree();
|
||||
// Create the change detection function
|
||||
AstScope* scopep = nodep->scopep();
|
||||
if (!scopep) nodep->v3fatalSrc("No scope found on top level,"
|
||||
" perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
|
||||
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_statep->m_tlChgFuncp->symProlog(true);
|
||||
m_statep->m_tlChgFuncp->declPrivate(true);
|
||||
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(),
|
||||
"_change_request", scopep, "QData");
|
||||
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_statep->m_tlChgFuncp->symProlog(true);
|
||||
m_statep->m_tlChgFuncp->declPrivate(true);
|
||||
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
if (nodep->isCircular()) {
|
||||
UINFO(8," CIRC "<<nodep<<endl);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
genChangeDet(nodep);
|
||||
}
|
||||
}
|
||||
if (nodep->isCircular()) {
|
||||
UINFO(8," CIRC "<<nodep<<endl);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
genChangeDet(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeMath* nodep) {
|
||||
// Short-circuit
|
||||
// Short-circuit
|
||||
}
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
@@ -288,7 +298,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
|
||||
m_statep = statep;
|
||||
m_statep = statep;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ChangedVisitor() {}
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void changedAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+108
-104
@@ -20,10 +20,10 @@
|
||||
// V3Clean's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// For each math operator, if it requires a clean operand,
|
||||
// and the operand is dirty, insert a CLEAN node.
|
||||
// Resize operands to C++ 32/64/wide types.
|
||||
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
|
||||
// For each math operator, if it requires a clean operand,
|
||||
// and the operand is dirty, insert a CLEAN node.
|
||||
// Resize operands to C++ 32/64/wide types.
|
||||
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -44,12 +44,12 @@ class CleanVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
||||
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
||||
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// TYPES
|
||||
enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
|
||||
@@ -62,153 +62,153 @@ private:
|
||||
|
||||
// Width resetting
|
||||
int cppWidth(AstNode* nodep) {
|
||||
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
|
||||
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
|
||||
else return nodep->widthWords()*VL_WORDSIZE;
|
||||
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
|
||||
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
|
||||
else return nodep->widthWords()*VL_WORDSIZE;
|
||||
}
|
||||
void setCppWidth(AstNode* nodep) {
|
||||
nodep->user2(true); // Don't resize it again
|
||||
AstNodeDType* old_dtypep = nodep->dtypep();
|
||||
int width=cppWidth(nodep); // widthMin is unchanged
|
||||
if (old_dtypep->width() != width) {
|
||||
// Since any given dtype's cppWidth() is the same, we can just
|
||||
// remember one convertion for each, and reuse it
|
||||
nodep->user2(true); // Don't resize it again
|
||||
AstNodeDType* old_dtypep = nodep->dtypep();
|
||||
int width = cppWidth(nodep); // widthMin is unchanged
|
||||
if (old_dtypep->width() != width) {
|
||||
// Since any given dtype's cppWidth() is the same, we can just
|
||||
// remember one convertion for each, and reuse it
|
||||
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
||||
nodep->dtypep(new_dtypep);
|
||||
} else {
|
||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
||||
if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed");
|
||||
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
||||
}
|
||||
}
|
||||
nodep->dtypep(new_dtypep);
|
||||
} else {
|
||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
||||
if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed");
|
||||
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
||||
}
|
||||
}
|
||||
}
|
||||
void computeCppWidth(AstNode* nodep) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
||||
} else {
|
||||
setCppWidth(nodep);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setCppWidth(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the clean state in the userp on each node
|
||||
void setCleanState(AstNode* nodep, CleanState clean) {
|
||||
nodep->user1(clean);
|
||||
nodep->user1(clean);
|
||||
}
|
||||
CleanState getCleanState(AstNode* nodep) {
|
||||
return static_cast<CleanState>(nodep->user1());
|
||||
}
|
||||
bool isClean(AstNode* nodep) {
|
||||
CleanState clstate = getCleanState(nodep);
|
||||
if (clstate==CS_CLEAN) return true;
|
||||
if (clstate==CS_DIRTY) return false;
|
||||
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
|
||||
return false;
|
||||
CleanState clstate = getCleanState(nodep);
|
||||
if (clstate==CS_CLEAN) return true;
|
||||
if (clstate==CS_DIRTY) return false;
|
||||
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
|
||||
return false;
|
||||
}
|
||||
void setClean(AstNode* nodep, bool isClean) {
|
||||
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
||||
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); //32,64,...
|
||||
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
|
||||
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); // 32,64,...
|
||||
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
|
||||
}
|
||||
|
||||
// Operate on nodes
|
||||
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
|
||||
UINFO(4," NeedClean "<<nodep<<endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
computeCppWidth(nodep);
|
||||
V3Number mask (nodep->fileline(), cppWidth(nodep));
|
||||
mask.setMask(nodep->widthMin());
|
||||
UINFO(4," NeedClean "<<nodep<<endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
computeCppWidth(nodep);
|
||||
V3Number mask (nodep, cppWidth(nodep));
|
||||
mask.setMask(nodep->widthMin());
|
||||
AstNode* cleanp = new AstAnd(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), mask),
|
||||
nodep);
|
||||
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
||||
relinkHandle.relink(cleanp);
|
||||
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
||||
relinkHandle.relink(cleanp);
|
||||
}
|
||||
void insureClean(AstNode* nodep) {
|
||||
computeCppWidth(nodep);
|
||||
if (!isClean(nodep)) insertClean(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (!isClean(nodep)) insertClean(nodep);
|
||||
}
|
||||
void insureCleanAndNext(AstNode* nodep) {
|
||||
// Editing list, careful looping!
|
||||
for (AstNode* exprp = nodep; exprp; ) {
|
||||
AstNode* nextp = exprp->nextp();
|
||||
insureClean(exprp);
|
||||
exprp = nextp;
|
||||
}
|
||||
// Editing list, careful looping!
|
||||
for (AstNode* exprp = nodep; exprp; ) {
|
||||
AstNode* nextp = exprp->nextp();
|
||||
insureClean(exprp);
|
||||
exprp = nextp;
|
||||
}
|
||||
}
|
||||
|
||||
// Base type handling methods
|
||||
void operandBiop(AstNodeBiop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
//no setClean.. must do it in each user routine.
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
//no setClean.. must do it in each user routine.
|
||||
}
|
||||
void operandTriop(AstNodeTriop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
if (nodep->cleanThs()) {
|
||||
insureClean(nodep->thsp());
|
||||
}
|
||||
//no setClean.. must do it in each user routine.
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
if (nodep->cleanThs()) {
|
||||
insureClean(nodep->thsp());
|
||||
}
|
||||
//no setClean.. must do it in each user routine.
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeUniop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) {
|
||||
operandBiop(nodep);
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstAnd* nodep) {
|
||||
operandBiop(nodep);
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstXor* nodep) {
|
||||
operandBiop(nodep);
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstOr* nodep) {
|
||||
operandBiop(nodep);
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstNodeMath* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstText* nodep) {
|
||||
setClean(nodep, true);
|
||||
@@ -217,50 +217,54 @@ private:
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
operandTriop(nodep);
|
||||
operandTriop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean(nodep, false);
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
if (!VN_IS(nodep->backp(), And)) {
|
||||
insertClean(nodep);
|
||||
}
|
||||
insertClean(nodep);
|
||||
}
|
||||
insureCleanAndNext(nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstTraceDecl* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->valuep());
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
// No cleaning, or would loose pointer to enum
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
// No cleaning, or would loose pointer to enum
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Control flow operators
|
||||
virtual void visit(AstNodeCond* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
||||
insureClean(nodep->condp());
|
||||
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
insureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
insureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->exprsp());
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) {
|
||||
iterateChildren(nodep);
|
||||
@@ -276,7 +280,7 @@ private:
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
computeCppWidth(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void cleanAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+573
-570
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void clkGaterAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+271
-266
@@ -22,11 +22,11 @@
|
||||
// Top Scope:
|
||||
// Check created ACTIVEs
|
||||
// Compress adjacent ACTIVEs with same sensitivity list
|
||||
// Form master _eval function
|
||||
// Add around the SENTREE a (IF POSEDGE(..))
|
||||
// Add a __Vlast_{clock} for the comparison
|
||||
// Set the __Vlast_{clock} at the end of the block
|
||||
// Replace UNTILSTABLEs with loops until specified signals become const.
|
||||
// Form master _eval function
|
||||
// Add around the SENTREE a (IF POSEDGE(..))
|
||||
// Add a __Vlast_{clock} for the comparison
|
||||
// Set the __Vlast_{clock} at the end of the block
|
||||
// Replace UNTILSTABLEs with loops until specified signals become const.
|
||||
// Create global calling function for any per-scope functions. (For FINALs).
|
||||
//
|
||||
//*************************************************************************
|
||||
@@ -49,22 +49,22 @@ class ClockVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Cleared each Module:
|
||||
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
|
||||
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstTopScope* m_topScopep; // Current top scope
|
||||
AstScope* m_scopep; // Current scope
|
||||
AstCFunc* m_evalFuncp; // Top eval function we are creating
|
||||
AstCFunc* m_initFuncp; // Top initial function we are creating
|
||||
AstCFunc* m_finalFuncp; // Top final function we are creating
|
||||
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
||||
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
||||
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstTopScope* m_topScopep; // Current top scope
|
||||
AstScope* m_scopep; // Current scope
|
||||
AstCFunc* m_evalFuncp; // Top eval function we are creating
|
||||
AstCFunc* m_initFuncp; // Top initial function we are creating
|
||||
AstCFunc* m_finalFuncp; // Top final function we are creating
|
||||
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
||||
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
||||
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
||||
AstMTaskBody* m_mtaskBodyp; // Current mtask body
|
||||
|
||||
// METHODS
|
||||
@@ -72,15 +72,18 @@ private:
|
||||
|
||||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||
AstVar* varp = vscp->varp();
|
||||
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<<varp->prettyName());
|
||||
string newvarname = (string("__Vclklast__")+vscp->scopep()->nameDotless()+"__"+varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
|
||||
AstVar* varp = vscp->varp();
|
||||
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "
|
||||
<<varp->prettyName());
|
||||
string newvarname = (string("__Vclklast__")
|
||||
+vscp->scopep()->nameDotless()+"__"+varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(),
|
||||
AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
m_modp->addStmtp(newvarp);
|
||||
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarp(newvscp);
|
||||
m_modp->addStmtp(newvarp);
|
||||
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarp(newvscp);
|
||||
// Add init
|
||||
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false);
|
||||
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
||||
@@ -88,243 +91,245 @@ private:
|
||||
new AstVarRef(newvarp->fileline(), newvscp, true),
|
||||
fromp);
|
||||
addToInitial(newinitp);
|
||||
// At bottom, assign them
|
||||
AstAssign* finalp
|
||||
// At bottom, assign them
|
||||
AstAssign* finalp
|
||||
= new AstAssign(vscp->fileline(),
|
||||
new AstVarRef(vscp->fileline(), newvscp, true),
|
||||
new AstVarRef(vscp->fileline(), vscp, false));
|
||||
m_evalFuncp->addFinalsp(finalp);
|
||||
//
|
||||
UINFO(4,"New Last: "<<newvscp<<endl);
|
||||
return newvscp;
|
||||
m_evalFuncp->addFinalsp(finalp);
|
||||
//
|
||||
UINFO(4,"New Last: "<<newvscp<<endl);
|
||||
return newvscp;
|
||||
}
|
||||
AstNode* createSenItemEquation(AstSenItem* nodep) {
|
||||
// We know the var is clean, and one bit, so we use binary ops
|
||||
// for speed instead of logical ops.
|
||||
// POSEDGE: var & ~var_last
|
||||
// NEGEDGE: ~var & var_last
|
||||
// BOTHEDGE: var ^ var_last
|
||||
// HIGHEDGE: var
|
||||
// LOWEDGE: ~var
|
||||
AstNode* newp = NULL;
|
||||
if (nodep->edgeType()==AstEdgeType::ET_ILLEGAL) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
|
||||
if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
lastVscp, false)));
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(nodep->fileline(),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false)),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstXor(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) {
|
||||
newp = new AstVarRef(nodep->fileline(),
|
||||
clkvscp, false);
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) {
|
||||
newp = new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
clkvscp, false));
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad edge type");
|
||||
}
|
||||
return newp;
|
||||
// We know the var is clean, and one bit, so we use binary ops
|
||||
// for speed instead of logical ops.
|
||||
// POSEDGE: var & ~var_last
|
||||
// NEGEDGE: ~var & var_last
|
||||
// BOTHEDGE: var ^ var_last
|
||||
// HIGHEDGE: var
|
||||
// LOWEDGE: ~var
|
||||
AstNode* newp = NULL;
|
||||
if (nodep->edgeType()==AstEdgeType::ET_ILLEGAL) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
|
||||
if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
lastVscp, false)));
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(nodep->fileline(),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false)),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstXor(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) {
|
||||
newp = new AstVarRef(nodep->fileline(),
|
||||
clkvscp, false);
|
||||
} else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) {
|
||||
newp = new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
clkvscp, false));
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad edge type");
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
AstNode* createSenGateEquation(AstSenGate* nodep) {
|
||||
AstNode* newp = new AstAnd(nodep->fileline(),
|
||||
createSenseEquation(nodep->sensesp()),
|
||||
nodep->rhsp()->cloneTree(true));
|
||||
return newp;
|
||||
AstNode* newp = new AstAnd(nodep->fileline(),
|
||||
createSenseEquation(nodep->sensesp()),
|
||||
nodep->rhsp()->cloneTree(true));
|
||||
return newp;
|
||||
}
|
||||
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
|
||||
// Nodep may be a list of elements; we need to walk it
|
||||
AstNode* senEqnp = NULL;
|
||||
for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
AstNode* senOnep = NULL;
|
||||
// Nodep may be a list of elements; we need to walk it
|
||||
AstNode* senEqnp = NULL;
|
||||
for (AstNodeSenItem* senp = nodesp; senp; senp = VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
AstNode* senOnep = NULL;
|
||||
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
|
||||
senOnep = createSenItemEquation(itemp);
|
||||
senOnep = createSenItemEquation(itemp);
|
||||
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
|
||||
senOnep = createSenGateEquation(itemp);
|
||||
} else {
|
||||
senp->v3fatalSrc("Strange node under sentree");
|
||||
}
|
||||
if (senEqnp) {
|
||||
// Add new OR to the sensitivity list equation
|
||||
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
|
||||
} else {
|
||||
senEqnp = senOnep;
|
||||
}
|
||||
}
|
||||
return senEqnp;
|
||||
senOnep = createSenGateEquation(itemp);
|
||||
} else {
|
||||
senp->v3fatalSrc("Strange node under sentree");
|
||||
}
|
||||
if (senEqnp) {
|
||||
// Add new OR to the sensitivity list equation
|
||||
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
|
||||
} else {
|
||||
senEqnp = senOnep;
|
||||
}
|
||||
}
|
||||
return senEqnp;
|
||||
}
|
||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
|
||||
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
|
||||
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL);
|
||||
return (newifp);
|
||||
return (newifp);
|
||||
}
|
||||
void clearLastSen() {
|
||||
m_lastSenp = NULL;
|
||||
m_lastIfp = NULL;
|
||||
m_lastSenp = NULL;
|
||||
m_lastIfp = NULL;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
UINFO(4," TOPSCOPE "<<nodep<<endl);
|
||||
m_topScopep=nodep;
|
||||
m_scopep = nodep->scopep();
|
||||
if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
|
||||
//VV***** We reset all user1p()
|
||||
AstNode::user1ClearTree();
|
||||
// Make top functions
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_evalFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_initFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep);
|
||||
funcp->skipDecl(true);
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->entryPoint(true);
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline(),
|
||||
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
m_scopep->addActivep(funcp);
|
||||
m_finalFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_settleFuncp = funcp;
|
||||
}
|
||||
// Process the activates
|
||||
UINFO(4," TOPSCOPE "<<nodep<<endl);
|
||||
m_topScopep = nodep;
|
||||
m_scopep = nodep->scopep();
|
||||
if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
|
||||
//VV***** We reset all user1p()
|
||||
AstNode::user1ClearTree();
|
||||
// Make top functions
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_evalFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_initFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep);
|
||||
funcp->skipDecl(true);
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->entryPoint(true);
|
||||
funcp->addInitsp(new AstCStmt
|
||||
(nodep->fileline(),
|
||||
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline(),
|
||||
EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
m_scopep->addActivep(funcp);
|
||||
m_finalFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_settleFuncp = funcp;
|
||||
}
|
||||
// Process the activates
|
||||
iterateChildren(nodep);
|
||||
// Done, clear so we can detect errors
|
||||
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
||||
clearLastSen();
|
||||
m_topScopep=NULL;
|
||||
m_scopep = NULL;
|
||||
// Done, clear so we can detect errors
|
||||
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
||||
clearLastSen();
|
||||
m_topScopep = NULL;
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
//UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
//UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_modp= NULL;
|
||||
m_modp= NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
//UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
//UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
|
||||
movep->unlinkFrBackWithNext();
|
||||
m_evalFuncp->addFinalsp(movep);
|
||||
}
|
||||
m_scopep = NULL;
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
|
||||
movep->unlinkFrBackWithNext();
|
||||
m_evalFuncp->addFinalsp(movep);
|
||||
}
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
||||
nodep->replaceWith(cmtp);
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
cmtp->addNextHere(stmtsp);
|
||||
}
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
||||
nodep->replaceWith(cmtp);
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
cmtp->addNextHere(stmtsp);
|
||||
}
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstAlwaysPost* nodep) {
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
||||
nodep->replaceWith(cmtp);
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
cmtp->addNextHere(stmtsp);
|
||||
}
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
||||
nodep->replaceWith(cmtp);
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
cmtp->addNextHere(stmtsp);
|
||||
}
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) {
|
||||
//nodep->dumpTree(cout,"ct:");
|
||||
//COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||
AstNode* incp = nodep->incp()->unlinkFrBack();
|
||||
AstNode* origp = nodep->origp()->unlinkFrBack();
|
||||
AstNode* changep = nodep->changep()->unlinkFrBack();
|
||||
AstIf* newp = new AstIf(nodep->fileline(),
|
||||
new AstXor(nodep->fileline(),
|
||||
origp,
|
||||
changep),
|
||||
incp, NULL);
|
||||
// We could add another IF to detect posedges, and only increment if so.
|
||||
// It's another whole branch though verus a potential memory miss.
|
||||
// We'll go with the miss.
|
||||
newp->addIfsp(new AstAssign(nodep->fileline(),
|
||||
changep->cloneTree(false),
|
||||
origp->cloneTree(false)));
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
//nodep->dumpTree(cout, "ct:");
|
||||
//COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||
AstNode* incp = nodep->incp()->unlinkFrBack();
|
||||
AstNode* origp = nodep->origp()->unlinkFrBack();
|
||||
AstNode* changep = nodep->changep()->unlinkFrBack();
|
||||
AstIf* newp = new AstIf(nodep->fileline(),
|
||||
new AstXor(nodep->fileline(),
|
||||
origp,
|
||||
changep),
|
||||
incp, NULL);
|
||||
// We could add another IF to detect posedges, and only increment if so.
|
||||
// It's another whole branch though verus a potential memory miss.
|
||||
// We'll go with the miss.
|
||||
newp->addIfsp(new AstAssign(nodep->fileline(),
|
||||
changep->cloneTree(false),
|
||||
origp->cloneTree(false)));
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
||||
nodep->replaceWith(cmtp);
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
cmtp->addNextHere(stmtsp);
|
||||
}
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
||||
nodep->replaceWith(cmtp);
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
cmtp->addNextHere(stmtsp);
|
||||
}
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
iterateChildren(nodep);
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) {
|
||||
// Delete it later; Actives still pointing to it
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep);
|
||||
// Delete it later; Actives still pointing to it
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep);
|
||||
}
|
||||
void addToEvalLoop(AstNode* stmtsp) {
|
||||
m_evalFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
@@ -336,13 +341,13 @@ private:
|
||||
m_initFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
||||
// Need to save and restore any member state in AstUntilStable block
|
||||
if (!m_topScopep || !nodep->stmtsp()) {
|
||||
// Not at the top or empty block...
|
||||
// Only empty blocks should be leftover on the non-top. Killem.
|
||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
||||
// Need to save and restore any member state in AstUntilStable block
|
||||
if (!m_topScopep || !nodep->stmtsp()) {
|
||||
// Not at the top or empty block...
|
||||
// Only empty blocks should be leftover on the non-top. Killem.
|
||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (m_mtaskBodyp) {
|
||||
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
@@ -367,39 +372,39 @@ private:
|
||||
m_mtaskBodyp->addStmtsp(stmtsp);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
// Remember the latest sensitivity so we can compare it next time
|
||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
} else {
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
// Make a new if statement
|
||||
m_lastIfp = makeActiveIf(m_lastSenp);
|
||||
addToEvalLoop(m_lastIfp);
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
} else if (nodep->hasInitial()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
addToInitial(stmtsp);
|
||||
} else if (nodep->hasSettle()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
addToSettleLoop(stmtsp);
|
||||
} else {
|
||||
// Combo
|
||||
clearLastSen();
|
||||
// Move statements to function
|
||||
addToEvalLoop(stmtsp);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
} else {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
// Remember the latest sensitivity so we can compare it next time
|
||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
} else {
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
// Make a new if statement
|
||||
m_lastIfp = makeActiveIf(m_lastSenp);
|
||||
addToEvalLoop(m_lastIfp);
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
} else if (nodep->hasInitial()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
addToInitial(stmtsp);
|
||||
} else if (nodep->hasSettle()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
addToSettleLoop(stmtsp);
|
||||
} else {
|
||||
// Combo
|
||||
clearLastSen();
|
||||
// Move statements to function
|
||||
addToEvalLoop(stmtsp);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) {
|
||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
|
||||
@@ -426,16 +431,16 @@ public:
|
||||
// CONSTUCTORS
|
||||
explicit ClockVisitor(AstNetlist* nodep) {
|
||||
m_modp = NULL;
|
||||
m_evalFuncp = NULL;
|
||||
m_evalFuncp = NULL;
|
||||
m_initFuncp = NULL;
|
||||
m_finalFuncp = NULL;
|
||||
m_settleFuncp = NULL;
|
||||
m_topScopep = NULL;
|
||||
m_lastSenp = NULL;
|
||||
m_lastIfp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_lastIfp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_mtaskBodyp = NULL;
|
||||
//
|
||||
//
|
||||
iterate(nodep);
|
||||
// Allow downstream modules to find _eval()
|
||||
// easily without iterating through the tree.
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void clockAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+275
-270
@@ -19,18 +19,18 @@
|
||||
//*************************************************************************
|
||||
// V3Combine's Transformations:
|
||||
//
|
||||
// For every function that we spit out
|
||||
// Examine code to find largest common blocks
|
||||
// Hash each node depth first
|
||||
// Hash includes varp name and operator type, and constants
|
||||
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
||||
// GO through table
|
||||
// Lookup in hash, while next of each statement match, grow that common block
|
||||
// Foreach common block
|
||||
// If common block large enough (> 20 statements) & used 2x or more
|
||||
// Make new function
|
||||
// Move common block to function
|
||||
// Replace each common block ref with funccall
|
||||
// For every function that we spit out
|
||||
// Examine code to find largest common blocks
|
||||
// Hash each node depth first
|
||||
// Hash includes varp name and operator type, and constants
|
||||
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
||||
// GO through table
|
||||
// Lookup in hash, while next of each statement match, grow that common block
|
||||
// Foreach common block
|
||||
// If common block large enough (> 20 statements) & used 2x or more
|
||||
// Make new function
|
||||
// Move common block to function
|
||||
// Replace each common block ref with funccall
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
//######################################################################
|
||||
|
||||
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
|
||||
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -79,57 +79,59 @@ class CombCallVisitor : CombBaseVisitor {
|
||||
// Find all CCALLS of each CFUNC, so that we can later rename them
|
||||
private:
|
||||
// NODE STATE
|
||||
bool m_find; // Find mode vs. delete mode
|
||||
bool m_find; // Find mode vs. delete mode
|
||||
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
if (oldfuncp==newfuncp) return;
|
||||
if (newfuncp) {
|
||||
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
||||
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
||||
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (!callp->user3()) { // !already done
|
||||
UINFO(4, " Called "<<callp<<endl);
|
||||
if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
AstCCall* newp = new AstCCall(callp, newfuncp);
|
||||
// Special new AstCCall form above transfers children of callp to newfuncp
|
||||
callp->replaceWith(newp);
|
||||
addCall(newp); // Fix the table
|
||||
} else { // Just deleting empty function
|
||||
callp->unlinkFrBack();
|
||||
}
|
||||
callp->user3(true); // Dead now
|
||||
pushDeletep(callp); VL_DANGLING(callp);
|
||||
m_callMmap.erase(eqit); // Fix the table
|
||||
}
|
||||
}
|
||||
if (oldfuncp==newfuncp) return;
|
||||
if (newfuncp) {
|
||||
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
||||
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
||||
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange
|
||||
= m_callMmap.equal_range(oldfuncp);
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (!callp->user3()) { // !already done
|
||||
UINFO(4, " Called "<<callp<<endl);
|
||||
if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
AstCCall* newp = new AstCCall(callp, newfuncp);
|
||||
// Special new AstCCall form above transfers children of callp to newfuncp
|
||||
callp->replaceWith(newp);
|
||||
addCall(newp); // Fix the table
|
||||
} else { // Just deleting empty function
|
||||
callp->unlinkFrBack();
|
||||
}
|
||||
callp->user3(true); // Dead now
|
||||
pushDeletep(callp); VL_DANGLING(callp);
|
||||
m_callMmap.erase(eqit); // Fix the table
|
||||
}
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
void addCall(AstCCall* nodep) {
|
||||
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
||||
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
||||
}
|
||||
void deleteCall(AstCCall* nodep) {
|
||||
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (callp==nodep) {
|
||||
m_callMmap.erase(eqit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nodep->v3fatalSrc("deleteCall node not found in table");
|
||||
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange
|
||||
= m_callMmap.equal_range(nodep->funcp());
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (callp==nodep) {
|
||||
m_callMmap.erase(eqit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nodep->v3fatalSrc("deleteCall node not found in table");
|
||||
}
|
||||
private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
addCall(nodep);
|
||||
addCall(nodep);
|
||||
}
|
||||
// Speed things up
|
||||
virtual void visit(AstNodeAssign* nodep) {}
|
||||
@@ -140,7 +142,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
CombCallVisitor() {
|
||||
m_find = false;
|
||||
m_find = false;
|
||||
}
|
||||
virtual ~CombCallVisitor() {}
|
||||
void main(AstNetlist* nodep) {
|
||||
@@ -155,10 +157,10 @@ class CombMarkVisitor : CombBaseVisitor {
|
||||
// Mark all nodes under specified one.
|
||||
private:
|
||||
// OUTPUT:
|
||||
// AstNode::user3() -> bool. True to indicate duplicated
|
||||
// AstNode::user3() -> bool. True to indicate duplicated
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->user3(true);
|
||||
nodep->user3(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
@@ -176,273 +178,276 @@ class CombineVisitor : CombBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNodeStmt::user() -> bool. True if iterated already
|
||||
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
||||
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser3InUse m_inuser3;
|
||||
//AstUser4InUse part of V3Hashed
|
||||
// AstNodeStmt::user() -> bool. True if iterated already
|
||||
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
||||
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser3InUse m_inuser3;
|
||||
//AstUser4InUse part of V3Hashed
|
||||
|
||||
// STATE
|
||||
typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState;
|
||||
V3Double0 m_statCombs; // Statistic tracking
|
||||
CombineState m_state; // Major state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current function
|
||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
int m_modNFuncs; // Number of functions made
|
||||
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
||||
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
|
||||
V3Hashed m_hashed; // Hash for every node in module
|
||||
V3Double0 m_statCombs; // Statistic tracking
|
||||
CombineState m_state; // Major state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current function
|
||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
int m_modNFuncs; // Number of functions made
|
||||
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
||||
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
|
||||
V3Hashed m_hashed; // Hash for every node in module
|
||||
|
||||
// METHODS
|
||||
void hashStatement(AstNode* nodep) {
|
||||
// Compute hash on entire tree of this statement
|
||||
m_hashed.hashAndInsert(nodep);
|
||||
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
|
||||
// Compute hash on entire tree of this statement
|
||||
m_hashed.hashAndInsert(nodep);
|
||||
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
|
||||
}
|
||||
void hashFunctions(AstCFunc* nodep) {
|
||||
// Compute hash of all statement trees in the function
|
||||
CombineState oldState = m_state;
|
||||
{
|
||||
m_state = STATE_HASH;
|
||||
// Compute hash of all statement trees in the function
|
||||
CombineState oldState = m_state;
|
||||
{
|
||||
m_state = STATE_HASH;
|
||||
iterate(nodep);
|
||||
}
|
||||
m_state = oldState;
|
||||
}
|
||||
m_state = oldState;
|
||||
}
|
||||
void walkEmptyFuncs() {
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||
if (oldfuncp
|
||||
&& oldfuncp->emptyBody()
|
||||
&& !oldfuncp->dontCombine()) {
|
||||
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, NULL);
|
||||
oldfuncp->unlinkFrBack();
|
||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||
}
|
||||
}
|
||||
if (oldfuncp
|
||||
&& oldfuncp->emptyBody()
|
||||
&& !oldfuncp->dontCombine()) {
|
||||
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())
|
||||
<<" "<<oldfuncp<<endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, NULL);
|
||||
oldfuncp->unlinkFrBack();
|
||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||
}
|
||||
}
|
||||
}
|
||||
void walkDupFuncs() {
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
V3Hash hashval = it->first;
|
||||
AstNode* node1p = it->second;
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
V3Hash hashval = it->first;
|
||||
AstNode* node1p = it->second;
|
||||
if (!VN_IS(node1p, CFunc)) continue;
|
||||
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
|
||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (!(eqit->first == hashval)) break;
|
||||
if (node1p==node2p) continue; // Identical iterator
|
||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
|
||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (!(eqit->first == hashval)) break;
|
||||
if (node1p==node2p) continue; // Identical iterator
|
||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
|
||||
// Replacement may promote a slow routine to fast path
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
UINFO(5," DupFunc "<<std::hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
||||
UINFO(5," and "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
++m_statCombs;
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
++m_statCombs;
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||
}
|
||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||
//oldfuncp->dumpTree(cout,"MAYDEL: ");
|
||||
if (nodep->nextp()==NULL
|
||||
&& oldfuncp->initsp()==NULL
|
||||
&& oldfuncp->stmtsp()==nodep
|
||||
&& oldfuncp->finalsp()==NULL) {
|
||||
UINFO(9," Function only has call "<<oldfuncp<<endl);
|
||||
m_call.deleteCall(nodep);
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
replaceFuncWFunc(oldfuncp, nodep->funcp()); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
//oldfuncp->dumpTree(cout, "MAYDEL: ");
|
||||
if (nodep->nextp()==NULL
|
||||
&& oldfuncp->initsp()==NULL
|
||||
&& oldfuncp->stmtsp()==nodep
|
||||
&& oldfuncp->finalsp()==NULL) {
|
||||
UINFO(9," Function only has call "<<oldfuncp<<endl);
|
||||
m_call.deleteCall(nodep);
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
replaceFuncWFunc(oldfuncp, nodep->funcp()); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void walkDupCodeStart(AstNode* node1p) {
|
||||
V3Hash hashval(node1p->user4p());
|
||||
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
|
||||
//
|
||||
int bestDepth = 0; // Best substitution found in the search
|
||||
AstNode* bestNode2p = NULL;
|
||||
AstNode* bestLast1p = NULL;
|
||||
AstNode* bestLast2p = NULL;
|
||||
//
|
||||
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
|
||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (node1p==node2p) continue;
|
||||
//
|
||||
// We need to mark iteration to prevent matching code inside code (abab matching in ababab)
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
m_walkLast1p = NULL;
|
||||
m_walkLast2p = NULL;
|
||||
int depth = walkDupCodeNext(node1p, node2p, 1);
|
||||
if (depth>COMBINE_MIN_STATEMENTS
|
||||
&& depth>bestDepth) {
|
||||
bestDepth = depth;
|
||||
bestNode2p = node2p;
|
||||
bestLast1p = m_walkLast1p;
|
||||
bestLast2p = m_walkLast2p;
|
||||
}
|
||||
}
|
||||
if (bestDepth) {
|
||||
// Found a replacement
|
||||
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
|
||||
UINFO(5," DupFunc "<<" "<<node1p<<endl);
|
||||
UINFO(5," and "<<" "<<bestNode2p<<endl);
|
||||
UINFO(5," Through "<<" "<<bestLast1p<<endl);
|
||||
UINFO(5," and "<<" "<<bestLast2p<<endl);
|
||||
//
|
||||
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
||||
}
|
||||
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
|
||||
//
|
||||
int bestDepth = 0; // Best substitution found in the search
|
||||
AstNode* bestNode2p = NULL;
|
||||
AstNode* bestLast1p = NULL;
|
||||
AstNode* bestLast2p = NULL;
|
||||
//
|
||||
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange
|
||||
= m_hashed.mmap().equal_range(hashval);
|
||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (node1p==node2p) continue;
|
||||
//
|
||||
// We need to mark iteration to prevent matching code inside
|
||||
// code (abab matching in ababab)
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
m_walkLast1p = NULL;
|
||||
m_walkLast2p = NULL;
|
||||
int depth = walkDupCodeNext(node1p, node2p, 1);
|
||||
if (depth>COMBINE_MIN_STATEMENTS
|
||||
&& depth>bestDepth) {
|
||||
bestDepth = depth;
|
||||
bestNode2p = node2p;
|
||||
bestLast1p = m_walkLast1p;
|
||||
bestLast2p = m_walkLast2p;
|
||||
}
|
||||
}
|
||||
if (bestDepth) {
|
||||
// Found a replacement
|
||||
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
|
||||
UINFO(5," DupFunc "<<" "<<node1p<<endl);
|
||||
UINFO(5," and "<<" "<<bestNode2p<<endl);
|
||||
UINFO(5," Through "<<" "<<bestLast1p<<endl);
|
||||
UINFO(5," and "<<" "<<bestLast2p<<endl);
|
||||
//
|
||||
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
||||
}
|
||||
}
|
||||
|
||||
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
|
||||
// Find number of common statements between the two node1p_nextp's...
|
||||
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
||||
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
||||
if (!m_hashed.sameNodes(node1p,node2p)) return 0; // walk of tree has same comparison
|
||||
V3Hash hashval(node1p->user4p());
|
||||
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||
m_walkLast1p = node1p;
|
||||
m_walkLast2p = node2p;
|
||||
node1p->user1(true);
|
||||
node2p->user1(true);
|
||||
if (node1p->nextp() && node2p->nextp()) {
|
||||
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
|
||||
}
|
||||
return hashval.depth();
|
||||
// Find number of common statements between the two node1p_nextp's...
|
||||
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
||||
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
||||
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
|
||||
V3Hash hashval(node1p->user4p());
|
||||
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||
m_walkLast1p = node1p;
|
||||
m_walkLast2p = node2p;
|
||||
node1p->user1(true);
|
||||
node2p->user1(true);
|
||||
if (node1p->nextp() && node2p->nextp()) {
|
||||
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
|
||||
}
|
||||
return hashval.depth();
|
||||
}
|
||||
|
||||
void walkReplace(AstNode* node1p, AstNode* node2p,
|
||||
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
|
||||
// Make new function
|
||||
string oldname = m_funcp->name();
|
||||
string::size_type pos;
|
||||
if ((pos=oldname.find("_common")) != string::npos) {
|
||||
oldname.erase(pos);
|
||||
}
|
||||
if ((pos=oldname.find("__")) != string::npos) {
|
||||
oldname.erase(pos);
|
||||
}
|
||||
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
||||
oldname+"_common"+cvtToStr(++m_modNFuncs),
|
||||
NULL);
|
||||
m_modp->addStmtp(newfuncp);
|
||||
// Create calls
|
||||
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
||||
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
||||
// Grab statement bodies
|
||||
AstNRelinker relink1Handle;
|
||||
AstNRelinker relink2Handle;
|
||||
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
|
||||
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
|
||||
if (walkp==last1p) break;
|
||||
}
|
||||
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
|
||||
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
|
||||
if (walkp==last2p) break;
|
||||
}
|
||||
// Move node1 statements to new function
|
||||
newfuncp->addStmtsp(node1p);
|
||||
//newfuncp->dumpTree(cout," newfunctree: ");
|
||||
// Mark node2 statements as dead
|
||||
CombMarkVisitor visitor(node2p);
|
||||
pushDeletep(node2p); // Delete later
|
||||
// Link in new function
|
||||
relink1Handle.relink(call1p);
|
||||
relink2Handle.relink(call2p);
|
||||
// Hash the new function
|
||||
hashFunctions(newfuncp);
|
||||
m_call.addCall(call1p);
|
||||
m_call.addCall(call2p);
|
||||
// If either new statement makes a func with only a single call, replace
|
||||
// the above callers to call it directly
|
||||
replaceOnlyCallFunc(call1p); VL_DANGLING(call1p);
|
||||
replaceOnlyCallFunc(call2p); VL_DANGLING(call2p);
|
||||
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
|
||||
// Make new function
|
||||
string oldname = m_funcp->name();
|
||||
string::size_type pos;
|
||||
if ((pos = oldname.find("_common")) != string::npos) {
|
||||
oldname.erase(pos);
|
||||
}
|
||||
if ((pos = oldname.find("__")) != string::npos) {
|
||||
oldname.erase(pos);
|
||||
}
|
||||
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
||||
oldname+"_common"+cvtToStr(++m_modNFuncs),
|
||||
NULL);
|
||||
m_modp->addStmtp(newfuncp);
|
||||
// Create calls
|
||||
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
||||
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
||||
// Grab statement bodies
|
||||
AstNRelinker relink1Handle;
|
||||
AstNRelinker relink2Handle;
|
||||
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
|
||||
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
|
||||
if (walkp==last1p) break;
|
||||
}
|
||||
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
|
||||
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
|
||||
if (walkp==last2p) break;
|
||||
}
|
||||
// Move node1 statements to new function
|
||||
newfuncp->addStmtsp(node1p);
|
||||
//newfuncp->dumpTree(cout, " newfunctree: ");
|
||||
// Mark node2 statements as dead
|
||||
CombMarkVisitor visitor(node2p);
|
||||
pushDeletep(node2p); // Delete later
|
||||
// Link in new function
|
||||
relink1Handle.relink(call1p);
|
||||
relink2Handle.relink(call2p);
|
||||
// Hash the new function
|
||||
hashFunctions(newfuncp);
|
||||
m_call.addCall(call1p);
|
||||
m_call.addCall(call2p);
|
||||
// If either new statement makes a func with only a single call, replace
|
||||
// the above callers to call it directly
|
||||
replaceOnlyCallFunc(call1p); VL_DANGLING(call1p);
|
||||
replaceOnlyCallFunc(call2p); VL_DANGLING(call2p);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Track all callers of each function
|
||||
m_call.main(nodep);
|
||||
//
|
||||
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
// Required so that a module instantiating another can benefit from collapsing.
|
||||
// Track all callers of each function
|
||||
m_call.main(nodep);
|
||||
//
|
||||
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
// Required so that a module instantiating another can benefit from collapsing.
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
m_modNFuncs = 0;
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast2p = NULL;
|
||||
m_hashed.clear();
|
||||
// Compute hash of all statement trees in the function
|
||||
m_state = STATE_HASH;
|
||||
m_hashed.clear();
|
||||
// Compute hash of all statement trees in the function
|
||||
m_state = STATE_HASH;
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
if (debug()>=9) {
|
||||
m_hashed.dumpFilePrefixed("combine");
|
||||
}
|
||||
// Walk the hashes removing empty functions
|
||||
if (emptyFunctionDeletion()) {
|
||||
walkEmptyFuncs();
|
||||
}
|
||||
// Walk the hashes looking for duplicate functions
|
||||
if (duplicateFunctionCombine()) {
|
||||
walkDupFuncs();
|
||||
}
|
||||
// Walk the statements looking for large replicated code sections
|
||||
if (statementCombine()) {
|
||||
m_state = STATE_DUP;
|
||||
m_state = STATE_IDLE;
|
||||
if (debug()>=9) {
|
||||
m_hashed.dumpFilePrefixed("combine");
|
||||
}
|
||||
// Walk the hashes removing empty functions
|
||||
if (emptyFunctionDeletion()) {
|
||||
walkEmptyFuncs();
|
||||
}
|
||||
// Walk the hashes looking for duplicate functions
|
||||
if (duplicateFunctionCombine()) {
|
||||
walkDupFuncs();
|
||||
}
|
||||
// Walk the statements looking for large replicated code sections
|
||||
if (statementCombine()) {
|
||||
m_state = STATE_DUP;
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
m_modp = NULL;
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
m_funcp = nodep;
|
||||
if (!nodep->dontCombine()) {
|
||||
if (m_state == STATE_HASH) {
|
||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||
} else if (m_state == STATE_DUP) {
|
||||
m_funcp = nodep;
|
||||
if (!nodep->dontCombine()) {
|
||||
if (m_state == STATE_HASH) {
|
||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||
} else if (m_state == STATE_DUP) {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
m_funcp = NULL;
|
||||
}
|
||||
}
|
||||
m_funcp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) {
|
||||
if (!nodep->isStatement()) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
if (m_state == STATE_HASH && m_funcp) {
|
||||
hashStatement(nodep);
|
||||
}
|
||||
else if (m_state == STATE_DUP && m_funcp) {
|
||||
walkDupCodeStart(nodep);
|
||||
}
|
||||
if (m_state == STATE_HASH && m_funcp) {
|
||||
hashStatement(nodep);
|
||||
}
|
||||
else if (m_state == STATE_DUP && m_funcp) {
|
||||
walkDupCodeStart(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
@@ -457,7 +462,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) {
|
||||
m_state = STATE_IDLE;
|
||||
m_state = STATE_IDLE;
|
||||
m_modp = NULL;
|
||||
m_funcp = NULL;
|
||||
m_modNFuncs = 0;
|
||||
@@ -466,7 +471,7 @@ public:
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CombineVisitor() {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ public:
|
||||
static void combineAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+80
-77
@@ -33,110 +33,113 @@
|
||||
|
||||
class V3ConfigLine {
|
||||
public:
|
||||
int m_lineno; // Line number to make change at
|
||||
V3ErrorCode m_code; // Error code
|
||||
bool m_on; // True to enaable message
|
||||
int m_lineno; // Line number to make change at
|
||||
V3ErrorCode m_code; // Error code
|
||||
bool m_on; // True to enaable message
|
||||
V3ConfigLine(V3ErrorCode code, int lineno, bool on)
|
||||
: m_lineno(lineno), m_code(code), m_on(on) {}
|
||||
: m_lineno(lineno), m_code(code), m_on(on) {}
|
||||
~V3ConfigLine() {}
|
||||
inline bool operator< (const V3ConfigLine& rh) const {
|
||||
if (m_lineno<rh.m_lineno) return true;
|
||||
if (m_lineno>rh.m_lineno) return false;
|
||||
if (m_code<rh.m_code) return true;
|
||||
if (m_code>rh.m_code) return false;
|
||||
// Always turn "on" before "off" so that overlapping lines will end up finally with the error "off"
|
||||
return (m_on>rh.m_on);
|
||||
if (m_lineno<rh.m_lineno) return true;
|
||||
if (m_lineno>rh.m_lineno) return false;
|
||||
if (m_code<rh.m_code) return true;
|
||||
if (m_code>rh.m_code) return false;
|
||||
// Always turn "on" before "off" so that overlapping lines will end
|
||||
// up finally with the error "off"
|
||||
return (m_on>rh.m_on);
|
||||
}
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
|
||||
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) {
|
||||
return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
|
||||
|
||||
class V3ConfigIgnores {
|
||||
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
||||
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
||||
|
||||
// MEMBERS
|
||||
string m_lastFilename; // Last filename looked up
|
||||
int m_lastLineno; // Last linenumber looked up
|
||||
string m_lastFilename; // Last filename looked up
|
||||
int m_lastLineno; // Last linenumber looked up
|
||||
|
||||
IgnLines::const_iterator m_lastIt; // Point with next linenumber > current line number
|
||||
IgnLines::const_iterator m_lastEnd; // Point with end()
|
||||
IgnLines::const_iterator m_lastIt; // Point with next linenumber > current line number
|
||||
IgnLines::const_iterator m_lastEnd; // Point with end()
|
||||
|
||||
IgnFiles m_ignWilds; // Ignores for each wildcarded filename
|
||||
IgnFiles m_ignFiles; // Ignores for each non-wildcarded filename
|
||||
IgnFiles m_ignWilds; // Ignores for each wildcarded filename
|
||||
IgnFiles m_ignFiles; // Ignores for each non-wildcarded filename
|
||||
|
||||
static V3ConfigIgnores s_singleton; // Singleton (not via local static, as that's slow)
|
||||
static V3ConfigIgnores s_singleton; // Singleton (not via local static, as that's slow)
|
||||
|
||||
V3ConfigIgnores() { m_lastLineno = -1; }
|
||||
~V3ConfigIgnores() {}
|
||||
|
||||
// METHODS
|
||||
inline IgnLines* findWilds(const string& wildname) {
|
||||
IgnFiles::iterator it = m_ignWilds.find(wildname);
|
||||
if (it != m_ignWilds.end()) {
|
||||
return &(it->second);
|
||||
} else {
|
||||
m_ignWilds.insert(make_pair(wildname, IgnLines()));
|
||||
it = m_ignWilds.find(wildname);
|
||||
return &(it->second);
|
||||
}
|
||||
IgnFiles::iterator it = m_ignWilds.find(wildname);
|
||||
if (it != m_ignWilds.end()) {
|
||||
return &(it->second);
|
||||
} else {
|
||||
m_ignWilds.insert(make_pair(wildname, IgnLines()));
|
||||
it = m_ignWilds.find(wildname);
|
||||
return &(it->second);
|
||||
}
|
||||
}
|
||||
inline void absBuild(const string& filename) {
|
||||
// Given a filename, find all wildcard matches against it and build
|
||||
// hash with the specific filename. This avoids having to wildmatch
|
||||
// more than once against any filename.
|
||||
IgnFiles::iterator it = m_ignFiles.find(filename);
|
||||
if (it == m_ignFiles.end()) {
|
||||
// Haven't seen this filename before
|
||||
m_ignFiles.insert(make_pair(filename, IgnLines()));
|
||||
it = m_ignFiles.find(filename);
|
||||
// Make new list for this file of all matches
|
||||
for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) {
|
||||
if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) {
|
||||
for (IgnLines::iterator lit = fnit->second.begin(); lit != fnit->second.end(); ++lit) {
|
||||
it->second.insert(*lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_lastIt = it->second.begin();
|
||||
m_lastEnd = it->second.end();
|
||||
// Given a filename, find all wildcard matches against it and build
|
||||
// hash with the specific filename. This avoids having to wildmatch
|
||||
// more than once against any filename.
|
||||
IgnFiles::iterator it = m_ignFiles.find(filename);
|
||||
if (it == m_ignFiles.end()) {
|
||||
// Haven't seen this filename before
|
||||
m_ignFiles.insert(make_pair(filename, IgnLines()));
|
||||
it = m_ignFiles.find(filename);
|
||||
// Make new list for this file of all matches
|
||||
for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) {
|
||||
if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) {
|
||||
for (IgnLines::iterator lit = fnit->second.begin();
|
||||
lit != fnit->second.end(); ++lit) {
|
||||
it->second.insert(*lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_lastIt = it->second.begin();
|
||||
m_lastEnd = it->second.end();
|
||||
}
|
||||
|
||||
public:
|
||||
inline static V3ConfigIgnores& singleton() { return s_singleton; }
|
||||
|
||||
void addIgnore(V3ErrorCode code, const string& wildname, int lineno, bool on) {
|
||||
// Insert
|
||||
IgnLines* linesp = findWilds(wildname);
|
||||
UINFO(9,"config addIgnore "<<wildname<<":"<<lineno<<", "<<code<<", "<<on<<endl);
|
||||
linesp->insert(V3ConfigLine(code, lineno, on));
|
||||
// Flush the match cache, due to a change in the rules.
|
||||
m_ignFiles.clear();
|
||||
m_lastFilename = " ";
|
||||
// Insert
|
||||
IgnLines* linesp = findWilds(wildname);
|
||||
UINFO(9,"config addIgnore "<<wildname<<":"<<lineno<<", "<<code<<", "<<on<<endl);
|
||||
linesp->insert(V3ConfigLine(code, lineno, on));
|
||||
// Flush the match cache, due to a change in the rules.
|
||||
m_ignFiles.clear();
|
||||
m_lastFilename = " ";
|
||||
}
|
||||
inline void applyIgnores(FileLine* filelinep) {
|
||||
// HOT routine, called each parsed token line
|
||||
if (m_lastLineno != filelinep->lineno()
|
||||
|| m_lastFilename != filelinep->filename()) {
|
||||
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
|
||||
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
|
||||
absBuild(filelinep->filename());
|
||||
m_lastFilename = filelinep->filename();
|
||||
}
|
||||
// Process all on/offs for lines up to and including the current line
|
||||
int curlineno = filelinep->lineno();
|
||||
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
|
||||
if (m_lastIt->m_lineno > curlineno) break;
|
||||
//UINFO(9," Hit "<<*m_lastIt<<endl);
|
||||
filelinep->warnOn(m_lastIt->m_code, m_lastIt->m_on);
|
||||
}
|
||||
if (0 && debug() >= 9) {
|
||||
for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) {
|
||||
UINFO(9," NXT "<<*it<<endl);
|
||||
}
|
||||
}
|
||||
m_lastLineno = filelinep->lineno();
|
||||
}
|
||||
// HOT routine, called each parsed token line
|
||||
if (m_lastLineno != filelinep->lineno()
|
||||
|| m_lastFilename != filelinep->filename()) {
|
||||
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
|
||||
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
|
||||
absBuild(filelinep->filename());
|
||||
m_lastFilename = filelinep->filename();
|
||||
}
|
||||
// Process all on/offs for lines up to and including the current line
|
||||
int curlineno = filelinep->lineno();
|
||||
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
|
||||
if (m_lastIt->m_lineno > curlineno) break;
|
||||
//UINFO(9," Hit "<<*m_lastIt<<endl);
|
||||
filelinep->warnOn(m_lastIt->m_code, m_lastIt->m_on);
|
||||
}
|
||||
if (0 && debug() >= 9) {
|
||||
for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) {
|
||||
UINFO(9," NXT "<<*it<<endl);
|
||||
}
|
||||
}
|
||||
m_lastLineno = filelinep->lineno();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -147,10 +150,10 @@ V3ConfigIgnores V3ConfigIgnores::s_singleton;
|
||||
|
||||
void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
|
||||
if (filename=="*") {
|
||||
FileLine::globalWarnOff(code,!on);
|
||||
FileLine::globalWarnOff(code,!on);
|
||||
} else {
|
||||
V3ConfigIgnores::singleton().addIgnore(code, filename, min, on);
|
||||
if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on);
|
||||
V3ConfigIgnores::singleton().addIgnore(code, filename, min, on);
|
||||
if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,4 +35,4 @@ public:
|
||||
static void applyIgnores(FileLine* filelinep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+1698
-1645
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -49,4 +49,4 @@ public:
|
||||
static AstNode* constifyExpensiveEdit(AstNode* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+264
-251
@@ -18,13 +18,13 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// COVERAGE TRANSFORMATIONS:
|
||||
// At each IF/(IF else)/CASEITEM,
|
||||
// If there's no coverage off on the block below it,
|
||||
// or a $stop
|
||||
// Insert a COVERDECL node in the module.
|
||||
// (V3Emit reencodes into per-module numbers for emitting.)
|
||||
// Insert a COVERINC node at the end of the statement list
|
||||
// for that if/else/case.
|
||||
// At each IF/(IF else)/CASEITEM,
|
||||
// If there's no coverage off on the block below it,
|
||||
// or a $stop
|
||||
// Insert a COVERDECL node in the module.
|
||||
// (V3Emit reencodes into per-module numbers for emitting.)
|
||||
// Insert a COVERINC node at the end of the statement list
|
||||
// for that if/else/case.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -47,334 +47,347 @@ private:
|
||||
typedef std::map<string,int> FileMap;
|
||||
|
||||
struct ToggleEnt {
|
||||
string m_comment; // Comment for coverage dump
|
||||
AstNode* m_varRefp; // How to get to this element
|
||||
AstNode* m_chgRefp; // How to get to this element
|
||||
ToggleEnt(const string& comment, AstNode* vp, AstNode* cp)
|
||||
: m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {}
|
||||
~ToggleEnt() {}
|
||||
void cleanup() {
|
||||
m_varRefp->deleteTree(); m_varRefp=NULL;
|
||||
m_chgRefp->deleteTree(); m_chgRefp=NULL;
|
||||
}
|
||||
string m_comment; // Comment for coverage dump
|
||||
AstNode* m_varRefp; // How to get to this element
|
||||
AstNode* m_chgRefp; // How to get to this element
|
||||
ToggleEnt(const string& comment, AstNode* vp, AstNode* cp)
|
||||
: m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {}
|
||||
~ToggleEnt() {}
|
||||
void cleanup() {
|
||||
m_varRefp->deleteTree(); m_varRefp = NULL;
|
||||
m_chgRefp->deleteTree(); m_chgRefp = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstIf::user1() -> bool. True indicates ifelse processed
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstIf::user1() -> bool. True indicates ifelse processed
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_inToggleOff; // In function/task etc
|
||||
bool m_inModOff; // In module with no coverage
|
||||
FileMap m_fileps; // Column counts for each fileline
|
||||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_inToggleOff; // In function/task etc
|
||||
bool m_inModOff; // In module with no coverage
|
||||
FileMap m_fileps; // Column counts for each fileline
|
||||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
const char* varIgnoreToggle(AstVar* nodep) {
|
||||
// Return true if this shouldn't be traced
|
||||
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
||||
if (!nodep->isToggleCoverable())
|
||||
return "Not relevant signal type";
|
||||
if (!v3Global.opt.coverageUnderscore()) {
|
||||
string prettyName = nodep->prettyName();
|
||||
if (prettyName[0] == '_')
|
||||
return "Leading underscore";
|
||||
if (prettyName.find("._") != string::npos)
|
||||
return "Inlined leading underscore";
|
||||
}
|
||||
if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) return "Wide bus/array > 256 bits";
|
||||
// We allow this, though tracing doesn't
|
||||
// if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array";
|
||||
return NULL;
|
||||
// Return true if this shouldn't be traced
|
||||
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
||||
if (!nodep->isToggleCoverable())
|
||||
return "Not relevant signal type";
|
||||
if (!v3Global.opt.coverageUnderscore()) {
|
||||
string prettyName = nodep->prettyName();
|
||||
if (prettyName[0] == '_')
|
||||
return "Leading underscore";
|
||||
if (prettyName.find("._") != string::npos)
|
||||
return "Inlined leading underscore";
|
||||
}
|
||||
if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) {
|
||||
return "Wide bus/array > 256 bits";
|
||||
}
|
||||
// We allow this, though tracing doesn't
|
||||
// if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AstCoverInc* newCoverInc(FileLine* fl, const string& hier,
|
||||
const string& page_prefix, const string& comment) {
|
||||
// For line coverage, we may have multiple if's on one line, so disambiguate if
|
||||
// everything is otherwise identical
|
||||
// (Don't set column otherwise as it may result in making bins not match up with
|
||||
// different types of coverage enabled.)
|
||||
string key = fl->filename()+"\001"+cvtToStr(fl->lineno())+"\001"+hier+"\001"+page_prefix+"\001"+comment;
|
||||
int column = 0;
|
||||
FileMap::iterator it = m_fileps.find(key);
|
||||
if (it == m_fileps.end()) {
|
||||
m_fileps.insert(make_pair(key,column+1));
|
||||
} else {
|
||||
column = (it->second)++;
|
||||
}
|
||||
const string& page_prefix, const string& comment) {
|
||||
// For line coverage, we may have multiple if's on one line, so disambiguate if
|
||||
// everything is otherwise identical
|
||||
// (Don't set column otherwise as it may result in making bins not match up with
|
||||
// different types of coverage enabled.)
|
||||
string key = fl->filename()+"\001"+cvtToStr(fl->lineno())
|
||||
+"\001"+hier+"\001"+page_prefix+"\001"+comment;
|
||||
int column = 0;
|
||||
FileMap::iterator it = m_fileps.find(key);
|
||||
if (it == m_fileps.end()) {
|
||||
m_fileps.insert(make_pair(key, column+1));
|
||||
} else {
|
||||
column = (it->second)++;
|
||||
}
|
||||
|
||||
// We could use the basename of the filename to the page, but seems better for code
|
||||
// from an include file to be listed under the module using it rather than the include file.
|
||||
// Note the module name could have parameters appended, we'll consider this
|
||||
// a feature as it allows for each parameterized block to be counted separately.
|
||||
// Someday the user might be allowed to specify a different page suffix
|
||||
string page = page_prefix + "/" + m_modp->prettyName();
|
||||
// We could use the basename of the filename to the page, but seems
|
||||
// better for code from an include file to be listed under the
|
||||
// module using it rather than the include file.
|
||||
// Note the module name could have parameters appended, we'll consider this
|
||||
// a feature as it allows for each parameterized block to be counted separately.
|
||||
// Someday the user might be allowed to specify a different page suffix
|
||||
string page = page_prefix + "/" + m_modp->prettyName();
|
||||
|
||||
AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment);
|
||||
declp->hier(hier);
|
||||
m_modp->addStmtp(declp);
|
||||
AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment);
|
||||
declp->hier(hier);
|
||||
m_modp->addStmtp(declp);
|
||||
|
||||
return new AstCoverInc(fl, declp);
|
||||
return new AstCoverInc(fl, declp);
|
||||
}
|
||||
|
||||
// VISITORS - BOTH
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
|
||||
m_fileps.clear();
|
||||
m_modp = nodep;
|
||||
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
|
||||
m_fileps.clear();
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
m_inModOff = true;
|
||||
m_modp = NULL;
|
||||
m_inModOff = true;
|
||||
}
|
||||
|
||||
// VISITORS - TOGGLE COVERAGE
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
bool oldtog = m_inToggleOff;
|
||||
{
|
||||
m_inToggleOff = true;
|
||||
bool oldtog = m_inToggleOff;
|
||||
{
|
||||
m_inToggleOff = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inToggleOff = oldtog;
|
||||
}
|
||||
m_inToggleOff = oldtog;
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (m_modp && !m_inModOff && !m_inToggleOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
|
||||
const char* disablep = varIgnoreToggle(nodep);
|
||||
if (disablep) {
|
||||
UINFO(4, " Disable Toggle: "<<disablep<<" "<<nodep<<endl);
|
||||
} else {
|
||||
UINFO(4, " Toggle: "<<nodep<<endl);
|
||||
// There's several overall ways to approach this
|
||||
// Treat like tracing, where a end-of-timestamp action sees all changes
|
||||
// Works ok, but would be quite slow as need to reform vectors before the calls
|
||||
// Convert to "always @ (posedge signal[#]) coverinc"
|
||||
// Would mark many signals as clocks, precluding many later optimizations
|
||||
// Convert to "if (x & !lastx) CoverInc"
|
||||
// OK, but we couldn't later detect them to schedule where the IFs get called
|
||||
// Convert to "AstCoverInc(CoverInc...)"
|
||||
// We'll do this, and make the if(...) coverinc later.
|
||||
if (m_modp && !m_inModOff && !m_inToggleOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
|
||||
const char* disablep = varIgnoreToggle(nodep);
|
||||
if (disablep) {
|
||||
UINFO(4, " Disable Toggle: "<<disablep<<" "<<nodep<<endl);
|
||||
} else {
|
||||
UINFO(4, " Toggle: "<<nodep<<endl);
|
||||
// There's several overall ways to approach this
|
||||
// Treat like tracing, where a end-of-timestamp action sees all changes
|
||||
// Works ok, but would be quite slow as need to reform
|
||||
// vectors before the calls
|
||||
// Convert to "always @ (posedge signal[#]) coverinc"
|
||||
// Would mark many signals as clocks, precluding many later optimizations
|
||||
// Convert to "if (x & !lastx) CoverInc"
|
||||
// OK, but we couldn't later detect them to schedule where the IFs get called
|
||||
// Convert to "AstCoverInc(CoverInc...)"
|
||||
// We'll do this, and make the if(...) coverinc later.
|
||||
|
||||
// Add signal to hold the old value
|
||||
// Add signal to hold the old value
|
||||
string newvarname = string("__Vtogcov__")+nodep->shortName();
|
||||
AstVar* chgVarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep);
|
||||
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
m_modp->addStmtp(chgVarp);
|
||||
AstVar* chgVarp = new AstVar(nodep->fileline(),
|
||||
AstVarType::MODULETEMP, newvarname, nodep);
|
||||
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
m_modp->addStmtp(chgVarp);
|
||||
|
||||
// Create bucket for each dimension * bit.
|
||||
// This is necessarily an O(n^2) expansion, which is why
|
||||
// we limit coverage to signals with < 256 bits.
|
||||
// Create bucket for each dimension * bit.
|
||||
// This is necessarily an O(n^2) expansion, which is why
|
||||
// we limit coverage to signals with < 256 bits.
|
||||
|
||||
ToggleEnt newvec (string(""),
|
||||
new AstVarRef(nodep->fileline(), nodep, false),
|
||||
new AstVarRef(nodep->fileline(), chgVarp, true));
|
||||
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec,
|
||||
nodep, chgVarp);
|
||||
newvec.cleanup();
|
||||
}
|
||||
}
|
||||
ToggleEnt newvec (string(""),
|
||||
new AstVarRef(nodep->fileline(), nodep, false),
|
||||
new AstVarRef(nodep->fileline(), chgVarp, true));
|
||||
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec,
|
||||
nodep, chgVarp);
|
||||
newvec.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) {
|
||||
AstCoverToggle* newp
|
||||
AstCoverToggle* newp
|
||||
= new AstCoverToggle(varp->fileline(),
|
||||
newCoverInc(varp->fileline(), "", "v_toggle",
|
||||
varp->name()+above.m_comment),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
m_modp->addStmtp(newp);
|
||||
m_modp->addStmtp(newp);
|
||||
}
|
||||
|
||||
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
|
||||
const ToggleEnt& above,
|
||||
AstVar* varp, AstVar* chgVarp) { // Constant
|
||||
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
|
||||
const ToggleEnt& above,
|
||||
AstVar* varp, AstVar* chgVarp) { // Constant
|
||||
if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (bdtypep->isRanged()) {
|
||||
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
|
||||
int index_code = index_docs - bdtypep->lsb();
|
||||
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code, 1),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code, 1));
|
||||
if (bdtypep->isRanged()) {
|
||||
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
|
||||
int index_code = index_docs - bdtypep->lsb();
|
||||
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
|
||||
new AstSel(varp->fileline(),
|
||||
above.m_varRefp->cloneTree(true), index_code, 1),
|
||||
new AstSel(varp->fileline(),
|
||||
above.m_chgRefp->cloneTree(true), index_code, 1));
|
||||
toggleVarBottom(newent, varp);
|
||||
newent.cleanup();
|
||||
}
|
||||
} else {
|
||||
newent.cleanup();
|
||||
}
|
||||
} else {
|
||||
toggleVarBottom(above, varp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
|
||||
int index_code = index_docs - adtypep->lsb();
|
||||
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
|
||||
new AstArraySel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code),
|
||||
new AstArraySel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
|
||||
int index_code = index_docs - adtypep->lsb();
|
||||
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
|
||||
new AstArraySel(varp->fileline(),
|
||||
above.m_varRefp->cloneTree(true), index_code),
|
||||
new AstArraySel(varp->fileline(),
|
||||
above.m_chgRefp->cloneTree(true), index_code));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
|
||||
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
|
||||
int index_code = index_docs - adtypep->lsb();
|
||||
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code*subtypep->width(), subtypep->width()),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code*subtypep->width(), subtypep->width()));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
|
||||
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
|
||||
int index_code = index_docs - adtypep->lsb();
|
||||
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code*subtypep->width(), subtypep->width()),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code*subtypep->width(), subtypep->width()));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
|
||||
// For now it's packed, so similar to array
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
int index_code = itemp->lsb();
|
||||
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, subtypep->width()));
|
||||
toggleVarRecurse(subtypep, depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
// For now it's packed, so similar to array
|
||||
for (AstMemberDType* itemp = adtypep->membersp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
int index_code = itemp->lsb();
|
||||
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, subtypep->width()));
|
||||
toggleVarRecurse(subtypep, depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
|
||||
// Arbitrarially handle only the first member of the union
|
||||
if (AstMemberDType* itemp = adtypep->membersp()) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
toggleVarRecurse(subtypep, depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else {
|
||||
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "<<dtypep->prettyTypeName());
|
||||
}
|
||||
// Arbitrarially handle only the first member of the union
|
||||
if (AstMemberDType* itemp = adtypep->membersp()) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
toggleVarRecurse(subtypep, depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else {
|
||||
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
|
||||
<<dtypep->prettyTypeName());
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS - LINE COVERAGE
|
||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
if (m_checkBlock) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
if (m_checkBlock) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
bool elsif = (VN_IS(nodep->elsesp(), If)
|
||||
&& !VN_CAST(nodep->elsesp(), If)->nextp());
|
||||
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
|
||||
//
|
||||
//
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (nodep->user1()) {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif"));
|
||||
} else {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if"));
|
||||
}
|
||||
}
|
||||
// Don't do empty else's, only empty if/case's
|
||||
if (nodep->elsesp()) {
|
||||
m_checkBlock = true;
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (nodep->user1()) {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif"));
|
||||
} else {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if"));
|
||||
}
|
||||
}
|
||||
// Don't do empty else's, only empty if/case's
|
||||
if (nodep->elsesp()) {
|
||||
m_checkBlock = true;
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (!elsif) { // elsif done inside if()
|
||||
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "else"));
|
||||
}
|
||||
}
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (!elsif) { // elsif done inside if()
|
||||
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(),
|
||||
"", "v_line", "else"));
|
||||
}
|
||||
}
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep) {
|
||||
UINFO(4," CASEI: "<<nodep<<endl);
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
|
||||
UINFO(4," CASEI: "<<nodep<<endl);
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
if (m_checkBlock) { // if the case body didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case"));
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
if (m_checkBlock) { // if the case body didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case"));
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
}
|
||||
virtual void visit(AstPslCover* nodep) {
|
||||
UINFO(4," PSLCOVER: "<<nodep<<endl);
|
||||
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
|
||||
UINFO(4," PSLCOVER: "<<nodep<<endl);
|
||||
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->coverincp()) {
|
||||
// Note the name may be overridden by V3Assert processing
|
||||
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover"));
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
if (!nodep->coverincp()) {
|
||||
// Note the name may be overridden by V3Assert processing
|
||||
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover"));
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
virtual void visit(AstStop* nodep) {
|
||||
UINFO(4," STOP: "<<nodep<<endl);
|
||||
m_checkBlock = false;
|
||||
UINFO(4," STOP: "<<nodep<<endl);
|
||||
m_checkBlock = false;
|
||||
}
|
||||
virtual void visit(AstPragma* nodep) {
|
||||
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
|
||||
// Skip all NEXT nodes under this block, and skip this if/case branch
|
||||
UINFO(4," OFF: "<<nodep<<endl);
|
||||
m_checkBlock = false;
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
|
||||
// Skip all NEXT nodes under this block, and skip this if/case branch
|
||||
UINFO(4," OFF: "<<nodep<<endl);
|
||||
m_checkBlock = false;
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
if (m_checkBlock) iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
// Record the hierarchy of any named begins, so we can apply to user
|
||||
// coverage points. This is because there may be cov points inside
|
||||
// generate blocks; each point should get separate consideration.
|
||||
// (Currently ignored for line coverage, since any generate iteration
|
||||
// covers the code in that line.)
|
||||
string oldHier = m_beginHier;
|
||||
bool oldtog = m_inToggleOff;
|
||||
{
|
||||
m_inToggleOff = true;
|
||||
if (nodep->name()!="") {
|
||||
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
|
||||
}
|
||||
// Record the hierarchy of any named begins, so we can apply to user
|
||||
// coverage points. This is because there may be cov points inside
|
||||
// generate blocks; each point should get separate consideration.
|
||||
// (Currently ignored for line coverage, since any generate iteration
|
||||
// covers the code in that line.)
|
||||
string oldHier = m_beginHier;
|
||||
bool oldtog = m_inToggleOff;
|
||||
{
|
||||
m_inToggleOff = true;
|
||||
if (nodep->name()!="") {
|
||||
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_beginHier = oldHier;
|
||||
m_inToggleOff = oldtog;
|
||||
}
|
||||
m_beginHier = oldHier;
|
||||
m_inToggleOff = oldtog;
|
||||
}
|
||||
|
||||
// VISITORS - BOTH
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
if (m_checkBlock) {
|
||||
// Default: Just iterate
|
||||
if (m_checkBlock) {
|
||||
iterateChildren(nodep);
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CoverageVisitor(AstNetlist* rootp) {
|
||||
// Operate on all modules
|
||||
m_checkBlock = true;
|
||||
// Operate on all modules
|
||||
m_checkBlock = true;
|
||||
m_modp = NULL;
|
||||
m_beginHier = "";
|
||||
m_inToggleOff = false;
|
||||
m_inModOff = true;
|
||||
m_beginHier = "";
|
||||
m_inToggleOff = false;
|
||||
m_inModOff = true;
|
||||
iterateChildren(rootp);
|
||||
}
|
||||
virtual ~CoverageVisitor() {}
|
||||
|
||||
+1
-1
@@ -35,4 +35,4 @@ public:
|
||||
static void coverage(AstNetlist* rootp);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+51
-47
@@ -18,7 +18,7 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// COVERAGEJOIN TRANSFORMATIONS:
|
||||
// If two COVERTOGGLEs have same VARSCOPE, combine them
|
||||
// If two COVERTOGGLEs have same VARSCOPE, combine them
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
@@ -40,72 +40,76 @@ class CoverageJoinVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// V3Hashed
|
||||
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
|
||||
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
|
||||
|
||||
//AstUser4InUse In V3Hashed
|
||||
//AstUser4InUse In V3Hashed
|
||||
|
||||
// TYPES
|
||||
typedef std::vector<AstCoverToggle*> ToggleList;
|
||||
|
||||
// STATE
|
||||
ToggleList m_toggleps; // List of of all AstCoverToggle's
|
||||
ToggleList m_toggleps; // List of of all AstCoverToggle's
|
||||
|
||||
V3Double0 m_statToggleJoins; // Statistic tracking
|
||||
V3Double0 m_statToggleJoins; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void detectDuplicates() {
|
||||
UINFO(9,"Finding duplicates\n");
|
||||
// Note uses user4
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
// Hash all of the original signals we toggle cover
|
||||
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
|
||||
AstCoverToggle* nodep = *it;
|
||||
hashed.hashAndInsert(nodep->origp());
|
||||
}
|
||||
// Find if there are any duplicates
|
||||
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
|
||||
AstCoverToggle* nodep = *it;
|
||||
if (nodep->backp()) { // nodep->backp() is null if we already detected it's a duplicate and unlinked it
|
||||
// Want to choose a base node, and keep finding duplicates that are identical
|
||||
// This prevents making chains where a->b, then c->d, then b->c, as we'll find a->b, a->c, a->d directly.
|
||||
while (1) {
|
||||
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp());
|
||||
if (dupit == hashed.end()) break;
|
||||
//
|
||||
AstNode* duporigp = hashed.iteratorNodep(dupit);
|
||||
// Note hashed will point to the original variable (what's duplicated), not the covertoggle,
|
||||
// but we need to get back to the covertoggle which is immediately above, so:
|
||||
UINFO(9,"Finding duplicates\n");
|
||||
// Note uses user4
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
// Hash all of the original signals we toggle cover
|
||||
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
|
||||
AstCoverToggle* nodep = *it;
|
||||
hashed.hashAndInsert(nodep->origp());
|
||||
}
|
||||
// Find if there are any duplicates
|
||||
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
|
||||
AstCoverToggle* nodep = *it;
|
||||
// nodep->backp() is null if we already detected it's a duplicate and unlinked it.
|
||||
if (nodep->backp()) {
|
||||
// Want to choose a base node, and keep finding duplicates that are identical.
|
||||
// This prevents making chains where a->b, then c->d, then b->c, as we'll
|
||||
// find a->b, a->c, a->d directly.
|
||||
while (1) {
|
||||
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp());
|
||||
if (dupit == hashed.end()) break;
|
||||
//
|
||||
AstNode* duporigp = hashed.iteratorNodep(dupit);
|
||||
// Note hashed will point to the original variable (what's
|
||||
// duplicated), not the covertoggle, but we need to get back to the
|
||||
// covertoggle which is immediately above, so:
|
||||
AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle);
|
||||
if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
|
||||
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
|
||||
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
|
||||
// The CoverDecl the duplicate pointed to now needs to point to the original's data
|
||||
// IE the duplicate will get the coverage number from the non-duplicate
|
||||
AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
|
||||
if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
|
||||
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
|
||||
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
|
||||
// The CoverDecl the duplicate pointed to now needs to point to the
|
||||
// original's data. I.e. the duplicate will get the coverage number
|
||||
// from the non-duplicate
|
||||
AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
|
||||
removep->incp()->declp()->dataDeclp(datadeclp);
|
||||
UINFO(8," new "<<removep->incp()->declp()<<endl);
|
||||
// Mark the found node as a duplicate of the first node
|
||||
// (Not vice-versa as we have the iterator for the found node)
|
||||
removep->unlinkFrBack(); pushDeletep(removep); VL_DANGLING(removep);
|
||||
// Remove node from comparison so don't hit it again
|
||||
hashed.erase(dupit);
|
||||
++m_statToggleJoins;
|
||||
}
|
||||
}
|
||||
}
|
||||
UINFO(8," new "<<removep->incp()->declp()<<endl);
|
||||
// Mark the found node as a duplicate of the first node
|
||||
// (Not vice-versa as we have the iterator for the found node)
|
||||
removep->unlinkFrBack(); pushDeletep(removep); VL_DANGLING(removep);
|
||||
// Remove node from comparison so don't hit it again
|
||||
hashed.erase(dupit);
|
||||
++m_statToggleJoins;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Find all Coverage's
|
||||
// Find all Coverage's
|
||||
iterateChildren(nodep);
|
||||
// Simplify
|
||||
detectDuplicates();
|
||||
// Simplify
|
||||
detectDuplicates();
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) {
|
||||
m_toggleps.push_back(nodep);
|
||||
m_toggleps.push_back(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
@@ -120,7 +124,7 @@ public:
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CoverageJoinVisitor() {
|
||||
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
|
||||
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,4 +35,4 @@ public:
|
||||
static void coverageJoin(AstNetlist* rootp);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+233
-229
@@ -18,8 +18,8 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// DEAD TRANSFORMATIONS:
|
||||
// Remove any unreferenced modules
|
||||
// Remove any unreferenced variables
|
||||
// Remove any unreferenced modules
|
||||
// Remove any unreferenced variables
|
||||
//
|
||||
// TODO: A graph would make the process of circular and interlinked
|
||||
// dependencies easier to resolve.
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCell* nodep) {
|
||||
iterateChildren(nodep);
|
||||
nodep->modp()->user1Inc(-1);
|
||||
nodep->modp()->user1Inc(-1);
|
||||
}
|
||||
//-----
|
||||
virtual void visit(AstNodeMath* nodep) {} // Accelerate
|
||||
@@ -81,337 +81,341 @@ class DeadVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire Netlist:
|
||||
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
|
||||
// AstVar::user1() -> int. Count of number of references
|
||||
// AstVarScope::user1() -> int. Count of number of references
|
||||
// AstNodeDType::user1() -> int. Count of number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
|
||||
// AstVar::user1() -> int. Count of number of references
|
||||
// AstVarScope::user1() -> int. Count of number of references
|
||||
// AstNodeDType::user1() -> int. Count of number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeModule* m_modp; // Current module
|
||||
std::vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstNode*> m_dtypesp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstScope*> m_scopesp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstCell*> m_cellsp; // List of all encountered to avoid another loop through tree
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
bool m_elimUserVars; // Allow removal of user's vars
|
||||
bool m_elimDTypes; // Allow removal of DTypes
|
||||
bool m_elimScopes; // Allow removal of Scopes
|
||||
bool m_elimCells; // Allow removal of Cells
|
||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
bool m_elimUserVars; // Allow removal of user's vars
|
||||
bool m_elimDTypes; // Allow removal of DTypes
|
||||
bool m_elimScopes; // Allow removal of Scopes
|
||||
bool m_elimCells; // Allow removal of Cells
|
||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void checkAll(AstNode* nodep) {
|
||||
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
|
||||
if (AstNode* subnodep = nodep->dtypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
}
|
||||
if (AstNode* subnodep = nodep->getChildDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
|
||||
if (AstNode* subnodep = nodep->dtypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
}
|
||||
if (AstNode* subnodep = nodep->getChildDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
}
|
||||
void checkDType(AstNodeDType* nodep) {
|
||||
if (!nodep->generic() // Don't remove generic types
|
||||
&& m_elimDTypes // dtypes stick around until post-widthing
|
||||
if (!nodep->generic() // Don't remove generic types
|
||||
&& m_elimDTypes // dtypes stick around until post-widthing
|
||||
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
|
||||
) {
|
||||
m_dtypesp.push_back(nodep);
|
||||
}
|
||||
if (AstNode* subnodep = nodep->virtRefDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
) {
|
||||
m_dtypesp.push_back(nodep);
|
||||
}
|
||||
if (AstNode* subnodep = nodep->virtRefDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
if (!nodep->dead()) {
|
||||
m_modp = nodep;
|
||||
if (!nodep->dead()) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
m_modp = NULL;
|
||||
checkAll(nodep);
|
||||
}
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
|
||||
checkAll(nodep);
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
|
||||
|
||||
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) {
|
||||
m_scopesp.push_back(nodep);
|
||||
}
|
||||
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) {
|
||||
m_scopesp.push_back(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
m_cellsp.push_back(nodep);
|
||||
nodep->modp()->user1Inc();
|
||||
checkAll(nodep);
|
||||
m_cellsp.push_back(nodep);
|
||||
nodep->modp()->user1Inc();
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->varScopep()) {
|
||||
nodep->varScopep()->user1Inc();
|
||||
nodep->varScopep()->varp()->user1Inc();
|
||||
}
|
||||
if (nodep->varp()) {
|
||||
nodep->varp()->user1Inc();
|
||||
}
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
checkAll(nodep);
|
||||
if (nodep->varScopep()) {
|
||||
nodep->varScopep()->user1Inc();
|
||||
nodep->varScopep()->varp()->user1Inc();
|
||||
}
|
||||
if (nodep->varp()) {
|
||||
nodep->varp()->user1Inc();
|
||||
}
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstRefDType* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstEnumItemRef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
checkAll(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstModport* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells) {
|
||||
if (!nodep->varsp()) {
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkAll(nodep);
|
||||
if (m_elimCells) {
|
||||
if (!nodep->varsp()) {
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells && !nodep->attrPublic()) {
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
checkAll(nodep);
|
||||
// Don't let packages with only public variables disappear
|
||||
// Normal modules may disappear, e.g. if they are parameterized then removed
|
||||
if (m_elimCells && !nodep->attrPublic()) {
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
checkAll(nodep);
|
||||
// Don't let packages with only public variables disappear
|
||||
// Normal modules may disappear, e.g. if they are parameterized then removed
|
||||
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
if (mightElimVar(nodep->varp())) {
|
||||
m_vscsp.push_back(nodep);
|
||||
}
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
if (mightElimVar(nodep->varp())) {
|
||||
m_vscsp.push_back(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
if (mightElimVar(nodep)) {
|
||||
m_varsp.push_back(nodep);
|
||||
}
|
||||
if (mightElimVar(nodep)) {
|
||||
m_varsp.push_back(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
// See if simple assignments to variables may be eliminated because that variable is never used.
|
||||
// Similar code in V3Life
|
||||
m_sideEffect = false;
|
||||
// See if simple assignments to variables may be eliminated because
|
||||
// that variable is never used.
|
||||
// Similar code in V3Life
|
||||
m_sideEffect = false;
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
checkAll(nodep);
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
checkAll(nodep);
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef);
|
||||
if (varrefp && !m_sideEffect
|
||||
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
|
||||
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
|
||||
checkAll(varrefp); // Must track reference to dtype()
|
||||
} else { // Track like any other statement
|
||||
if (varrefp && !m_sideEffect
|
||||
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
|
||||
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
|
||||
checkAll(varrefp); // Must track reference to dtype()
|
||||
} else { // Track like any other statement
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
|
||||
//-----
|
||||
virtual void visit(AstNode* nodep) {
|
||||
if (nodep->isOutputter()) m_sideEffect=true;
|
||||
if (nodep->isOutputter()) m_sideEffect = true;
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void deadCheckMod() {
|
||||
// Kill any unused modules
|
||||
// V3LinkCells has a graph that is capable of this too, but we need to do it
|
||||
// after we've done all the generate blocks
|
||||
for (bool retry=true; retry; ) {
|
||||
retry=false;
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
|
||||
// Kill any unused modules
|
||||
// V3LinkCells has a graph that is capable of this too, but we need to do it
|
||||
// after we've done all the generate blocks
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
|
||||
nextmodp = VN_CAST(modp->nextp(), NodeModule);
|
||||
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4," Dead module "<<modp<<endl);
|
||||
// And its children may now be killable too; correct counts
|
||||
// Recurse, as cells may not be directly under the module but in a generate
|
||||
if (!modp->dead()) { // If was dead didn't increment user1's
|
||||
DeadModVisitor visitor(modp);
|
||||
}
|
||||
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4," Dead module "<<modp<<endl);
|
||||
// And its children may now be killable too; correct counts
|
||||
// Recurse, as cells may not be directly under the module but in a generate
|
||||
if (!modp->dead()) { // If was dead didn't increment user1's
|
||||
DeadModVisitor visitor(modp);
|
||||
}
|
||||
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool mightElimVar(AstVar* nodep) {
|
||||
return (!nodep->isSigPublic() // Can't elim publics!
|
||||
&& !nodep->isIO()
|
||||
&& (nodep->isTemp()
|
||||
|| (nodep->isParam() && !nodep->isTrace())
|
||||
|| m_elimUserVars)); // Post-Trace can kill most anything
|
||||
return (!nodep->isSigPublic() // Can't elim publics!
|
||||
&& !nodep->isIO()
|
||||
&& (nodep->isTemp()
|
||||
|| (nodep->isParam() && !nodep->isTrace())
|
||||
|| m_elimUserVars)); // Post-Trace can kill most anything
|
||||
}
|
||||
|
||||
void deadCheckScope() {
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
for (std::vector<AstScope*>::iterator it = m_scopesp.begin(); it != m_scopesp.end();++it) {
|
||||
AstScope* scp = *it;
|
||||
if (!scp)
|
||||
continue;
|
||||
if (scp->user1() == 0) {
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
for (std::vector<AstScope*>::iterator it = m_scopesp.begin();
|
||||
it != m_scopesp.end();++it) {
|
||||
AstScope* scp = *it;
|
||||
if (!scp)
|
||||
continue;
|
||||
if (scp->user1() == 0) {
|
||||
UINFO(4, " Dead AstScope " << scp << endl);
|
||||
scp->aboveScopep()->user1Inc(-1);
|
||||
if (scp->dtypep()) {
|
||||
scp->dtypep()->user1Inc(-1);
|
||||
}
|
||||
scp->unlinkFrBack()->deleteTree(); VL_DANGLING(scp);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
scp->aboveScopep()->user1Inc(-1);
|
||||
if (scp->dtypep()) {
|
||||
scp->dtypep()->user1Inc(-1);
|
||||
}
|
||||
scp->unlinkFrBack()->deleteTree(); VL_DANGLING(scp);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deadCheckCells() {
|
||||
for (std::vector<AstCell*>::iterator it = m_cellsp.begin(); it!=m_cellsp.end(); ++it) {
|
||||
AstCell* cellp = *it;
|
||||
if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) {
|
||||
cellp->modp()->user1Inc(-1);
|
||||
cellp->unlinkFrBack()->deleteTree(); VL_DANGLING(cellp);
|
||||
}
|
||||
}
|
||||
AstCell* cellp = *it;
|
||||
if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) {
|
||||
cellp->modp()->user1Inc(-1);
|
||||
cellp->unlinkFrBack()->deleteTree(); VL_DANGLING(cellp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deadCheckVar() {
|
||||
// Delete any unused varscopes
|
||||
// Delete any unused varscopes
|
||||
for (std::vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
|
||||
AstVarScope* vscp = *it;
|
||||
if (vscp->user1() == 0) {
|
||||
UINFO(4," Dead "<<vscp<<endl);
|
||||
std::pair<AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
|
||||
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
|
||||
AstNodeAssign* assp = itr->second;
|
||||
AstVarScope* vscp = *it;
|
||||
if (vscp->user1() == 0) {
|
||||
UINFO(4," Dead "<<vscp<<endl);
|
||||
std::pair<AssignMap::iterator,AssignMap::iterator> eqrange
|
||||
= m_assignMap.equal_range(vscp);
|
||||
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
|
||||
AstNodeAssign* assp = itr->second;
|
||||
UINFO(4," Dead assign "<<assp<<endl);
|
||||
assp->dtypep()->user1Inc(-1);
|
||||
assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp);
|
||||
}
|
||||
if (vscp->scopep()) vscp->scopep()->user1Inc(-1);
|
||||
vscp->dtypep()->user1Inc(-1);
|
||||
vscp->unlinkFrBack()->deleteTree(); VL_DANGLING(vscp);
|
||||
}
|
||||
}
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
assp->dtypep()->user1Inc(-1);
|
||||
assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp);
|
||||
}
|
||||
if (vscp->scopep()) vscp->scopep()->user1Inc(-1);
|
||||
vscp->dtypep()->user1Inc(-1);
|
||||
vscp->unlinkFrBack()->deleteTree(); VL_DANGLING(vscp);
|
||||
}
|
||||
}
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
for (std::vector<AstVar *>::iterator it = m_varsp.begin(); it != m_varsp.end();++it) {
|
||||
AstVar* varp = *it;
|
||||
if (!varp)
|
||||
continue;
|
||||
if (varp->user1() == 0) {
|
||||
AstVar* varp = *it;
|
||||
if (!varp)
|
||||
continue;
|
||||
if (varp->user1() == 0) {
|
||||
UINFO(4, " Dead " << varp << endl);
|
||||
if (varp->dtypep()) {
|
||||
varp->dtypep()->user1Inc(-1);
|
||||
}
|
||||
varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (varp->dtypep()) {
|
||||
varp->dtypep()->user1Inc(-1);
|
||||
}
|
||||
varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end();++it) {
|
||||
if ((*it)->user1() == 0) {
|
||||
AstNodeClassDType *classp;
|
||||
// It's possible that there if a reference to each individual member, but
|
||||
// not to the dtype itself. Check and don't remove the parent dtype if
|
||||
// members are still alive.
|
||||
if ((*it)->user1() == 0) {
|
||||
AstNodeClassDType *classp;
|
||||
// It's possible that there if a reference to each individual member, but
|
||||
// not to the dtype itself. Check and don't remove the parent dtype if
|
||||
// members are still alive.
|
||||
if ((classp = VN_CAST((*it), NodeClassDType))) {
|
||||
bool cont = true;
|
||||
bool cont = true;
|
||||
for (AstMemberDType *memberp = classp->membersp();
|
||||
memberp; memberp = VN_CAST(memberp->nextp(), MemberDType)) {
|
||||
if (memberp->user1() != 0) {
|
||||
cont = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cont)
|
||||
continue;
|
||||
}
|
||||
(*it)->unlinkFrBack()->deleteTree(); VL_DANGLING(*it);
|
||||
}
|
||||
}
|
||||
if (memberp->user1() != 0) {
|
||||
cont = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cont)
|
||||
continue;
|
||||
}
|
||||
(*it)->unlinkFrBack()->deleteTree(); VL_DANGLING(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes, bool elimCells) {
|
||||
m_modp = NULL;
|
||||
m_elimCells = elimCells;
|
||||
m_elimUserVars = elimUserVars;
|
||||
m_elimDTypes = elimDTypes;
|
||||
m_elimScopes = elimScopes;
|
||||
m_sideEffect = false;
|
||||
// Prepare to remove some datatypes
|
||||
nodep->typeTablep()->clearCache();
|
||||
// Operate on whole netlist
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes,
|
||||
bool elimScopes, bool elimCells) {
|
||||
m_modp = NULL;
|
||||
m_elimCells = elimCells;
|
||||
m_elimUserVars = elimUserVars;
|
||||
m_elimDTypes = elimDTypes;
|
||||
m_elimScopes = elimScopes;
|
||||
m_sideEffect = false;
|
||||
// Prepare to remove some datatypes
|
||||
nodep->typeTablep()->clearCache();
|
||||
// Operate on whole netlist
|
||||
iterate(nodep);
|
||||
|
||||
deadCheckVar();
|
||||
// We only elimate scopes when in a flattened structure
|
||||
// Otherwise we have no easy way to know if a scope is used
|
||||
if (elimScopes) deadCheckScope();
|
||||
if (elimCells) deadCheckCells();
|
||||
// Modules after vars, because might be vars we delete inside a mod we delete
|
||||
deadCheckMod();
|
||||
deadCheckVar();
|
||||
// We only elimate scopes when in a flattened structure
|
||||
// Otherwise we have no easy way to know if a scope is used
|
||||
if (elimScopes) deadCheckScope();
|
||||
if (elimCells) deadCheckCells();
|
||||
// Modules after vars, because might be vars we delete inside a mod we delete
|
||||
deadCheckMod();
|
||||
|
||||
// We may have removed some datatypes, cleanup
|
||||
nodep->typeTablep()->repairCache();
|
||||
// We may have removed some datatypes, cleanup
|
||||
nodep->typeTablep()->repairCache();
|
||||
}
|
||||
virtual ~DeadVisitor() {}
|
||||
};
|
||||
|
||||
+1
-1
@@ -41,4 +41,4 @@ public:
|
||||
static void deadifyAllScoped(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
#endif // Guard
|
||||
|
||||
+307
-299
@@ -20,34 +20,35 @@
|
||||
// V3Delayed's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Replace ASSIGNDLY var, exp
|
||||
// With ASSIGNDLY newvar, exp
|
||||
// At top of block: VAR newvar
|
||||
// At bottom of block: ASSIGNW var newvar
|
||||
// Need _x_dly = x at top of active if "x" is not always set
|
||||
// For now we'll say it's set if at top of block (not under IF, etc)
|
||||
// Need x = _x_dly at bottom of active if "x" is never referenced on LHS
|
||||
// in the active, and above rule applies too. (If so, use x on LHS, not _x_dly.)
|
||||
// Replace ASSIGNDLY var, exp
|
||||
// With ASSIGNDLY newvar, exp
|
||||
// At top of block: VAR newvar
|
||||
// At bottom of block: ASSIGNW var newvar
|
||||
// Need _x_dly = x at top of active if "x" is not always set
|
||||
// For now we'll say it's set if at top of block (not under IF, etc)
|
||||
// Need x = _x_dly at bottom of active if "x" is never referenced on LHS
|
||||
// in the active, and above rule applies too.
|
||||
// (If so, use x on LHS, not _x_dly.)
|
||||
//
|
||||
// If a signal is set in multiple always blocks, we need a dly read & set with
|
||||
// multiple clock sensitivities. We have 3 options:
|
||||
// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there
|
||||
// 2. Form unique ACTIVE for every multiple clocked assignment
|
||||
// 3. Predetect signals from multiple always blocks and do #2 on them
|
||||
// Since all 3 require a top activation cleanup, we do #2 which is easiest.
|
||||
// If a signal is set in multiple always blocks, we need a dly read & set with
|
||||
// multiple clock sensitivities. We have 3 options:
|
||||
// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there
|
||||
// 2. Form unique ACTIVE for every multiple clocked assignment
|
||||
// 3. Predetect signals from multiple always blocks and do #2 on them
|
||||
// Since all 3 require a top activation cleanup, we do #2 which is easiest.
|
||||
//
|
||||
// ASSIGNDLY (BITSEL(ARRAYSEL (VARREF(v), bits), selbits), rhs)
|
||||
// -> VAR __Vdlyvset_x
|
||||
// VAR __Vdlyvval_x
|
||||
// VAR __Vdlyvdim_x
|
||||
// VAR __Vdlyvlsb_x
|
||||
// ASSIGNW (__Vdlyvset_x,0)
|
||||
// ...
|
||||
// ASSIGNW (VARREF(__Vdlyvval_x), rhs)
|
||||
// ASSIGNW (__Vdlyvdim_x, dimension_number)
|
||||
// ASSIGNW (__Vdlyvset_x, 1)
|
||||
// ...
|
||||
// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x)
|
||||
// -> VAR __Vdlyvset_x
|
||||
// VAR __Vdlyvval_x
|
||||
// VAR __Vdlyvdim_x
|
||||
// VAR __Vdlyvlsb_x
|
||||
// ASSIGNW (__Vdlyvset_x,0)
|
||||
// ...
|
||||
// ASSIGNW (VARREF(__Vdlyvval_x), rhs)
|
||||
// ASSIGNW (__Vdlyvdim_x, dimension_number)
|
||||
// ASSIGNW (__Vdlyvset_x, 1)
|
||||
// ...
|
||||
// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -71,76 +72,81 @@ class DelayedVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Cleared each module:
|
||||
// AstVarScope::user1p() -> AstVarScope*. Points to temp var created.
|
||||
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal (valid when AstVarScope::user1p is valid)
|
||||
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
|
||||
// AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage
|
||||
// AstVar::user2() -> bool. Set true if already made warning
|
||||
// AstVarRef::user2() -> bool. Set true if already processed
|
||||
// AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal (valid when AstAlwaysPost::user4p is valid)
|
||||
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
|
||||
// AstVarScope::user1p() -> AstVarScope*. Points to temp var created.
|
||||
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal
|
||||
// (valid when AstVarScope::user1p is valid)
|
||||
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
|
||||
// AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage
|
||||
// AstVar::user2() -> bool. Set true if already made warning
|
||||
// AstVarRef::user2() -> bool. Set true if already processed
|
||||
// AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal
|
||||
// (valid when AstAlwaysPost::user4p is valid)
|
||||
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
|
||||
// Cleared each scope/active:
|
||||
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
|
||||
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser4InUse m_inuser4;
|
||||
AstUser5InUse m_inuser5;
|
||||
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
|
||||
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser4InUse m_inuser4;
|
||||
AstUser5InUse m_inuser5;
|
||||
|
||||
enum VarUsage { VU_NONE=0, VU_DLY=1, VU_NONDLY=2 };
|
||||
|
||||
// STATE
|
||||
AstActive* m_activep; // Current activate
|
||||
AstCFunc* m_cfuncp; // Current public C Function
|
||||
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
|
||||
bool m_inDly; // True in delayed assignments
|
||||
bool m_inLoop; // True in for loops
|
||||
bool m_inInitial; // True in intial blocks
|
||||
AstActive* m_activep; // Current activate
|
||||
AstCFunc* m_cfuncp; // Current public C Function
|
||||
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
|
||||
bool m_inDly; // True in delayed assignments
|
||||
bool m_inLoop; // True in for loops
|
||||
bool m_inInitial; // True in intial blocks
|
||||
typedef std::map<std::pair<AstNodeModule*,string>,AstVar*> VarMap;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
V3Double0 m_statSharedSet;// Statistic tracking
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
V3Double0 m_statSharedSet;// Statistic tracking
|
||||
typedef std::map<AstVarScope*,int> ScopeVecMap;
|
||||
ScopeVecMap m_scopeVecMap; // Next var number for each scope
|
||||
ScopeVecMap m_scopeVecMap; // Next var number for each scope
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void markVarUsage(AstVarScope* nodep, uint32_t flags) {
|
||||
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
|
||||
nodep->user5( nodep->user5() | flags );
|
||||
if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) {
|
||||
nodep->v3warn(BLKANDNBLK,"Unsupported: Blocked and non-blocking assignments to same variable: "
|
||||
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
|
||||
nodep->user5( nodep->user5() | flags );
|
||||
if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) {
|
||||
nodep->v3warn(BLKANDNBLK, "Unsupported: Blocked and non-blocking assignments to same variable: "
|
||||
<<nodep->varp()->prettyName());
|
||||
}
|
||||
}
|
||||
}
|
||||
AstVarScope* createVarSc(AstVarScope* oldvarscp, const string& name,
|
||||
int width/*0==fromoldvar*/, AstNodeDType* newdtypep) {
|
||||
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
|
||||
if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped");
|
||||
AstVar* varp;
|
||||
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
|
||||
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
|
||||
VarMap::iterator it = m_modVarMap.find(make_pair(addmodp,name));
|
||||
if (it != m_modVarMap.end()) {
|
||||
// Created module's AstVar earlier under some other scope
|
||||
varp = it->second;
|
||||
} else {
|
||||
if (newdtypep) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, newdtypep);
|
||||
} else if (width==0) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, oldvarscp->varp());
|
||||
varp->dtypeFrom(oldvarscp);
|
||||
} else { // Used for vset and dimensions, so can zero init
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, VFlagBitPacked(), width);
|
||||
}
|
||||
addmodp->addStmtp(varp);
|
||||
m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp));
|
||||
}
|
||||
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
|
||||
if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped");
|
||||
AstVar* varp;
|
||||
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
|
||||
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
|
||||
VarMap::iterator it = m_modVarMap.find(make_pair(addmodp, name));
|
||||
if (it != m_modVarMap.end()) {
|
||||
// Created module's AstVar earlier under some other scope
|
||||
varp = it->second;
|
||||
} else {
|
||||
if (newdtypep) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
|
||||
name, newdtypep);
|
||||
} else if (width==0) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
|
||||
name, oldvarscp->varp());
|
||||
varp->dtypeFrom(oldvarscp);
|
||||
} else { // Used for vset and dimensions, so can zero init
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
|
||||
name, VFlagBitPacked(), width);
|
||||
}
|
||||
addmodp->addStmtp(varp);
|
||||
m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp));
|
||||
}
|
||||
|
||||
AstVarScope* varscp = new AstVarScope(oldvarscp->fileline(), oldvarscp->scopep(), varp);
|
||||
oldvarscp->scopep()->addVarp(varscp);
|
||||
return varscp;
|
||||
oldvarscp->scopep()->addVarp(varscp);
|
||||
return varscp;
|
||||
}
|
||||
|
||||
AstActive* createActivePost(AstVarRef* varrefp) {
|
||||
@@ -148,299 +154,301 @@ private:
|
||||
m_activep->sensesp());
|
||||
// Was addNext(), but addNextHere() avoids a linear search.
|
||||
m_activep->addNextHere(newactp);
|
||||
return newactp;
|
||||
return newactp;
|
||||
}
|
||||
void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) {
|
||||
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
|
||||
if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block");
|
||||
if (oldactivep->sensesp() != m_activep->sensesp()) {
|
||||
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
|
||||
&& !varrefp->varp()->user2()) {
|
||||
varrefp->varp()->v3warn(MULTIDRIVEN,"Signal has multiple driving blocks with different clocking: "
|
||||
<<varrefp->varp()->prettyName()<<endl
|
||||
<<varrefp->warnMore()<<"... Location of first driving block"<<endl
|
||||
<<oldactivep->warnMore()<<"... Location of other driving block");
|
||||
varrefp->varp()->user2(true);
|
||||
}
|
||||
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
|
||||
UINFO(4," Act: "<<m_activep<<endl);
|
||||
UINFO(4," Act: "<<oldactivep<<endl);
|
||||
// Make a new sensitivity list, which is the combination of both blocks
|
||||
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
|
||||
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
|
||||
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
|
||||
if (senb) treep->addSensesp(senb);
|
||||
if (AstSenTree* storep = oldactivep->sensesStorep()) {
|
||||
storep->unlinkFrBack();
|
||||
pushDeletep(storep);
|
||||
}
|
||||
oldactivep->sensesStorep(treep);
|
||||
oldactivep->sensesp(treep);
|
||||
}
|
||||
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
|
||||
if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block");
|
||||
if (oldactivep->sensesp() != m_activep->sensesp()) {
|
||||
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
|
||||
&& !varrefp->varp()->user2()) {
|
||||
varrefp->varp()->v3warn(
|
||||
MULTIDRIVEN, "Signal has multiple driving blocks with different clocking: "
|
||||
<<varrefp->varp()->prettyName()<<endl
|
||||
<<varrefp->warnMore()<<"... Location of first driving block"<<endl
|
||||
<<oldactivep->warnMore()<<"... Location of other driving block");
|
||||
varrefp->varp()->user2(true);
|
||||
}
|
||||
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
|
||||
UINFO(4," Act: "<<m_activep<<endl);
|
||||
UINFO(4," Act: "<<oldactivep<<endl);
|
||||
// Make a new sensitivity list, which is the combination of both blocks
|
||||
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
|
||||
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
|
||||
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
|
||||
if (senb) treep->addSensesp(senb);
|
||||
if (AstSenTree* storep = oldactivep->sensesStorep()) {
|
||||
storep->unlinkFrBack();
|
||||
pushDeletep(storep);
|
||||
}
|
||||
oldactivep->sensesStorep(treep);
|
||||
oldactivep->sensesp(treep);
|
||||
}
|
||||
}
|
||||
|
||||
AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) {
|
||||
// Create delayed assignment
|
||||
// See top of this file for transformation
|
||||
// Return the new LHS for the assignment, Null = unlink
|
||||
// Find selects
|
||||
AstNode* newlhsp = NULL; // NULL = unlink old assign
|
||||
AstSel* bitselp = NULL;
|
||||
AstArraySel* arrayselp = NULL;
|
||||
// Create delayed assignment
|
||||
// See top of this file for transformation
|
||||
// Return the new LHS for the assignment, Null = unlink
|
||||
// Find selects
|
||||
AstNode* newlhsp = NULL; // NULL = unlink old assign
|
||||
AstSel* bitselp = NULL;
|
||||
AstArraySel* arrayselp = NULL;
|
||||
if (VN_IS(lhsp, Sel)) {
|
||||
bitselp = VN_CAST(lhsp, Sel);
|
||||
arrayselp = VN_CAST(bitselp->fromp(), ArraySel);
|
||||
} else {
|
||||
} else {
|
||||
arrayselp = VN_CAST(lhsp, ArraySel);
|
||||
}
|
||||
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
|
||||
}
|
||||
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
|
||||
if (VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
}
|
||||
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
|
||||
//
|
||||
//=== Dimensions: __Vdlyvdim__
|
||||
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
|
||||
AstNode* dimselp=arrayselp;
|
||||
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
|
||||
//
|
||||
//=== Dimensions: __Vdlyvdim__
|
||||
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
|
||||
AstNode* dimselp = arrayselp;
|
||||
for (; VN_IS(dimselp, ArraySel); dimselp=VN_CAST(dimselp, ArraySel)->fromp()) {
|
||||
AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack();
|
||||
dimvalp.push_front(valp);
|
||||
}
|
||||
dimvalp.push_front(valp);
|
||||
}
|
||||
AstVarRef* varrefp = VN_CAST(dimselp, VarRef);
|
||||
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels");
|
||||
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
varrefp->unlinkFrBack();
|
||||
AstVar* oldvarp = varrefp->varp();
|
||||
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
//
|
||||
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
||||
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
|
||||
AstNode* dimp = dimvalp[dimension];
|
||||
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels");
|
||||
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
varrefp->unlinkFrBack();
|
||||
AstVar* oldvarp = varrefp->varp();
|
||||
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
//
|
||||
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
||||
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
|
||||
AstNode* dimp = dimvalp[dimension];
|
||||
if (VN_IS(dimp, Const)) { // bit = const, can just use it
|
||||
dimreadps.push_front(dimp);
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
|
||||
+"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), NULL);
|
||||
AstAssign* bitassignp
|
||||
dimreadps.push_front(dimp);
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
|
||||
+"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(),
|
||||
bitvarname, dimp->width(), NULL);
|
||||
AstAssign* bitassignp
|
||||
= new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), bitvscp, true),
|
||||
dimp);
|
||||
nodep->addNextHere(bitassignp);
|
||||
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false));
|
||||
}
|
||||
}
|
||||
//
|
||||
//=== Bitselect: __Vdlyvlsb__
|
||||
AstNode* bitreadp=NULL; // Code to read Vdlyvlsb
|
||||
if (bitselp) {
|
||||
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
|
||||
nodep->addNextHere(bitassignp);
|
||||
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false));
|
||||
}
|
||||
}
|
||||
//
|
||||
//=== Bitselect: __Vdlyvlsb__
|
||||
AstNode* bitreadp = NULL; // Code to read Vdlyvlsb
|
||||
if (bitselp) {
|
||||
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
|
||||
if (VN_IS(bitselp->fromp(), Const)) { // vlsb = constant, can just push constant into where we use it
|
||||
bitreadp = lsbvaluep;
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), NULL);
|
||||
bitreadp = lsbvaluep;
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(),
|
||||
bitvarname, lsbvaluep->width(), NULL);
|
||||
AstAssign* bitassignp = new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), bitvscp, true),
|
||||
lsbvaluep);
|
||||
nodep->addNextHere(bitassignp);
|
||||
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false);
|
||||
}
|
||||
}
|
||||
//
|
||||
//=== Value: __Vdlyvval__
|
||||
AstNode* valreadp; // Code to read Vdlyvval
|
||||
nodep->addNextHere(bitassignp);
|
||||
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false);
|
||||
}
|
||||
}
|
||||
//
|
||||
//=== Value: __Vdlyvval__
|
||||
AstNode* valreadp; // Code to read Vdlyvval
|
||||
if (VN_IS(nodep->rhsp(), Const)) { // vval = constant, can just push constant into where we use it
|
||||
valreadp = nodep->rhsp()->unlinkFrBack();
|
||||
} else {
|
||||
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
||||
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
|
||||
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
|
||||
}
|
||||
//
|
||||
//=== Setting/not setting boolean: __Vdlyvset__
|
||||
AstVarScope* setvscp;
|
||||
AstAssignPre* setinitp = NULL;
|
||||
valreadp = nodep->rhsp()->unlinkFrBack();
|
||||
} else {
|
||||
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
||||
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
|
||||
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
|
||||
}
|
||||
//
|
||||
//=== Setting/not setting boolean: __Vdlyvset__
|
||||
AstVarScope* setvscp;
|
||||
AstAssignPre* setinitp = NULL;
|
||||
|
||||
if (nodep->user3p()) {
|
||||
// Simplistic optimization. If the previous statement in same scope was also a =>,
|
||||
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
|
||||
// This is good for code like:
|
||||
// for (i=0; i<5; i++) vector[i] <= something;
|
||||
if (nodep->user3p()) {
|
||||
// Simplistic optimization. If the previous statement in same scope was also a =>,
|
||||
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
|
||||
// This is good for code like:
|
||||
// for (i=0; i<5; i++) vector[i] <= something;
|
||||
setvscp = VN_CAST(nodep->user3p(), VarScope);
|
||||
++m_statSharedSet;
|
||||
} else { // Create new one
|
||||
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL);
|
||||
++m_statSharedSet;
|
||||
} else { // Create new one
|
||||
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL);
|
||||
setinitp = new AstAssignPre(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, true),
|
||||
new AstConst(nodep->fileline(), 0));
|
||||
AstAssign* setassignp
|
||||
AstAssign* setassignp
|
||||
= new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, true),
|
||||
new AstConst(nodep->fileline(),
|
||||
V3Number(nodep->fileline(),1,true)));
|
||||
nodep->addNextHere(setassignp);
|
||||
}
|
||||
if (m_nextDlyp) { // Tell next assigndly it can share the variable
|
||||
m_nextDlyp->user3p(setvscp);
|
||||
}
|
||||
//
|
||||
// Create ALWAYSPOST for delayed variable
|
||||
// We add all logic to the same block if it's for the same memory
|
||||
// This ensures that multiple assignments to the same memory will result
|
||||
// in correctly ordered code - the last assignment must be last.
|
||||
// It also has the nice side effect of assisting cache locality.
|
||||
AstNode* selectsp = varrefp;
|
||||
for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) {
|
||||
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
|
||||
}
|
||||
if (bitselp) {
|
||||
selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp,
|
||||
bitselp->widthp()->cloneTree(false));
|
||||
}
|
||||
// Build "IF (changeit) ...
|
||||
UINFO(9," For "<<setvscp<<endl);
|
||||
UINFO(9," & "<<varrefp<<endl);
|
||||
new AstConst(nodep->fileline(), AstConst::LogicTrue()));
|
||||
nodep->addNextHere(setassignp);
|
||||
}
|
||||
if (m_nextDlyp) { // Tell next assigndly it can share the variable
|
||||
m_nextDlyp->user3p(setvscp);
|
||||
}
|
||||
//
|
||||
// Create ALWAYSPOST for delayed variable
|
||||
// We add all logic to the same block if it's for the same memory
|
||||
// This ensures that multiple assignments to the same memory will result
|
||||
// in correctly ordered code - the last assignment must be last.
|
||||
// It also has the nice side effect of assisting cache locality.
|
||||
AstNode* selectsp = varrefp;
|
||||
for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) {
|
||||
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
|
||||
}
|
||||
if (bitselp) {
|
||||
selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp,
|
||||
bitselp->widthp()->cloneTree(false));
|
||||
}
|
||||
// Build "IF (changeit) ...
|
||||
UINFO(9," For "<<setvscp<<endl);
|
||||
UINFO(9," & "<<varrefp<<endl);
|
||||
AstAlwaysPost* finalp = VN_CAST(varrefp->varScopep()->user4p(), AlwaysPost);
|
||||
if (finalp) {
|
||||
if (finalp) {
|
||||
AstActive* oldactivep = VN_CAST(finalp->user2p(), Active);
|
||||
checkActivePost(varrefp, oldactivep);
|
||||
if (setinitp) oldactivep->addStmtsp(setinitp);
|
||||
} else { // first time we've dealt with this memory
|
||||
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
|
||||
UINFO(9," Created "<<finalp<<endl);
|
||||
AstActive* newactp = createActivePost(varrefp);
|
||||
newactp->addStmtsp(finalp);
|
||||
varrefp->varScopep()->user4p(finalp);
|
||||
finalp->user2p(newactp);
|
||||
if (setinitp) newactp->addStmtsp(setinitp);
|
||||
}
|
||||
AstIf* postLogicp;
|
||||
if (finalp->user3p() == setvscp) {
|
||||
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
|
||||
// we can share the IF statement too
|
||||
checkActivePost(varrefp, oldactivep);
|
||||
if (setinitp) oldactivep->addStmtsp(setinitp);
|
||||
} else { // first time we've dealt with this memory
|
||||
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
|
||||
UINFO(9," Created "<<finalp<<endl);
|
||||
AstActive* newactp = createActivePost(varrefp);
|
||||
newactp->addStmtsp(finalp);
|
||||
varrefp->varScopep()->user4p(finalp);
|
||||
finalp->user2p(newactp);
|
||||
if (setinitp) newactp->addStmtsp(setinitp);
|
||||
}
|
||||
AstIf* postLogicp;
|
||||
if (finalp->user3p() == setvscp) {
|
||||
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
|
||||
// we can share the IF statement too
|
||||
postLogicp = VN_CAST(finalp->user4p(), If);
|
||||
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
|
||||
} else {
|
||||
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
|
||||
} else {
|
||||
postLogicp = new AstIf(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, false),
|
||||
NULL, NULL);
|
||||
UINFO(9," Created "<<postLogicp<<endl);
|
||||
finalp->addBodysp(postLogicp);
|
||||
finalp->user3p(setvscp); // Remember IF's vset variable
|
||||
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
||||
}
|
||||
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
|
||||
return newlhsp;
|
||||
UINFO(9," Created "<<postLogicp<<endl);
|
||||
finalp->addBodysp(postLogicp);
|
||||
finalp->user3p(setvscp); // Remember IF's vset variable
|
||||
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
||||
}
|
||||
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
|
||||
return newlhsp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
//VV***** We reset all userp() on the netlist
|
||||
m_modVarMap.clear();
|
||||
//VV***** We reset all userp() on the netlist
|
||||
m_modVarMap.clear();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
AstNode::user3ClearTree();
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
AstNode::user3ClearTree();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
m_cfuncp = nodep;
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_cfuncp = NULL;
|
||||
m_cfuncp = NULL;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
m_activep = nodep;
|
||||
bool oldinit = m_inInitial;
|
||||
m_inInitial = nodep->hasInitial();
|
||||
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
|
||||
m_activep = nodep;
|
||||
bool oldinit = m_inInitial;
|
||||
m_inInitial = nodep->hasInitial();
|
||||
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
|
||||
iterateChildren(nodep);
|
||||
m_inInitial = oldinit;
|
||||
m_inInitial = oldinit;
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
m_inDly = true;
|
||||
m_inDly = true;
|
||||
m_nextDlyp = VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe NULL.
|
||||
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
|
||||
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
|
||||
if (VN_IS(nodep->lhsp(), ArraySel)
|
||||
|| (VN_IS(nodep->lhsp(), Sel)
|
||||
&& VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), ArraySel))) {
|
||||
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNode* newlhsp = createDlyArray(nodep, lhsp);
|
||||
if (m_inLoop) nodep->v3warn(BLKLOOPINIT,"Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
|
||||
if (newlhsp) {
|
||||
nodep->lhsp(newlhsp);
|
||||
} else {
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
lhsp->deleteTree(); VL_DANGLING(lhsp);
|
||||
}
|
||||
else {
|
||||
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNode* newlhsp = createDlyArray(nodep, lhsp);
|
||||
if (m_inLoop) nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
|
||||
if (newlhsp) {
|
||||
nodep->lhsp(newlhsp);
|
||||
} else {
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
lhsp->deleteTree(); VL_DANGLING(lhsp);
|
||||
}
|
||||
else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inDly = false;
|
||||
m_nextDlyp = NULL;
|
||||
}
|
||||
m_inDly = false;
|
||||
m_nextDlyp = NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (!nodep->user2Inc()) { // Not done yet
|
||||
if (m_inDly && nodep->lvalue()) {
|
||||
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
|
||||
markVarUsage(nodep->varScopep(), VU_DLY);
|
||||
if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block");
|
||||
if (!nodep->user2Inc()) { // Not done yet
|
||||
if (m_inDly && nodep->lvalue()) {
|
||||
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
|
||||
markVarUsage(nodep->varScopep(), VU_DLY);
|
||||
if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block");
|
||||
if (!m_activep->hasClocked()) {
|
||||
nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should have converted in V3Active");
|
||||
}
|
||||
AstVarScope* oldvscp = nodep->varScopep();
|
||||
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
AstVarScope* oldvscp = nodep->varScopep();
|
||||
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope);
|
||||
if (dlyvscp) { // Multiple use of delayed variable
|
||||
if (dlyvscp) { // Multiple use of delayed variable
|
||||
AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active);
|
||||
checkActivePost(nodep, oldactivep);
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL);
|
||||
AstNodeAssign* prep
|
||||
checkActivePost(nodep, oldactivep);
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL);
|
||||
AstNodeAssign* prep
|
||||
= new AstAssignPre(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, true),
|
||||
new AstVarRef(nodep->fileline(), oldvscp, false));
|
||||
AstNodeAssign* postp
|
||||
AstNodeAssign* postp
|
||||
= new AstAssignPost(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), oldvscp, true),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, false));
|
||||
postp->lhsp()->user2(true); // Don't detect this assignment
|
||||
oldvscp->user1p(dlyvscp); // So we can find it later
|
||||
// Make new ACTIVE with identical sensitivity tree
|
||||
AstActive* newactp = createActivePost(nodep);
|
||||
dlyvscp->user2p(newactp);
|
||||
newactp->addStmtsp(prep); // Add to FRONT of statements
|
||||
newactp->addStmtsp(postp);
|
||||
}
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
|
||||
newrefp->user2(true); // No reason to do it again
|
||||
nodep->replaceWith(newrefp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (!m_inDly && nodep->lvalue()) {
|
||||
//UINFO(9,"NBA "<<nodep<<endl);
|
||||
if (!m_inInitial) {
|
||||
UINFO(4,"AssignNDlyVar: "<<nodep<<endl);
|
||||
markVarUsage(nodep->varScopep(), VU_NONDLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
postp->lhsp()->user2(true); // Don't detect this assignment
|
||||
oldvscp->user1p(dlyvscp); // So we can find it later
|
||||
// Make new ACTIVE with identical sensitivity tree
|
||||
AstActive* newactp = createActivePost(nodep);
|
||||
dlyvscp->user2p(newactp);
|
||||
newactp->addStmtsp(prep); // Add to FRONT of statements
|
||||
newactp->addStmtsp(postp);
|
||||
}
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
|
||||
newrefp->user2(true); // No reason to do it again
|
||||
nodep->replaceWith(newrefp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (!m_inDly && nodep->lvalue()) {
|
||||
//UINFO(9,"NBA "<<nodep<<endl);
|
||||
if (!m_inInitial) {
|
||||
UINFO(4,"AssignNDlyVar: "<<nodep<<endl);
|
||||
markVarUsage(nodep->varScopep(), VU_NONDLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeFor* nodep) {
|
||||
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin");
|
||||
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin");
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) {
|
||||
bool oldloop = m_inLoop;
|
||||
m_inLoop = true;
|
||||
bool oldloop = m_inLoop;
|
||||
m_inLoop = true;
|
||||
iterateChildren(nodep);
|
||||
m_inLoop = oldloop;
|
||||
m_inLoop = oldloop;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
@@ -452,17 +460,17 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit DelayedVisitor(AstNetlist* nodep) {
|
||||
m_inDly = false;
|
||||
m_activep=NULL;
|
||||
m_cfuncp=NULL;
|
||||
m_nextDlyp=NULL;
|
||||
m_inLoop = false;
|
||||
m_inInitial = false;
|
||||
m_inDly = false;
|
||||
m_activep = NULL;
|
||||
m_cfuncp = NULL;
|
||||
m_nextDlyp = NULL;
|
||||
m_inLoop = false;
|
||||
m_inInitial = false;
|
||||
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DelayedVisitor() {
|
||||
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
|
||||
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user