mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfcd412e5f | ||
|
|
307549e8a6 | ||
|
|
09b79a731a | ||
|
|
4544a2fd99 | ||
|
|
e09c918198 | ||
|
|
9bf6135f6d | ||
|
|
d472ef63e9 | ||
|
|
f1c04b5d25 | ||
|
|
1cf29c4d20 | ||
|
|
977a767477 | ||
|
|
771a301f66 | ||
|
|
a9954a71c3 | ||
|
|
967dc24fda | ||
|
|
e1e45cf13c | ||
|
|
bd89c71694 | ||
|
|
633131b984 | ||
|
|
99db18b6c3 | ||
|
|
961a8e190f | ||
|
|
045ff25f80 | ||
|
|
66209d1114 | ||
|
|
3f1fe2d18a | ||
|
|
8b6fb26272 | ||
|
|
881362bb04 | ||
|
|
239ef1ae6d | ||
|
|
9eaec3b5c1 | ||
|
|
dd4aa948ae | ||
|
|
12fa085b26 | ||
|
|
9d6b0d7dd6 | ||
|
|
a1405dac8e | ||
|
|
cb115e1a15 | ||
|
|
1c7eb89df5 | ||
|
|
6e91f82a7d | ||
|
|
17123cd0e0 | ||
|
|
3f0c5b7550 | ||
|
|
f6e0e9adec | ||
|
|
dea1531f40 | ||
|
|
edcfaa888d | ||
|
|
28b9db1903 | ||
|
|
9e64be71a7 | ||
|
|
99a8845a96 | ||
|
|
be4692772c | ||
|
|
4f315d9078 | ||
|
|
f133c4d0b3 | ||
|
|
8490046028 | ||
|
|
d6ac351dcb | ||
|
|
502bd6cbd6 | ||
|
|
d7b6b53c4d | ||
|
|
96725b3431 | ||
|
|
8686ed9b00 | ||
|
|
32e1207782 | ||
|
|
900115bb2d | ||
|
|
3469c78153 | ||
|
|
baa634343b | ||
|
|
1471f5691a | ||
|
|
e556269692 | ||
|
|
d1af69f91d | ||
|
|
fa904f386c | ||
|
|
a4daae66de | ||
|
|
c813026566 | ||
|
|
314cd92129 | ||
|
|
3bc260c55d | ||
|
|
4c0f95af10 | ||
|
|
35fe205fd6 | ||
|
|
70cf2582d6 | ||
|
|
ec620387af | ||
|
|
e1e4bde125 | ||
|
|
b947391662 | ||
|
|
edc0f442e4 | ||
|
|
562b9551bf | ||
|
|
f74af949da | ||
|
|
81e806e895 | ||
|
|
7d4958264a | ||
|
|
88b87429dc | ||
|
|
429f463032 | ||
|
|
af4a39603c | ||
|
|
a13d230d8c | ||
|
|
b7bd5956ea | ||
|
|
1a36e05cc6 | ||
|
|
71265a8ec9 | ||
|
|
3a720204c2 | ||
|
|
a95f58749f | ||
|
|
9e58ede480 | ||
|
|
cf2ac30f66 | ||
|
|
68b22a4a3d | ||
|
|
875dc9a27e | ||
|
|
1332446f2a | ||
|
|
6b798830c9 | ||
|
|
ffa23b59b8 | ||
|
|
7d27461054 | ||
|
|
b3f318d8ab | ||
|
|
4a14788c9b | ||
|
|
5560da2934 | ||
|
|
43ce048f9e | ||
|
|
b045111a67 | ||
|
|
654571bd1c | ||
|
|
a3fd105acb | ||
|
|
97561bf064 | ||
|
|
a4820fc700 | ||
|
|
ee469eedaf | ||
|
|
a49d819437 | ||
|
|
cbb9288cb1 | ||
|
|
173efbc829 | ||
|
|
0fef3b02ec | ||
|
|
d1ee6689c4 | ||
|
|
b3eab9d78a | ||
|
|
defd05bdd7 | ||
|
|
a7e5cccf33 | ||
|
|
0f857ce423 | ||
|
|
83cf1882de | ||
|
|
e17543d6d8 | ||
|
|
dacf45fea9 | ||
|
|
13a20c5ee9 | ||
|
|
58dfe9d071 | ||
|
|
6c5cc885a6 | ||
|
|
2c44ab61ac | ||
|
|
0847ea57ae | ||
|
|
9af84c71f5 | ||
|
|
4ce77d3e68 | ||
|
|
3c67791624 | ||
|
|
a281c00752 | ||
|
|
2ca418288d | ||
|
|
bea34d0ced | ||
|
|
a5f2ce0dad | ||
|
|
07ae7146e3 | ||
|
|
8548ecfdac | ||
|
|
7e54ff1b37 | ||
|
|
a4b9745e6e | ||
|
|
8612ba2b8d | ||
|
|
fea6e047e2 | ||
|
|
01ef7122e9 | ||
|
|
ac43e7322b | ||
|
|
02a3f2089f | ||
|
|
33ad834106 | ||
|
|
efe739c52f | ||
|
|
5cd531a5e0 | ||
|
|
0e8dfdbb71 | ||
|
|
ec32a9b976 | ||
|
|
1aae6df669 | ||
|
|
f7641d2ecc | ||
|
|
5f27c41ee3 | ||
|
|
51df77099a | ||
|
|
b8b7e5c978 | ||
|
|
dddcfeb482 | ||
|
|
8d63f83177 | ||
|
|
d43e9aa2c5 | ||
|
|
a2c1b7f827 | ||
|
|
2fff92fe78 | ||
|
|
ba9af4aabf | ||
|
|
ef02678305 | ||
|
|
6990db70fa | ||
|
|
af5616c077 | ||
|
|
009f053d6e | ||
|
|
6903c52ef7 | ||
|
|
aa65dfb1bc | ||
|
|
84a2cd0ec7 | ||
|
|
8e7559c6c9 | ||
|
|
951521320a | ||
|
|
abc0eb92a0 | ||
|
|
c514bb26e8 | ||
|
|
5cb6474cc6 | ||
|
|
b8eb6368e9 | ||
|
|
6239deb1df | ||
|
|
89c2a6070a | ||
|
|
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 |
+1
-1
@@ -2,7 +2,7 @@ Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-modernize*,-hicpp*,-and
|
|||||||
WarningsAsErrors: ''
|
WarningsAsErrors: ''
|
||||||
HeaderFilterRegex: ''
|
HeaderFilterRegex: ''
|
||||||
FormatStyle: none
|
FormatStyle: none
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||||
value: '2'
|
value: '2'
|
||||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
*.v linguist-language=SystemVerilog
|
||||||
|
*.vh linguist-language=SystemVerilog
|
||||||
|
*.sv linguist-language=SystemVerilog
|
||||||
|
nodist linguist-detectable=false
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
# DESCRIPTION: Travis-CI config
|
||||||
|
#
|
||||||
|
# Copyright 2003-2019 by Todd Strader. 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.
|
||||||
|
|
||||||
|
language: cpp
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# Non-cron build will just run on whatever linux flavor we get
|
||||||
|
- if: type != cron
|
||||||
|
os: linux
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--dist
|
||||||
|
- if: type != cron
|
||||||
|
os: linux
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vlt
|
||||||
|
- if: type != cron
|
||||||
|
os: linux
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vltmt
|
||||||
|
# Cron builds try different OS/compiler combinations
|
||||||
|
- if: type = cron
|
||||||
|
dist: xenial
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--dist
|
||||||
|
- if: type = cron
|
||||||
|
dist: xenial
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vlt
|
||||||
|
- if: type = cron
|
||||||
|
dist: xenial
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vltmt
|
||||||
|
- if: type = cron
|
||||||
|
dist: xenial
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--dist
|
||||||
|
- if: type = cron
|
||||||
|
dist: xenial
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vlt
|
||||||
|
- if: type = cron
|
||||||
|
dist: xenial
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vltmt
|
||||||
|
# - if: type = cron
|
||||||
|
# os: osx
|
||||||
|
# compiler: gcc
|
||||||
|
# env:
|
||||||
|
# - SCENARIOS=--dist
|
||||||
|
# - if: type = cron
|
||||||
|
# os: osx
|
||||||
|
# compiler: gcc
|
||||||
|
# env:
|
||||||
|
# - SCENARIOS=--vlt
|
||||||
|
# - if: type = cron
|
||||||
|
# os: osx
|
||||||
|
# compiler: gcc
|
||||||
|
# env:
|
||||||
|
# - SCENARIOS=--vltmt
|
||||||
|
- if: type = cron
|
||||||
|
dist: trusty
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--dist
|
||||||
|
- if: type = cron
|
||||||
|
dist: trusty
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vlt
|
||||||
|
- if: type = cron
|
||||||
|
dist: trusty
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- SCENARIOS=--vltmt
|
||||||
|
|
||||||
|
cache: ccache
|
||||||
|
|
||||||
|
# Some tests require gdb
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- gdb
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
# Perl modules needed for testing
|
||||||
|
- yes yes | sudo cpan -fi Unix::Processors Parallel::Forker Bit::Vector
|
||||||
|
before_script:
|
||||||
|
- export VLT_JOBS=$((`nproc` + 1))
|
||||||
|
- export OBJCACHE=ccache
|
||||||
|
- export VERILATOR_ROOT=`pwd`
|
||||||
|
- autoconf
|
||||||
|
script:
|
||||||
|
- >
|
||||||
|
./configure --enable-maintainer-mode --enable-longtests &&
|
||||||
|
make -j $VLT_JOBS &&
|
||||||
|
make test_regress DRIVER_FLAGS="-j 0 --quiet --rerun"
|
||||||
|
after_script:
|
||||||
|
- ccache -s
|
||||||
@@ -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,99 @@ Revision history for Verilator
|
|||||||
|
|
||||||
The contributors that suggested a given feature are shown in []. Thanks!
|
The contributors that suggested a given feature are shown in []. Thanks!
|
||||||
|
|
||||||
|
* Verilator 4.020 2019-10-06
|
||||||
|
|
||||||
|
*** Support $fseek, $ftell, $frewind, bug1496. [Howard Su]
|
||||||
|
|
||||||
|
*** Add --public-flat-rw, bug1511. [Stefan Wallentowitz]
|
||||||
|
|
||||||
|
*** Support vpiModule, bug1469. [Stefan Wallentowitz]
|
||||||
|
|
||||||
|
**** Make Syms file honor --output-split-cfuncs, bug1499. [Todd Strader]
|
||||||
|
|
||||||
|
**** Fix make test with no VERILATOR_ROOT, bug1494. [Ahmed El-Mahmoudy]
|
||||||
|
|
||||||
|
**** Fix error on multidimensional cells, bug1505. [Anderson Ignacio Da Silva]
|
||||||
|
|
||||||
|
**** Fix config_rev revision detection on old versions.
|
||||||
|
|
||||||
|
**** Fix false warning on backward indexing, bug1507. [Hao Shi]
|
||||||
|
|
||||||
|
**** Fix vpiType accessor, bug1509, bug1510. [Stefan Wallentowitz]
|
||||||
|
|
||||||
|
**** Fix ugly error on interface misuse, bug1525. [Bogdan Vukobratovic]
|
||||||
|
|
||||||
|
**** Fix misc bad-syntax crashes, bug1529-bug1533. [Eric Rippey]
|
||||||
|
|
||||||
|
**** Fix case statements with strings, bug1536. [Philipp Wagner]
|
||||||
|
|
||||||
|
|
||||||
|
* Verilator 4.018 2019-08-29
|
||||||
|
|
||||||
|
** When showing an error, show source code and offer suggestions of replacements.
|
||||||
|
|
||||||
|
** When showing an error, show the instance location, bug1305. [Todd Strader]
|
||||||
|
|
||||||
|
*** Add --rr, bug1481. [Todd Strader]
|
||||||
|
|
||||||
|
*** Change MULTITOP to warning to help linting, see manual.
|
||||||
|
|
||||||
|
*** Add XSim support to driver.pl, bug1493. [Todd Strader]
|
||||||
|
|
||||||
|
**** Show included-from filenames in warnings, bug1439. [Todd Strader]
|
||||||
|
|
||||||
|
**** Fix elaboration time errors, bug1429. [Udi Finkelstein]
|
||||||
|
|
||||||
|
**** Fix not reporting some duplicate signals/ports, bug1462. [Peter Gerst]
|
||||||
|
|
||||||
|
**** Fix not in array context on non-power-of-two slices, msg2946. [Yu Sheng Lin]
|
||||||
|
|
||||||
|
**** Fix system compile flags injection. [Gianfranco Costamagna]
|
||||||
|
|
||||||
|
**** Fix enum values not being sized based on parent, bug1442. [Dan Petrisko]
|
||||||
|
|
||||||
|
**** Fix internal error on gate optimization of assign, bug1475. [Oyvind Harboe]
|
||||||
|
|
||||||
|
**** Add --dpi-hdr-only, bug1491. [Todd Strader]
|
||||||
|
|
||||||
|
|
||||||
|
* Verilator 4.016 2019-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
|
* Verilator 4.014 2019-05-08
|
||||||
|
|
||||||
*** Add --trace-fst-thread.
|
*** Add --trace-fst-thread.
|
||||||
@@ -3245,7 +3338,7 @@ of input ports exists for tracing.
|
|||||||
* Verilator 0.0 1994/7/8
|
* Verilator 0.0 1994/7/8
|
||||||
|
|
||||||
**** First code written.
|
**** First code written.
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
This uses outline mode in Emacs. See C-h m [M-x describe-mode].
|
This uses outline mode in Emacs. See C-h m [M-x describe-mode].
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
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
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
+3
-1
@@ -14,6 +14,7 @@
|
|||||||
.*\.key
|
.*\.key
|
||||||
.*\.vcd
|
.*\.vcd
|
||||||
.*\.1
|
.*\.1
|
||||||
|
\.travis\.yml
|
||||||
/obj_dir/
|
/obj_dir/
|
||||||
/obj_dbg/
|
/obj_dbg/
|
||||||
/obj_nc/
|
/obj_nc/
|
||||||
@@ -28,6 +29,8 @@
|
|||||||
^Makefile$
|
^Makefile$
|
||||||
bin/verilator_bin.*
|
bin/verilator_bin.*
|
||||||
bin/verilator_coverage_bin.*
|
bin/verilator_coverage_bin.*
|
||||||
|
docs/Makefile$
|
||||||
|
docs/doxygen-doc/.*
|
||||||
src/Makefile$
|
src/Makefile$
|
||||||
src/Makefile_obj$
|
src/Makefile_obj$
|
||||||
include/verilated.mk$
|
include/verilated.mk$
|
||||||
@@ -47,7 +50,6 @@ nodist/
|
|||||||
/simv.daidir/
|
/simv.daidir/
|
||||||
/vc_hdrs.h$
|
/vc_hdrs.h$
|
||||||
/csrc/
|
/csrc/
|
||||||
doxygen-doc/.*
|
|
||||||
obj_dir/.*
|
obj_dir/.*
|
||||||
TAGS
|
TAGS
|
||||||
.*~
|
.*~
|
||||||
|
|||||||
+35
-57
@@ -83,7 +83,7 @@ infodir = @infodir@
|
|||||||
pkgdatadir = @pkgdatadir@
|
pkgdatadir = @pkgdatadir@
|
||||||
|
|
||||||
# Directory in which to install pkgconfig file
|
# Directory in which to install pkgconfig file
|
||||||
# Generall ${prefix}/share/pkgconfig
|
# Generally ${prefix}/share/pkgconfig
|
||||||
pkgconfigdir = @pkgconfigdir@
|
pkgconfigdir = @pkgconfigdir@
|
||||||
|
|
||||||
# Directory in which to install data across multiple architectures
|
# Directory in which to install data across multiple architectures
|
||||||
@@ -107,16 +107,17 @@ SUBDIRS = src test_regress \
|
|||||||
examples/hello_world_c examples/hello_world_sc \
|
examples/hello_world_c examples/hello_world_sc \
|
||||||
examples/tracing_c examples/tracing_sc \
|
examples/tracing_c examples/tracing_sc \
|
||||||
|
|
||||||
INFOS = README README.html README.pdf internals.txt internals.html \
|
INFOS = README README.html README.pdf \
|
||||||
internals.pdf verilator.txt verilator.html verilator.pdf \
|
verilator.txt verilator.html verilator.pdf
|
||||||
$(VL_INST_MAN_FILES)
|
|
||||||
|
|
||||||
# Files that can be generated, but should be up to date for a distribution.
|
# Files that can be generated, but should be up to date for a distribution.
|
||||||
DISTDEP = info Makefile
|
DISTDEP = info Makefile
|
||||||
|
|
||||||
DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
DISTFILES_INC = $(INFOS) .gitignore \
|
||||||
*.in *.ac \
|
*.in *.ac \
|
||||||
Changes TODO \
|
Artistic \
|
||||||
|
Changes \
|
||||||
|
LICENSE \
|
||||||
MANIFEST.SKIP \
|
MANIFEST.SKIP \
|
||||||
bin/verilator \
|
bin/verilator \
|
||||||
bin/verilator_coverage \
|
bin/verilator_coverage \
|
||||||
@@ -124,7 +125,15 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
|||||||
bin/verilator_gantt \
|
bin/verilator_gantt \
|
||||||
bin/verilator_includer \
|
bin/verilator_includer \
|
||||||
bin/verilator_profcfunc \
|
bin/verilator_profcfunc \
|
||||||
doxygen-mainpage doxygen.config verilator_logo.png \
|
docs/.gitignore \
|
||||||
|
docs/CONTRIBUTING.adoc \
|
||||||
|
docs/CONTRIBUTORS \
|
||||||
|
docs/Makefile.in \
|
||||||
|
docs/TODO \
|
||||||
|
docs/doxygen-mainpage \
|
||||||
|
docs/doxygen.config \
|
||||||
|
docs/internals.adoc \
|
||||||
|
docs/verilator_logo.png \
|
||||||
install-sh configure *.pod \
|
install-sh configure *.pod \
|
||||||
include/*.[chv]* \
|
include/*.[chv]* \
|
||||||
include/*.in \
|
include/*.in \
|
||||||
@@ -172,12 +181,21 @@ INST_PROJ_BIN_FILES = \
|
|||||||
|
|
||||||
DISTFILES := $(DISTFILES_INC)
|
DISTFILES := $(DISTFILES_INC)
|
||||||
|
|
||||||
|
EXAMPLES_FIRST = \
|
||||||
|
examples/hello_world_c \
|
||||||
|
examples/hello_world_sc \
|
||||||
|
|
||||||
|
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
|
||||||
|
|
||||||
ifeq ($(OBJCACHE_JOBS),)
|
ifeq ($(OBJCACHE_JOBS),)
|
||||||
ifneq ($(OBJCACHE_HOSTS),)
|
ifneq ($(OBJCACHE_HOSTS),)
|
||||||
export OBJCACHE_JOBS := -j $(shell objcache --jobs "$(OBJCACHE_HOSTS)")
|
export OBJCACHE_JOBS := -j $(shell objcache --jobs "$(OBJCACHE_HOSTS)")
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||||
|
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
|
||||||
|
|
||||||
default: all
|
default: all
|
||||||
all: all_nomsg msg_test
|
all: all_nomsg msg_test
|
||||||
all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
||||||
@@ -218,13 +236,15 @@ test_regress: all_nomsg
|
|||||||
$(MAKE) -C test_regress
|
$(MAKE) -C test_regress
|
||||||
|
|
||||||
examples: all_nomsg
|
examples: all_nomsg
|
||||||
for p in examples/* ; do \
|
for p in $(EXAMPLES) ; do \
|
||||||
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
|
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
.PHONY: docs
|
||||||
|
docs: info
|
||||||
|
|
||||||
info: $(INFOS)
|
info: $(INFOS)
|
||||||
|
|
||||||
# Use --no-split to avoid creating filenames > 14 chars.
|
|
||||||
%.1: ${srcdir}/bin/%
|
%.1: ${srcdir}/bin/%
|
||||||
pod2man $< $@
|
pod2man $< $@
|
||||||
|
|
||||||
@@ -259,31 +279,12 @@ README.pdf: README.pod Makefile $(POD2LATEXFIX)
|
|||||||
pdflatex README.tex
|
pdflatex README.tex
|
||||||
-rm -f README.toc README.aux README.idx README.out
|
-rm -f README.toc README.aux README.idx README.out
|
||||||
|
|
||||||
internals.txt: internals.pod
|
|
||||||
-rm -f $@
|
|
||||||
$(POD2TEXT) --loose $< > $@
|
|
||||||
|
|
||||||
internals.html: internals.pod
|
|
||||||
pod2html $< >$@
|
|
||||||
|
|
||||||
# PDF needs DIST variables; but having configure.ac as dependency isn't detected
|
|
||||||
internals.pdf: internals.pod Makefile $(POD2LATEXFIX)
|
|
||||||
pod2latex --full --out internals.tex $<
|
|
||||||
$(PERL) $(POD2LATEXFIX) "$(DISTTITLE) Internals Manual" "${DISTDATE}" < internals.tex > internals2.tex
|
|
||||||
mv internals2.tex internals.tex
|
|
||||||
pdflatex internals.tex
|
|
||||||
pdflatex internals.tex
|
|
||||||
-rm -f internals.toc internals.aux internals.idx internals.out
|
|
||||||
|
|
||||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
||||||
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||||
# Some scripts go into both the search path and pkgdatadir,
|
# Some scripts go into both the search path and pkgdatadir,
|
||||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||||
|
|
||||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
|
||||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
|
|
||||||
|
|
||||||
VL_INST_INC_BLDDIR_FILES = \
|
VL_INST_INC_BLDDIR_FILES = \
|
||||||
include/verilated_config.h \
|
include/verilated_config.h \
|
||||||
include/verilated.mk \
|
include/verilated.mk \
|
||||||
@@ -367,34 +368,9 @@ install-here: installman ftp
|
|||||||
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
|
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
|
||||||
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
|
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
|
||||||
|
|
||||||
DIRPROJECT := $(shell project_dir --project)
|
|
||||||
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
|
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
|
||||||
INST_PROJ_CVS = cp_if_cvs_diff
|
INST_PROJ_CVS = cp_if_cvs_diff
|
||||||
|
|
||||||
install-project: dist
|
|
||||||
@echo "Install-project to $(DIRPROJECT)"
|
|
||||||
strip bin/verilator_bin*
|
|
||||||
strip bin/verilator_coverage_bin*
|
|
||||||
$(MAKE) install-project-quick
|
|
||||||
for p in $(VL_INST_MAN_FILES) ; do \
|
|
||||||
$(INSTALL_DATA) $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
|
|
||||||
done
|
|
||||||
$(INST_PROJ_CVS) $(DISTNAME).tgz $(DIRPROJECT)/hw/utils/verilator/verilator.tgz
|
|
||||||
rm $(DISTNAME).tgz
|
|
||||||
|
|
||||||
install-project-quick:
|
|
||||||
ifeq ($(CFG_WITH_DEFENV),yes)
|
|
||||||
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
|
|
||||||
false
|
|
||||||
endif
|
|
||||||
@echo "Install-project-quick (no strip) to $(DIRPROJECT)"
|
|
||||||
for p in $(INST_PROJ_FILES) ; do \
|
|
||||||
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p; \
|
|
||||||
done
|
|
||||||
for p in $(INST_PROJ_BIN_FILES) ; do \
|
|
||||||
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p-$(DIRPROJECT_ARCH); \
|
|
||||||
done
|
|
||||||
|
|
||||||
install-cadtools: dist
|
install-cadtools: dist
|
||||||
@echo "Install-project to $(CAD_DIR)"
|
@echo "Install-project to $(CAD_DIR)"
|
||||||
strip bin/verilator_bin*
|
strip bin/verilator_bin*
|
||||||
@@ -435,7 +411,8 @@ CPPCHECK_H = $(wildcard \
|
|||||||
$(srcdir)/src/*.h )
|
$(srcdir)/src/*.h )
|
||||||
CPPCHECK = src/cppcheck_filtered
|
CPPCHECK = src/cppcheck_filtered
|
||||||
CPPCHECK_FLAGS = --enable=all --inline-suppr \
|
CPPCHECK_FLAGS = --enable=all --inline-suppr \
|
||||||
--suppress=unusedScopedObject --suppress=cstyleCast --suppress=useInitializationList
|
--suppress=unusedScopedObject --suppress=cstyleCast --suppress=useInitializationList \
|
||||||
|
--suppress=nullPointerRedundantCheck
|
||||||
CPPCHECK_FLAGS += --xml
|
CPPCHECK_FLAGS += --xml
|
||||||
CPPCHECK_DEP = $(subst .cpp,.cppcheck,$(CPPCHECK_CPP))
|
CPPCHECK_DEP = $(subst .cpp,.cppcheck,$(CPPCHECK_CPP))
|
||||||
CPPCHECK_INC = -I$(srcdir)/include -I$(srcdir)/src/obj_dbg -I$(srcdir)/src
|
CPPCHECK_INC = -I$(srcdir)/include -I$(srcdir)/src/obj_dbg -I$(srcdir)/src
|
||||||
@@ -511,11 +488,12 @@ clean mostlyclean distclean maintainer-clean::
|
|||||||
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
|
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
|
||||||
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
|
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
|
||||||
rm -f *.tex
|
rm -f *.tex
|
||||||
rm -rf test_*/obj_dir
|
|
||||||
rm -rf examples/*/obj_dir examples/*/logs
|
rm -rf examples/*/obj_dir examples/*/logs
|
||||||
|
rm -rf test_*/obj_dir
|
||||||
|
rm -rf nodist/obj_dir
|
||||||
|
|
||||||
distclean maintainer-clean::
|
distclean maintainer-clean::
|
||||||
rm -f *.info* *.1 $(INFOS)
|
rm -f *.info* *.1 $(INFOS) $(VL_INST_MAN_FILES)
|
||||||
rm -f Makefile config.status config.cache config.log TAGS
|
rm -f Makefile config.status config.cache config.log TAGS
|
||||||
rm -f verilator_bin* verilator_coverage_bin*
|
rm -f verilator_bin* verilator_coverage_bin*
|
||||||
rm -f bin/verilator_bin* bin/verilator_coverage_bin*
|
rm -f bin/verilator_bin* bin/verilator_coverage_bin*
|
||||||
@@ -530,7 +508,7 @@ TAGS: $(TAGFILES)
|
|||||||
.PHONY: doxygen
|
.PHONY: doxygen
|
||||||
|
|
||||||
doxygen:
|
doxygen:
|
||||||
$(DOXYGEN) doxygen.config
|
$(MAKE) -C docs doxygen
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Test targets
|
# Test targets
|
||||||
|
|||||||
+146
-73
@@ -3,32 +3,19 @@
|
|||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=for html <a href="https://travis-ci.com/verilator/verilator"><img src="https://travis-ci.com/verilator/verilator.svg?branch=master"></a>
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
This is the Verilator package README file.
|
Welcome to Verilator. This is the Verilator package's README file.
|
||||||
|
|
||||||
=head1 DISTRIBUTION
|
This document describes how to initially install Verilator. For more
|
||||||
|
general information please see L<http://verilator.org>.
|
||||||
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.
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
Verilator converts synthesizable (generally not behavioral) Verilog code
|
Verilator is a simulator which "Verilates" synthesizable (generally not
|
||||||
into C++ or SystemC code. It is not a complete simulator, just a
|
behavioral) Verilog code into "Verilated" C++ or SystemC code.
|
||||||
translator.
|
|
||||||
|
|
||||||
Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It
|
Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It
|
||||||
reads the specified Verilog code, lints it, and optionally adds coverage
|
reads the specified Verilog code, lints it, and optionally adds coverage
|
||||||
@@ -45,27 +32,77 @@ The resulting executable will perform the actual simulation.
|
|||||||
|
|
||||||
Verilator is developed and has primary testing on Ubuntu. Versions have
|
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
|
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
|
with minor porting on any Linux-ish platform. Verilator also works on
|
||||||
Windows under Cygwin, and Windows under MinGW (gcc -mno-cygwin). Verilated
|
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
|
=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>.
|
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
|
=over 4
|
||||||
|
|
||||||
=item
|
=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 bison # 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
|
=item
|
||||||
|
|
||||||
@@ -78,22 +115,32 @@ instead.)
|
|||||||
|
|
||||||
=item
|
=item
|
||||||
|
|
||||||
To use Verilator you will need the C<perl>, C<make> (or C<gmake>), and
|
To use Verilator FST tracing you will need the C<gtkwave> and C<libgz> (and on
|
||||||
C<g++> (or C<clang>) packages.
|
|
||||||
|
|
||||||
To use Verilator FST tracing you will need the C<gtkwave> and C<libgz> (on
|
|
||||||
Ubuntu C<zlibc> C<zlib1g> C<zlib1g-dev>) packages installed.
|
Ubuntu C<zlibc> C<zlib1g> C<zlib1g-dev>) packages installed.
|
||||||
|
|
||||||
To compile Verilator in addition to the above you need the C<flex>,
|
=back
|
||||||
C<bison> and C<texi2html> packages installed.
|
|
||||||
|
|
||||||
=item
|
=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
|
=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,
|
Note Verilator builds the current value of VERILATOR_ROOT, SYSTEMC_INCLUDE,
|
||||||
and SYSTEMC_LIBDIR as defaults into the executable, so try to have them
|
and SYSTEMC_LIBDIR as defaults into the executable, so try to have them
|
||||||
@@ -103,25 +150,32 @@ correct before configuring.
|
|||||||
|
|
||||||
=item 1.
|
=item 1.
|
||||||
|
|
||||||
Our personal favorite is to always run Verilator from the kit directory.
|
Our personal favorite is to always run Verilator from its git directory.
|
||||||
This allows the easiest experimentation and upgrading. It's also how most
|
This allows the easiest experimentation and upgrading, and allows many
|
||||||
EDA tools operate; to run you point to the tarball, no install is needed.
|
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
|
export VERILATOR_ROOT=`pwd` # if your shell is bash
|
||||||
setenv VERILATOR_ROOT `pwd` # if your shell is csh
|
setenv VERILATOR_ROOT `pwd` # if your shell is csh
|
||||||
./configure
|
./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.
|
=item 2.
|
||||||
|
|
||||||
To install globally onto a "cad" disk with multiple versions of every tool,
|
You may eventually be installing onto a project/company-wide "CAD" tools
|
||||||
and add it to path using Modules/modulecmd:
|
disk that may support multiple versions of every tool.
|
||||||
|
|
||||||
unset VERILATOR_ROOT # if your shell is bash
|
unset VERILATOR_ROOT # if your shell is bash
|
||||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||||
# For the tarball, use the version number instead of git describe
|
# For the tarball, use the version number instead of git describe
|
||||||
./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"`
|
./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}
|
set install_root /CAD_DISK/verilator/{version-number-used-above}
|
||||||
unsetenv VERILATOR_ROOT
|
unsetenv VERILATOR_ROOT
|
||||||
@@ -131,50 +185,49 @@ and add it to path using Modules/modulecmd:
|
|||||||
|
|
||||||
=item 3.
|
=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
|
unset VERILATOR_ROOT # if your shell is bash
|
||||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||||
./configure
|
./configure
|
||||||
|
|
||||||
|
Then after installing (below) the binary directories should already be in
|
||||||
|
your PATH.
|
||||||
|
|
||||||
=item 4.
|
=item 4.
|
||||||
|
|
||||||
Alternatively you can configure a prefix that install will populate, as
|
Finally, you may eventually install it into a specific installation prefix,
|
||||||
most GNU tools support:
|
as most GNU tools support:
|
||||||
|
|
||||||
unset VERILATOR_ROOT # if your shell is bash
|
unset VERILATOR_ROOT # if your shell is bash
|
||||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||||
./configure --prefix /opt/verilator-VERSION
|
./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.
|
PATH.
|
||||||
|
|
||||||
=back
|
=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
|
=item
|
||||||
|
|
||||||
Type C<make> to compile Verilator.
|
Type C<make> to compile Verilator.
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
Type C<make test> to check the compilation.
|
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
|
=item
|
||||||
|
|
||||||
If you used the VERILATOR_ROOT scheme you're done. Programs should set the
|
If you used the prefix scheme, now do a C<make install>.
|
||||||
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>. To run verilator,
|
=item
|
||||||
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.
|
|
||||||
|
|
||||||
You may now wish to consult the examples directory. Type C<make> inside any
|
You may now wish to consult the examples directory. Type C<make> inside any
|
||||||
example directory to run the example.
|
example directory to run the example.
|
||||||
@@ -187,25 +240,45 @@ Detailed documentation and the man page can be seen by running:
|
|||||||
|
|
||||||
bin/verilator --help
|
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
|
bin/verilator => Compiler Wrapper invoked to Verilate code
|
||||||
include/ => Files that should be in your -I compiler path
|
docs/ => Additional documentation
|
||||||
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
|
|
||||||
examples/hello_world_c => Example simple Verilog->C++ conversion
|
examples/hello_world_c => Example simple Verilog->C++ conversion
|
||||||
examples/hello_world_sc => Example simple Verilog->SystemC conversion
|
examples/hello_world_sc => Example simple Verilog->SystemC conversion
|
||||||
examples/tracing_c => Example Verilog->C++ with tracing
|
examples/tracing_c => Example Verilog->C++ with tracing
|
||||||
examples/tracing_sc => Example Verilog->SystemC 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
|
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.
|
||||||
|
|||||||
+122
-50
@@ -17,13 +17,6 @@ eval 'exec perl -wS $0 ${1+"$@"}'
|
|||||||
|
|
||||||
require 5.006_001;
|
require 5.006_001;
|
||||||
use warnings;
|
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};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
use FindBin qw($RealBin $RealScript);
|
use FindBin qw($RealBin $RealScript);
|
||||||
use IO::File;
|
use IO::File;
|
||||||
@@ -41,7 +34,9 @@ autoflush STDERR 1;
|
|||||||
|
|
||||||
$Debug = 0;
|
$Debug = 0;
|
||||||
my $opt_gdb;
|
my $opt_gdb;
|
||||||
|
my $opt_rr;
|
||||||
my $opt_gdbbt;
|
my $opt_gdbbt;
|
||||||
|
my $opt_quiet_exit;
|
||||||
|
|
||||||
# No arguments can't do anything useful. Give help
|
# No arguments can't do anything useful. Give help
|
||||||
if ($#ARGV < 0) {
|
if ($#ARGV < 0) {
|
||||||
@@ -61,11 +56,13 @@ Getopt::Long::config("no_auto_abbrev","pass_through");
|
|||||||
if (! GetOptions(
|
if (! GetOptions(
|
||||||
# Major operating modes
|
# Major operating modes
|
||||||
"help" => \&usage,
|
"help" => \&usage,
|
||||||
"debug:s" => \&debug,
|
"debug" => \&debug,
|
||||||
# "version!" => \&version, # Also passthru'ed
|
# "version!" => \&version, # Also passthru'ed
|
||||||
# Switches
|
# Switches
|
||||||
"gdb!" => \$opt_gdb,
|
"gdb!" => \$opt_gdb,
|
||||||
"gdbbt!" => \$opt_gdbbt,
|
"gdbbt!" => \$opt_gdbbt,
|
||||||
|
"quiet-exit!" => \$opt_quiet_exit,
|
||||||
|
"rr!" => \$opt_rr,
|
||||||
# Additional parameters
|
# Additional parameters
|
||||||
"<>" => sub {}, # Ignored
|
"<>" => sub {}, # Ignored
|
||||||
)) {
|
)) {
|
||||||
@@ -98,6 +95,10 @@ if ($opt_gdb) {
|
|||||||
." -ex \"run ".join(' ', @quoted_sw)."\""
|
." -ex \"run ".join(' ', @quoted_sw)."\""
|
||||||
." -ex 'set width 0'"
|
." -ex 'set width 0'"
|
||||||
." -ex 'bt'");
|
." -ex 'bt'");
|
||||||
|
} elsif ($opt_rr) {
|
||||||
|
# Record with rr
|
||||||
|
run ("rr record ".verilator_bin()
|
||||||
|
." ".join(' ', @quoted_sw));
|
||||||
} elsif ($opt_gdbbt && $Debug) {
|
} elsif ($opt_gdbbt && $Debug) {
|
||||||
# Run under GDB to get gdbbt
|
# Run under GDB to get gdbbt
|
||||||
run ("gdb"
|
run ("gdb"
|
||||||
@@ -105,7 +106,7 @@ if ($opt_gdb) {
|
|||||||
." --batch --quiet --return-child-result"
|
." --batch --quiet --return-child-result"
|
||||||
." -ex \"run ".join(' ', @quoted_sw)."\""
|
." -ex \"run ".join(' ', @quoted_sw)."\""
|
||||||
." -ex 'set width 0'"
|
." -ex 'set width 0'"
|
||||||
." -ex 'bt'");
|
." -ex 'bt' -ex 'quit'");
|
||||||
} else {
|
} else {
|
||||||
# Normal, non gdb
|
# Normal, non gdb
|
||||||
run(verilator_bin()." ".join(' ',@quoted_sw));
|
run(verilator_bin()." ".join(' ',@quoted_sw));
|
||||||
@@ -114,7 +115,7 @@ if ($opt_gdb) {
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
@@ -164,7 +165,8 @@ sub gdb_works {
|
|||||||
." --batch-silent --quiet --return-child-result"
|
." --batch-silent --quiet --return-child-result"
|
||||||
." -ex 'run -n'" # `echo -n`
|
." -ex 'run -n'" # `echo -n`
|
||||||
." -ex 'set width 0'"
|
." -ex 'set width 0'"
|
||||||
." -ex 'bt'");
|
." -ex 'bt'"
|
||||||
|
." -ex 'quit'");
|
||||||
my $status = $?;
|
my $status = $?;
|
||||||
return $status==0;
|
return $status==0;
|
||||||
}
|
}
|
||||||
@@ -185,9 +187,9 @@ sub run {
|
|||||||
warn "%Error: $command\n";
|
warn "%Error: $command\n";
|
||||||
}
|
}
|
||||||
if ($status & 127) {
|
if ($status & 127) {
|
||||||
if (($status & 127) == 4 # SIGILL
|
if (($status & 127) == 4 # SIGILL
|
||||||
|| ($status & 127) == 8 # SIGFPA
|
|| ($status & 127) == 8 # SIGFPA
|
||||||
|| ($status & 127) == 11) { # SIGSEGV
|
|| ($status & 127) == 11) { # SIGSEGV
|
||||||
warn "%Error: Verilator internal fault, sorry. Consider trying --debug --gdbbt\n" if !$Debug;
|
warn "%Error: Verilator internal fault, sorry. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||||
} elsif (($status & 127) == 6) { # SIGABRT
|
} elsif (($status & 127) == 6) { # SIGABRT
|
||||||
warn "%Error: Verilator aborted. Consider trying --debug --gdbbt\n" if !$Debug;
|
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;
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +298,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||||||
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
||||||
--default-language <lang> Default language to parse
|
--default-language <lang> Default language to parse
|
||||||
+define+<var>=<value> Set preprocessor define
|
+define+<var>=<value> Set preprocessor define
|
||||||
|
--dpi-hdr-only Only produce the DPI header file
|
||||||
--dump-defines Show preprocessor defines with -E
|
--dump-defines Show preprocessor defines with -E
|
||||||
--dump-tree Enable dumping .tree files
|
--dump-tree Enable dumping .tree files
|
||||||
--dump-treei <level> Enable dumping .tree files at a level
|
--dump-treei <level> Enable dumping .tree files at a level
|
||||||
@@ -311,7 +321,6 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||||||
--inhibit-sim Create function to turn off sim
|
--inhibit-sim Create function to turn off sim
|
||||||
--inline-mult <value> Tune module inlining
|
--inline-mult <value> Tune module inlining
|
||||||
-LDFLAGS <flags> Linker pre-object flags for makefile
|
-LDFLAGS <flags> Linker pre-object flags for makefile
|
||||||
-LDLIBS <flags> Linker library flags for makefile
|
|
||||||
--l2-name <value> Verilog scope name of the top module
|
--l2-name <value> Verilog scope name of the top module
|
||||||
--language <lang> Default language standard to parse
|
--language <lang> Default language standard to parse
|
||||||
+libext+<ext>+[ext]... Extensions for finding modules
|
+libext+<ext>+[ext]... Extensions for finding modules
|
||||||
@@ -345,10 +354,13 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||||||
--prof-threads Enable generating gantt chart data for threads
|
--prof-threads Enable generating gantt chart data for threads
|
||||||
--private Debugging; see docs
|
--private Debugging; see docs
|
||||||
--public Debugging; see docs
|
--public Debugging; see docs
|
||||||
|
--public-flat-rw Mark all variables, etc as public_flat_rw
|
||||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||||
|
--quiet-exit Don't print the command on failure
|
||||||
--relative-includes Resolve includes relative to current file
|
--relative-includes Resolve includes relative to current file
|
||||||
--no-relative-cfuncs Disallow 'this->' in generated functions
|
--no-relative-cfuncs Disallow 'this->' in generated functions
|
||||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||||
|
--rr Run Verilator and record with rr
|
||||||
--savable Enable model save-restore
|
--savable Enable model save-restore
|
||||||
--sc Create SystemC output
|
--sc Create SystemC output
|
||||||
--stats Create statistics file
|
--stats Create statistics file
|
||||||
@@ -730,6 +742,11 @@ Defines the given preprocessor symbol, or multiple symbols if separated by
|
|||||||
plusses. Similar to -D; +define is fairly standard across Verilog tools
|
plusses. Similar to -D; +define is fairly standard across Verilog tools
|
||||||
while -D is an alias for GCC compatibility.
|
while -D is an alias for GCC compatibility.
|
||||||
|
|
||||||
|
=item --dpi-hdr-only
|
||||||
|
|
||||||
|
Only generate the DPI header file. This option has no effect on the name
|
||||||
|
or location of the file.
|
||||||
|
|
||||||
=item --dump-defines
|
=item --dump-defines
|
||||||
|
|
||||||
With -E, suppress normal output, and instead print a list of all defines
|
With -E, suppress normal output, and instead print a list of all defines
|
||||||
@@ -765,8 +782,8 @@ will also go to standard out.
|
|||||||
|
|
||||||
=item --error-limit I<value>
|
=item --error-limit I<value>
|
||||||
|
|
||||||
After this number of errors or warnings are encountered, exit. Defaults to
|
After this number of errors are encountered, exit. Warnings are not
|
||||||
50.
|
counted in this limit. Defaults to 50.
|
||||||
|
|
||||||
=item --exe
|
=item --exe
|
||||||
|
|
||||||
@@ -1024,15 +1041,15 @@ developers.
|
|||||||
|
|
||||||
=item --output-split I<bytes>
|
=item --output-split I<bytes>
|
||||||
|
|
||||||
Enables splitting the output .cpp files into multiple outputs. When a
|
Enables splitting the output .cpp files into multiple outputs. When a C++
|
||||||
C++ file exceeds the specified number of operations, a new file will be
|
file exceeds the specified number of operations, a new file will be created
|
||||||
created at the next function boundary. In addition, any slow routines will
|
at the next function boundary. In addition, any infrequently executed
|
||||||
be placed into __Slow files. This accelerates compilation by as
|
"cold" routines will be placed into __Slow files. This accelerates
|
||||||
optimization can be disabled on the slow routines, and the remaining files
|
compilation by as optimization can be disabled on the routines in __Slow,
|
||||||
can be compiled on parallel machines. Using --output-split should have
|
and the remaining files can be compiled on parallel machines. Using
|
||||||
only a trivial impact on performance. With GCC 3.3 on a 2GHz Opteron,
|
--output-split should have only a trivial impact on performance. With GCC
|
||||||
--output-split 20000 will result in splitting into approximately
|
3.3 on a 2GHz Opteron, --output-split 20000 will result in splitting into
|
||||||
one-minute-compile chunks.
|
approximately one-minute-compile chunks.
|
||||||
|
|
||||||
=item --output-split-cfuncs I<statements>
|
=item --output-split-cfuncs I<statements>
|
||||||
|
|
||||||
@@ -1152,11 +1169,25 @@ inlining. This will also turn off inlining as if all modules had a
|
|||||||
/*verilator public_module*/, unless the module specifically enabled it with
|
/*verilator public_module*/, unless the module specifically enabled it with
|
||||||
/*verilator inline_module*/.
|
/*verilator inline_module*/.
|
||||||
|
|
||||||
|
=item --public-flat-rw
|
||||||
|
|
||||||
|
Declares all variables, ports and wires public as if they had /*verilator
|
||||||
|
public_flat_rw*/ comments. This will make them VPI accessible by their
|
||||||
|
flat name, but not turn off module inlining. This is particularly useful
|
||||||
|
in combination with --vpi. This may also in some rare cases result in
|
||||||
|
mis-simulation of generated clocks. Instead of this global switch, marking
|
||||||
|
only those signals that need public_flat_rw is typically significantly
|
||||||
|
better performing.
|
||||||
|
|
||||||
=item -pvalue+I<name>=I<value>
|
=item -pvalue+I<name>=I<value>
|
||||||
|
|
||||||
Overwrites the given parameter(s) of the toplevel module. See -G for a
|
Overwrites the given parameter(s) of the toplevel module. See -G for a
|
||||||
detailed description.
|
detailed description.
|
||||||
|
|
||||||
|
=item --quiet-exit
|
||||||
|
|
||||||
|
When exiting due to an error, do not display the "Command Failed" message.
|
||||||
|
|
||||||
=item --relative-includes
|
=item --relative-includes
|
||||||
|
|
||||||
When a file references an include file, resolve the filename relative to
|
When a file references an include file, resolve the filename relative to
|
||||||
@@ -1184,6 +1215,10 @@ printing. For example:
|
|||||||
will generate a PDF Vt_unoptflat_simple_2_35_unoptflat.dot.pdf from the DOT
|
will generate a PDF Vt_unoptflat_simple_2_35_unoptflat.dot.pdf from the DOT
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
=item --rr
|
||||||
|
|
||||||
|
Run Verilator and record with rr. See: rr-project.org.
|
||||||
|
|
||||||
=item --savable
|
=item --savable
|
||||||
|
|
||||||
Enable including save and restore functions in the generated model.
|
Enable including save and restore functions in the generated model.
|
||||||
@@ -1273,7 +1308,7 @@ good value.
|
|||||||
When the input Verilog contains more than one top level module, specifies
|
When the input Verilog contains more than one top level module, specifies
|
||||||
the name of the top level Verilog module to become the top, and sets the
|
the name of the top level Verilog module to become the top, and sets the
|
||||||
default for if --prefix is not used. This is not needed with standard
|
default for if --prefix is not used. This is not needed with standard
|
||||||
designs with only one top.
|
designs with only one top. See also the MULTITOP warning section.
|
||||||
|
|
||||||
=item --trace
|
=item --trace
|
||||||
|
|
||||||
@@ -1533,7 +1568,7 @@ ensure that X E<rarr> 1 triggers a C<posedge>.
|
|||||||
B<Note.> Some users have reported that using this option can affect
|
B<Note.> Some users have reported that using this option can affect
|
||||||
convergence, and that it may be necessary to use --converge-limit to
|
convergence, and that it may be necessary to use --converge-limit to
|
||||||
increase the number of convergence iterations. This may be another
|
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
|
=item --xml-only
|
||||||
|
|
||||||
@@ -1604,7 +1639,7 @@ posedge eval() and one negedge eval().
|
|||||||
|
|
||||||
=item +verilator+rand+reset+I<value>
|
=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 =
|
initialization technique. 0 = Reset to zeros. 1 = Reset to all-ones. 2 =
|
||||||
Randomize. See L</"Unknown states">.
|
Randomize. See L</"Unknown states">.
|
||||||
|
|
||||||
@@ -1857,7 +1892,7 @@ For -cc and -sc mode, it also creates:
|
|||||||
|
|
||||||
{prefix}.cpp // Top level C++ file
|
{prefix}.cpp // Top level C++ file
|
||||||
{prefix}.h // Top level header
|
{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}{__n}.cpp // Additional top C++ files (--output-split)
|
||||||
{prefix}{each_verilog_module}.cpp // Lower level internal C++ files
|
{prefix}{each_verilog_module}.cpp // Lower level internal C++ files
|
||||||
{prefix}{each_verilog_module}.h // Lower level internal header files
|
{prefix}{each_verilog_module}.h // Lower level internal header files
|
||||||
@@ -2353,7 +2388,7 @@ 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
|
manage the other N-1 threads. It's the client's responsibility not to
|
||||||
oversubscribe the available CPU cores. Under CPU oversubscription, the
|
oversubscribe the available CPU cores. Under CPU oversubscription, the
|
||||||
Verilated model should not livelock nor deadlock, however, you can expect
|
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.
|
threads and CPU cores.
|
||||||
|
|
||||||
With --trace-fst-thread, tracing occurs in a separate thread from the main
|
With --trace-fst-thread, tracing occurs in a separate thread from the main
|
||||||
@@ -2404,7 +2439,7 @@ completing after the $stop or $finish.
|
|||||||
If using --coverage, the coverage routines are fully thread safe.
|
If using --coverage, the coverage routines are fully thread safe.
|
||||||
|
|
||||||
If using --dpi, Verilator assumes pure DPI imports are 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
|
If using --savable, the save/restore classes are not multithreaded and are
|
||||||
must be called only by the eval thread.
|
must be called only by the eval thread.
|
||||||
@@ -2437,6 +2472,10 @@ Configuration files are parsed after the normal Verilog preprocessing, so
|
|||||||
`ifdefs, `defines, and comments may be used as if it were normal Verilog
|
`ifdefs, `defines, and comments may be used as if it were normal Verilog
|
||||||
code.
|
code.
|
||||||
|
|
||||||
|
Note that file or line-specific configuration only applies to files read
|
||||||
|
after the configuration file. It is therefore recommended to pass the
|
||||||
|
configuration file to Verilator as first file.
|
||||||
|
|
||||||
The grammar of configuration commands is as follows:
|
The grammar of configuration commands is as follows:
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
@@ -2888,7 +2927,8 @@ signal should be declared public_flat (see above), but read-only.
|
|||||||
Used after an input, output, register, or wire declaration to indicate the
|
Used after an input, output, register, or wire declaration to indicate the
|
||||||
signal should be declared public_flat_rd (see above), and also writable,
|
signal should be declared public_flat_rd (see above), and also writable,
|
||||||
where writes should be considered to have the timing specified by the given
|
where writes should be considered to have the timing specified by the given
|
||||||
sensitivity edge list.
|
sensitivity edge list. Set for all variables, ports and wires using the
|
||||||
|
--public-flat-rw switch.
|
||||||
|
|
||||||
=item /*verilator public_module*/
|
=item /*verilator public_module*/
|
||||||
|
|
||||||
@@ -2909,7 +2949,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>>
|
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
|
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
|
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
|
you should avoid using this attribute when not necessary as with increasing
|
||||||
usage of sc_bv the performance increases significantly.
|
usage of sc_bv the performance increases significantly.
|
||||||
@@ -3300,7 +3340,7 @@ $value$plusargs.
|
|||||||
|
|
||||||
Not supported as Verilator needs to determine all formatting at compile
|
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
|
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.
|
when displaying all times, model wide.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
@@ -3711,14 +3751,31 @@ correctly.
|
|||||||
|
|
||||||
=item MULTITOP
|
=item MULTITOP
|
||||||
|
|
||||||
Error that there are multiple top level modules, that is modules not
|
Warns that there are multiple top level modules, that is modules not
|
||||||
instantiated by any other module. Verilator only supports a single top
|
instantiated by any other module, and both modules were put on the command
|
||||||
level, if you need more, create a module that wraps all of the top modules.
|
line (not in a library). Three likely cases:
|
||||||
|
|
||||||
Often this error is because some low level cell is being read in, but is
|
1. A single module is intended to be the top. This warning then occurs
|
||||||
not really needed. The best solution is to insure that each module is in a
|
because some low level cell is being read in, but is not really needed as
|
||||||
unique file by the same name. Otherwise, make sure all library files are
|
part of the design. The best solution for this situation is to ensure that
|
||||||
read in as libraries with -v, instead of automatically with -y.
|
only the top module is put on the command line without any flags, and all
|
||||||
|
remaining library files are read in as libraries with -v, or are
|
||||||
|
automatically resolved by having filenames that match the module names.
|
||||||
|
|
||||||
|
2. A single module is intended to be the top, the name of it is known, and
|
||||||
|
all other modules should be ignored if not part of the design. The best
|
||||||
|
solution is to use the --top-module option to specify the top module's
|
||||||
|
name. All other modules that are not part of the design will be for the
|
||||||
|
most part ignored (they must be clean in syntax and their contents will be
|
||||||
|
removed as part of the Verilog module elaboration process.)
|
||||||
|
|
||||||
|
3. Multiple modules are intended to be design tops, e.g. when linting a
|
||||||
|
library file. As multiple modules are desired, disable the MULTITOP
|
||||||
|
warning. All input/outputs will go uniquely to each module, with any
|
||||||
|
conflicting and identical signal names being uniquified by adding a prefix
|
||||||
|
based on the top module name followed by __02E (a Verilator-encoded ASCII
|
||||||
|
".'). This renaming is done even if the two modules' signals seem
|
||||||
|
identical, e.g. multiple modules with a "clk" input.
|
||||||
|
|
||||||
=item PINCONNECTEMPTY
|
=item PINCONNECTEMPTY
|
||||||
|
|
||||||
@@ -4107,17 +4164,32 @@ code to add appropriate prints to see what is going on.
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item Does it run under Windows?
|
=item Can I contribute?
|
||||||
|
|
||||||
|
Please contribute! Just file an issue asking for a merge, or ask on the
|
||||||
|
forums if looking for something to help on. For more information see our
|
||||||
|
contributor agreement.
|
||||||
|
|
||||||
|
=item How commonly is Verilator used?
|
||||||
|
|
||||||
|
Verilator is used by many of the largest silicon design companies, and all
|
||||||
|
the way down to college projects. Verilator is a "big 4" simulator,
|
||||||
|
meaning one of the 4 main SystemVerilog simulators available, namely the
|
||||||
|
commercial products Synopsys' VCS (tm), Mentor's ModelSim (tm), Cadence
|
||||||
|
Incisive/NC-Verilog/NC-Sim, and the open-source Verilator. The three
|
||||||
|
commercial choices are commonly collectively called the "big 3" simulators.
|
||||||
|
|
||||||
|
=item Does Verilator run under Windows?
|
||||||
|
|
||||||
Yes, using Cygwin. Verilated output also compiles under Microsoft Visual
|
Yes, using Cygwin. Verilated output also compiles under Microsoft Visual
|
||||||
C++ Version 7 or newer, but this is not tested every release.
|
C++, but this is not tested every release.
|
||||||
|
|
||||||
=item Can you provide binaries?
|
=item Can you provide binaries?
|
||||||
|
|
||||||
Verilator is available as a RPM for Debian/Ubuntu, SuSE, Fedora, and
|
Verilator is available as a RPM for Debian/Ubuntu, SuSE, Fedora, and other
|
||||||
perhaps other systems; this is done by porters and may slightly lag the
|
distributions; this is done by porters and may slightly lag the primary
|
||||||
primary distribution. If there isn't a binary build for your distribution,
|
distribution. If there isn't a binary build for your distribution, how
|
||||||
how about you set one up? Please contact the authors for assistance.
|
about you set one up? Please contact the authors for assistance.
|
||||||
|
|
||||||
Note people sometimes request binaries when they are having problems with
|
Note people sometimes request binaries when they are having problems with
|
||||||
their C++ compiler. Alas, binaries won't help this, as in the end a fully
|
their C++ compiler. Alas, binaries won't help this, as in the end a fully
|
||||||
@@ -4161,7 +4233,7 @@ placed under the Verilator copyright and LGPL/Artistic license. Small test
|
|||||||
cases will be released into the public domain so they can be used anywhere,
|
cases will be released into the public domain so they can be used anywhere,
|
||||||
and large tests under the LGPL/Artistic, unless requested otherwise.
|
and large tests under the LGPL/Artistic, unless requested otherwise.
|
||||||
|
|
||||||
=item Why is Verilation so slow?
|
=item Why is running Verilator (to create a model) so slow?
|
||||||
|
|
||||||
Verilator needs more memory than the resulting simulator will require, as
|
Verilator needs more memory than the resulting simulator will require, as
|
||||||
Verilator creates internally all of the state of the resulting generated
|
Verilator creates internally all of the state of the resulting generated
|
||||||
|
|||||||
@@ -17,13 +17,6 @@ eval 'exec perl -wS $0 ${1+"$@"}'
|
|||||||
|
|
||||||
require 5.006_001;
|
require 5.006_001;
|
||||||
use warnings;
|
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};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
use FindBin qw($RealBin $RealScript);
|
use FindBin qw($RealBin $RealScript);
|
||||||
use IO::File;
|
use IO::File;
|
||||||
@@ -73,7 +66,7 @@ run(verilator_coverage_bin()
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ sub filter {
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
exit(1);
|
exit(1); # Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
|
|||||||
+2
-2
@@ -43,8 +43,8 @@ exit(0);
|
|||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
exit(1);
|
exit(1); # Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
sub parameter {
|
sub parameter {
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ profcfunc($Opt_File);
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
exit(1);
|
exit(1); # Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
@@ -212,7 +212,7 @@ verilator_profcfunc - Read gprof report created with --prof-cfuncs
|
|||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
Verilator_profcfunc reads a profile report created by gprof. The names of
|
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,
|
--prof-cfuncs, and a report printed showing the percentage of time, etc,
|
||||||
in each Verilog block.
|
in each Verilog block.
|
||||||
|
|
||||||
|
|||||||
+9
-7
@@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||||
#AC_INIT([Verilator],[#.### devel])
|
#AC_INIT([Verilator],[#.### devel])
|
||||||
AC_INIT([Verilator],[4.014 2019-05-08],
|
AC_INIT([Verilator],[4.020 2019-10-06],
|
||||||
[https://www.veripool.org/verilator],
|
[https://www.veripool.org/verilator],
|
||||||
[verilator],[https://www.veripool.org/verilator])
|
[verilator],[https://www.veripool.org/verilator])
|
||||||
# When releasing, also update header of Changes file
|
# When releasing, also update header of Changes file
|
||||||
# and commit using "devel release" or "Version bump" message
|
# and commit using "devel release" or "Version bump" message
|
||||||
|
|
||||||
AC_CONFIG_HEADER(src/config_build.h)
|
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])
|
AC_MSG_RESULT([configuring for $PACKAGE_STRING])
|
||||||
|
|
||||||
@@ -91,10 +91,10 @@ AC_SUBST(CFG_WITH_PREC11)
|
|||||||
AC_MSG_RESULT($CFG_WITH_PREC11)
|
AC_MSG_RESULT($CFG_WITH_PREC11)
|
||||||
|
|
||||||
# Compiler flags
|
# Compiler flags
|
||||||
CFLAGS=-I${includedir}
|
CFLAGS+=" -I${includedir}"
|
||||||
CPPFLAGS=-I${includedir}
|
CPPFLAGS+=" -I${includedir}"
|
||||||
CXXFLAGS=-I${includedir}
|
CXXFLAGS+=" -I${includedir}"
|
||||||
LDFLAGS=-L${libdir}
|
LDFLAGS+=" -L${libdir}"
|
||||||
|
|
||||||
# Checks for programs.
|
# Checks for programs.
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
@@ -273,7 +273,9 @@ AC_SUBST(CFG_CXXFLAGS_PARSER)
|
|||||||
# without this flag, even though there's a conditional to prevent the divide.
|
# without this flag, even though there's a conditional to prevent the divide.
|
||||||
# We still don't add no-div-by-zero as it throws message to stdout, though doesn't die.
|
# We still don't add no-div-by-zero as it throws message to stdout, though doesn't die.
|
||||||
#_MY_CXX_CHECK_OPT(-Wno-div-by-zero)
|
#_MY_CXX_CHECK_OPT(-Wno-div-by-zero)
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-faligned-new)
|
# For some reason -faligned-new does not work under Travis w/ clang but the
|
||||||
|
# configure test doesn't catch this either
|
||||||
|
AS_IF([test "x$TRAVIS_COMPILER" != xclang], [_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-faligned-new)])
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-fbracket-depth=4096)
|
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-fbracket-depth=4096)
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Qunused-arguments)
|
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Qunused-arguments)
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-bool-operation)
|
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-bool-operation)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Makefile
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
= How to contribute to Verilator
|
||||||
|
|
||||||
|
Thanks for using Verilator! We welcome your contributions in whatever form.
|
||||||
|
|
||||||
|
This contributing document contains some suggestions that may make
|
||||||
|
contributions flow more efficiently.
|
||||||
|
|
||||||
|
==== Did you find a bug?
|
||||||
|
|
||||||
|
* Please **Ensure the bug was not already reported** by searching
|
||||||
|
https://www.veripool.org/projects/verilator/issues[Verilator Issues].
|
||||||
|
|
||||||
|
* If you're unable to find an open issue addressing the problem,
|
||||||
|
https://www.veripool.org/projects/verilator/issues/new[open a new issue].
|
||||||
|
|
||||||
|
** Be sure to include a **code sample** or an **executable test case**
|
||||||
|
demonstrating the bug and expected behavior that is not occurring.
|
||||||
|
|
||||||
|
** The ideal example works against other simulators, and is in the
|
||||||
|
test_regress/t test format, as described in
|
||||||
|
link:internals.adoc[docs/internals].
|
||||||
|
|
||||||
|
==== Did you write a patch that fixes a bug?
|
||||||
|
|
||||||
|
* Please https://www.veripool.org/projects/verilator/issues/new[Open a new
|
||||||
|
issue].
|
||||||
|
|
||||||
|
* You may attach a patch to the issue, or (preferred) may point to a github
|
||||||
|
repository branch within your github account.
|
||||||
|
|
||||||
|
** Verilator uses Travis CI to provide continuous integration. You may
|
||||||
|
want to setup Travis CI on your github branch to ensure your changes
|
||||||
|
keep the tests passing. See link:internals.adoc[docs/internals].
|
||||||
|
|
||||||
|
* Your source-code contributions must be certified as open source, under
|
||||||
|
the https://developercertificate.org/[Developer Certificate of
|
||||||
|
Origin]. On your first contribution, you must either:
|
||||||
|
|
||||||
|
** Have your patch include the addition of your name to
|
||||||
|
link:CONTRIBUTORS[docs/CONTRIBUTORS] (preferred).
|
||||||
|
|
||||||
|
** Use "git -s" as part of your commit. This adds a "signed-of-by"
|
||||||
|
attribute which will certify your contribution as described in the
|
||||||
|
https://github.com/wking/signed-off-by/blob/master/Documentation/SubmittingPatches[Signed-of-By
|
||||||
|
convention].
|
||||||
|
|
||||||
|
** Email, or post in an issue a statement that you certify your
|
||||||
|
contributions.
|
||||||
|
|
||||||
|
** In any of these cases your name will be added to
|
||||||
|
link:CONTRIBUTORS[docs/CONTRIBUTRORS] and you are agreeing all future
|
||||||
|
contributions are also certified.
|
||||||
|
|
||||||
|
** We occasionally accept contributions where people do not want their
|
||||||
|
name published. Please email us; you must still certify your
|
||||||
|
contribution using email.
|
||||||
|
|
||||||
|
* Your test contributions are generally considered released into the public
|
||||||
|
domain, unless you request otherwise or put a GNU/Artistic license on
|
||||||
|
your test file.
|
||||||
|
|
||||||
|
* Most important is we get your patch. If you'd like to clean up
|
||||||
|
indentation and related issues ahead of our feedback, that is
|
||||||
|
appreciated; please see the coding conventions in
|
||||||
|
link:internals.adoc[docs/internals].
|
||||||
|
|
||||||
|
==== Do you have questions?
|
||||||
|
|
||||||
|
* Please see the
|
||||||
|
https://www.veripool.org/projects/verilator/wiki/Faq[Verilator FAQ].
|
||||||
|
|
||||||
|
* Ask any question in the
|
||||||
|
https://www.veripool.org/projects/verilator/boards[Verilator forums].
|
||||||
|
|
||||||
|
==== Code of Conduct
|
||||||
|
|
||||||
|
* Our contributors and participants pledge to make participation in our
|
||||||
|
project and our community a positive experience for everyone. We follow
|
||||||
|
the https://www.contributor-covenant.org/version/1/4/code-of-conduct.html[Contributor
|
||||||
|
Covenant version 1.4].
|
||||||
|
|
||||||
|
Thanks!
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
Alex Chadwick
|
||||||
|
Gianfranco Costamagna
|
||||||
|
Howard Su
|
||||||
|
Jeremy Bennett
|
||||||
|
John Coiner
|
||||||
|
Kanad Kanhere
|
||||||
|
Lukasz Dalek
|
||||||
|
Maarten De Braekeleer
|
||||||
|
Philipp Wagner
|
||||||
|
Richard Myers
|
||||||
|
Sebastien Van Cauwenberghe
|
||||||
|
Stefan Wallentowitz
|
||||||
|
Todd Strader
|
||||||
|
Wilson Snyder
|
||||||
|
Yves Mathieu
|
||||||
@@ -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.
|
# exceed 55 pixels and the maximum width should not exceed 200 pixels.
|
||||||
# Doxygen will copy the logo to the output directory.
|
# 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)
|
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||||
# base path where the generated documentation will be put.
|
# base path where the generated documentation will be put.
|
||||||
@@ -0,0 +1,956 @@
|
|||||||
|
= Verilator Internals Documentation
|
||||||
|
:toc: right
|
||||||
|
|
||||||
|
// Github doesn't render unless absolute URL
|
||||||
|
image::https://www.veripool.org/img/verilator_256_200_min.png[Logo,256,200,role="right"]
|
||||||
|
|
||||||
|
== Introduction
|
||||||
|
|
||||||
|
This file discusses internal and programming details for Verilator. It's a
|
||||||
|
reference for developers and debugging problems.
|
||||||
|
|
||||||
|
See also the Verilator internals presentation at http://www.veripool.org.
|
||||||
|
|
||||||
|
== Code Flows
|
||||||
|
|
||||||
|
=== Verilator Flow
|
||||||
|
|
||||||
|
The main flow of Verilator can be followed by reading the Verilator.cpp
|
||||||
|
`process()` function:
|
||||||
|
|
||||||
|
. First, the files specified on the command line are read. Reading
|
||||||
|
involves preprocessing, then lexical analysis with Flex and parsing with
|
||||||
|
Bison. This produces an abstract syntax tree (AST) representation of the
|
||||||
|
design, which is what is visible in the .tree files described below.
|
||||||
|
|
||||||
|
. Verilator then makes a series of passes over the AST, progressively
|
||||||
|
refining and optimizing it.
|
||||||
|
|
||||||
|
. Cells in the AST first linked, which will read and parse additional files
|
||||||
|
as above.
|
||||||
|
|
||||||
|
. Functions, variable and other references are linked to their definitions.
|
||||||
|
|
||||||
|
. Parameters are resolved and the design is elaborated.
|
||||||
|
|
||||||
|
. Verilator then performs many additional edits and optimizations on the
|
||||||
|
hierarchical design. This includes coverage, assertions, X elimination,
|
||||||
|
inlining, constant propagation, and dead code elimination.
|
||||||
|
|
||||||
|
. References in the design are then pseudo-flattened. Each module's
|
||||||
|
variables and functions get "Scope" references. A scope reference is an
|
||||||
|
occurrence of that un-flattened variable in the flattened hierarchy. A
|
||||||
|
module that occurs only once in the hierarchy will have a single scope and
|
||||||
|
single VarScope for each variable. A module that occurs twice will have a
|
||||||
|
scope for each occurrence, and two VarScopes for each variable. This
|
||||||
|
allows optimizations to proceed across the flattened design, while still
|
||||||
|
preserving the hierarchy.
|
||||||
|
|
||||||
|
. Additional edits and optimizations proceed on the pseudo-flat design.
|
||||||
|
These include module references, function inlining, loop unrolling,
|
||||||
|
variable lifetime analysis, lookup table creation, always splitting, and
|
||||||
|
logic gate simplifications (pushing inverters, etc).
|
||||||
|
|
||||||
|
. Verilator orders the code. Best case, this results in a single "eval"
|
||||||
|
function which has all always statements flowing from top to bottom with no
|
||||||
|
loops.
|
||||||
|
|
||||||
|
. Verilator mostly removes the flattening, so that code may be shared
|
||||||
|
between multiple invocations of the same module. It localizes variables,
|
||||||
|
combines identical functions, expands macros to C primitives, adds branch
|
||||||
|
prediction hints, and performs additional constant propagation.
|
||||||
|
|
||||||
|
. Verilator finally writes the C++ modules.
|
||||||
|
|
||||||
|
=== Key Classes Used in the Verilator Flow
|
||||||
|
|
||||||
|
==== `AstNode`
|
||||||
|
|
||||||
|
The AST is represented at the top level by the class `AstNode`. This
|
||||||
|
abstract class has derived classes for the individual components
|
||||||
|
(e.g. `AstGenerate` for a generate block) or groups of components
|
||||||
|
(e.g. `AstNodeFTask` for functions and tasks, which in turn has `AstFunc`
|
||||||
|
and `AstTask` as derived classes).
|
||||||
|
|
||||||
|
Each `AstNode` has pointers to up to four children, accessed by the `op1p`
|
||||||
|
through `op4p` methods. These methods are then abstracted in a specific
|
||||||
|
Ast* node class to a more specific name. For example with the `AstIf` node
|
||||||
|
(for `if` statements), `ifsp` calls `op2p` to give the pointer to the AST
|
||||||
|
for the "then" block, while `elsesp` calls `op3p` to give the pointer to
|
||||||
|
the AST for the "else" block, or NULL if there is not one.
|
||||||
|
|
||||||
|
`AstNode` has the concept of a next and previous AST - for example the next
|
||||||
|
and previous statements in a block. Pointers to the AST for these
|
||||||
|
statements (if they exist) can be obtained using the `back` and `next`
|
||||||
|
methods.
|
||||||
|
|
||||||
|
It is useful to remember that the derived class `AstNetlist` is at the top
|
||||||
|
of the tree, so checking for this class is the standard way to see if you
|
||||||
|
are at the top of the tree.
|
||||||
|
|
||||||
|
By convention, each function/method uses the variable `nodep` as a pointer
|
||||||
|
to the `AstNode` currently being processed.
|
||||||
|
|
||||||
|
==== `AstNVisitor`
|
||||||
|
|
||||||
|
The passes are implemented by AST visitor classes. These are implemented
|
||||||
|
by subclasses of the abstract class, `AstNVisitor`. Each pass creates an
|
||||||
|
instance of the visitor class, which in turn implements a method to perform
|
||||||
|
the pass.
|
||||||
|
|
||||||
|
==== `V3Graph`
|
||||||
|
|
||||||
|
A number of passes use graph algorithms, and the class `V3Graph` is
|
||||||
|
provided to represent those graphs. Graphs are directed, and algorithms are
|
||||||
|
provided to manipulate the graphs and to output them in
|
||||||
|
http://www.graphviz.org/[GraphViz] dot format. `V3Graph.h` provides
|
||||||
|
documentation of this class.
|
||||||
|
|
||||||
|
==== `V3GraphVertex`
|
||||||
|
|
||||||
|
`V3GraphVertex` is the base class for vertices in a graph. Vertices have
|
||||||
|
an associated `fanout`, `color` and `rank`, which may be used in algorithms
|
||||||
|
for ordering the graph. A generic `user`/`userp` member variable is also
|
||||||
|
provided.
|
||||||
|
|
||||||
|
Virtual methods are provided to specify the name, color, shape and style to
|
||||||
|
be used in dot output. Typically users provide derived classes from
|
||||||
|
`V3GraphVertex` which will reimplement these methods.
|
||||||
|
|
||||||
|
Iterators are provided to access in and out edges. Typically these are used
|
||||||
|
in the form:
|
||||||
|
|
||||||
|
for (V3GraphEdge *edgep = vertexp->inBeginp();
|
||||||
|
edgep;
|
||||||
|
edgep = edgep->inNextp()) {
|
||||||
|
|
||||||
|
==== `V3GraphEdge`
|
||||||
|
|
||||||
|
`V3GraphEdge` is the base class for directed edges between pairs of
|
||||||
|
vertices. Edges have an associated `weight` and may also be made
|
||||||
|
`cutable`. A generic `user`/`userp` member variable is also provided.
|
||||||
|
|
||||||
|
Accessors, `fromp` and `top` return the "from" and "to" vertices
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
Virtual methods are provided to specify the label, color and style to be
|
||||||
|
used in dot output. Typically users provided derived classes from
|
||||||
|
`V3GraphEdge` which will reimplement these methods.
|
||||||
|
|
||||||
|
==== `V3GraphAlg`
|
||||||
|
|
||||||
|
This is the base class for graph algorithms. It implements a `bool` method,
|
||||||
|
`followEdge` which algorithms can use to decide whether an edge is
|
||||||
|
followed. This method returns true if the graph edge has weight greater
|
||||||
|
than one and a user function, `edgeFuncp` (supplied in the constructor)
|
||||||
|
returns `true`.
|
||||||
|
|
||||||
|
A number of predefined derived algorithm classes and access methods are
|
||||||
|
provided and documented in `V3GraphAlg.cpp`.
|
||||||
|
|
||||||
|
=== Multithreaded Mode
|
||||||
|
|
||||||
|
In `--threads` mode, the frontend of the Verilator pipeline is the same as
|
||||||
|
serial mode, up until V3Order.
|
||||||
|
|
||||||
|
`V3Order` builds a fine-grained, statement-level dependency graph that
|
||||||
|
governs the ordering of code within a single `eval()` call. In serial mode,
|
||||||
|
that dependency graph is used to order all statements into a total serial
|
||||||
|
order. In parallel mode, the same dependency graph is the starting point
|
||||||
|
for a partitioner (`V3Partition`).
|
||||||
|
|
||||||
|
The partitioner's goal is to coarsen the fine-grained graph into a coarser
|
||||||
|
graph, while maintaining as much available parallelism as possible. Often
|
||||||
|
the partitioner can transform an input graph with millions of nodes into a
|
||||||
|
coarsened execution graph with a few dozen nodes, while maintaining enough
|
||||||
|
parallelism to take advantage of a modern multicore CPU. Runtime
|
||||||
|
synchronization cost is not prohibitive with so few nodes.
|
||||||
|
|
||||||
|
==== Partitioning
|
||||||
|
|
||||||
|
Our partitioner is similar to the one Vivek Sarkar described in his 1989
|
||||||
|
paper _Partitioning and Scheduling Parallel Programs for Multiprocessors_.
|
||||||
|
|
||||||
|
Let's define some terms:
|
||||||
|
|
||||||
|
==== Par Factor
|
||||||
|
|
||||||
|
The available parallelism or "par-factor" of a DAG is the total cost to
|
||||||
|
execute all nodes, divided by the cost to execute the longest critical path
|
||||||
|
through the graph. This is the speedup you would get from running the graph
|
||||||
|
in parallel, if given infinite CPU cores available and communication and
|
||||||
|
synchronization are zero.
|
||||||
|
|
||||||
|
==== Macro Task
|
||||||
|
|
||||||
|
When the partitioner coarsens the graph, it combines nodes together. Each
|
||||||
|
fine-grained node represents an atomic "task"; combined nodes in the
|
||||||
|
coarsened graph are "macro-tasks". This term comes from Sarkar. Each
|
||||||
|
macro-task executes from start to end on one processor, without any
|
||||||
|
synchronization to any other macro-task during its
|
||||||
|
execution. (Synchronization only happens before the macro-task begins or
|
||||||
|
after it ends.)
|
||||||
|
|
||||||
|
==== Edge Contraction
|
||||||
|
|
||||||
|
Verilator's partitioner, like Sarkar's, primarily relies on "edge
|
||||||
|
contraction" to coarsen the graph. It starts with one macro-task per atomic
|
||||||
|
task and iteratively combines pairs of edge-connected macro-tasks.
|
||||||
|
|
||||||
|
==== Local Critical Path
|
||||||
|
|
||||||
|
Each node in the graph has a "local" critical path. That's the critical
|
||||||
|
path from the start of the graph to the start of the node, plus the node's
|
||||||
|
cost, plus the critical path from the end of the node to the end of the
|
||||||
|
graph.
|
||||||
|
|
||||||
|
Sarkar calls out an important trade-off: coarsening the graph reduces
|
||||||
|
runtime synchronization overhead among the macro-tasks, but it tends to
|
||||||
|
increase the critical path through the graph and thus reduces par-factor.
|
||||||
|
|
||||||
|
Sarkar's partitioner, and ours, chooses pairs of macro-tasks to merge such
|
||||||
|
that the growth in critical path is minimized. Each candidate merge would
|
||||||
|
result in a new node, which would have some local critical path. We choose
|
||||||
|
the candidate that would produce the shortest local critical path. Repeat
|
||||||
|
until par-factor falls to a target threshold. It's a greedy algorithm, and
|
||||||
|
it's not guaranteed to produce the best partition (which Sarkar proves is
|
||||||
|
NP-hard).
|
||||||
|
|
||||||
|
==== Estimating Logic Costs
|
||||||
|
|
||||||
|
To compute the cost of any given path through the graph, Verilator
|
||||||
|
estimates an execution cost for each task. Each macro-task has an execution
|
||||||
|
cost which is simply the sum of its tasks' costs. We assume that
|
||||||
|
communication overhead and synchronization overhead are zero, so the cost
|
||||||
|
of any given path through the graph is simply the sum of macro-task
|
||||||
|
execution costs. Sarkar does almost the same thing, except that he has
|
||||||
|
nonzero estimates for synchronization costs.
|
||||||
|
|
||||||
|
Verilator's cost estimates are assigned by `InstrCountCostVisitor`. This
|
||||||
|
class is perhaps the most fragile piece of the multithread implementation.
|
||||||
|
It's easy to have a bug where you count something cheap (eg. accessing one
|
||||||
|
element of a huge array) as if it were expensive (eg. by counting it as if
|
||||||
|
it were an access to the entire array.) Even without such gross bugs, the
|
||||||
|
estimates this produce are only loosely predictive of actual runtime cost.
|
||||||
|
Multithread performance would be better with better runtime costs
|
||||||
|
estimates. This is an area to improve.
|
||||||
|
|
||||||
|
==== Scheduling Macro-Tasks at Runtime
|
||||||
|
|
||||||
|
After coarsening the graph, we must schedule the macro-tasks for runtime.
|
||||||
|
Sarkar describes two options: you can dynamically schedule tasks at
|
||||||
|
runtime, with a runtime graph follower. Sarkar calls this the
|
||||||
|
"macro-dataflow model." Verilator does not support this; early experiments
|
||||||
|
with this approach had poor performance.
|
||||||
|
|
||||||
|
The other option is to statically assign macro-tasks to threads, with each
|
||||||
|
thread running its macro-tasks in a static order. Sarkar describes this in
|
||||||
|
Chapter 5. Verilator takes this static approach. The only dynamic aspect is
|
||||||
|
that each macro task may block before starting, to wait until its
|
||||||
|
prerequisites on other threads have finished.
|
||||||
|
|
||||||
|
The synchronization cost is cheap if the prereqs are done. If they're not,
|
||||||
|
fragmentation (idle CPU cores waiting) is possible. This is the major
|
||||||
|
source of overhead in this approach. The `--prof-threads` switch and the
|
||||||
|
`verilator_gantt` script can visualize the time lost to such fragmentation.
|
||||||
|
|
||||||
|
==== Locating Variables for Best Spatial Locality
|
||||||
|
|
||||||
|
After scheduling all code, we attempt to locate variables in memory such
|
||||||
|
that variables accessed by a single macro-task are close together in
|
||||||
|
memory. This provides "spatial locality" -- when we pull in a 64-byte
|
||||||
|
cache line to access a 2-byte variable, we want the other 62 bytes to be
|
||||||
|
ones we'll also likely access soon, for best cache performance.
|
||||||
|
|
||||||
|
This turns out to be critical for performance. It should allow Verilator to
|
||||||
|
scale to very large models. We don't rely on our working set fitting in any
|
||||||
|
CPU cache; instead we essentially "stream" data into caches from
|
||||||
|
memory. It's not literally streaming, where the address increases
|
||||||
|
monotonically, but it should have similar performance characteristics, so
|
||||||
|
long as each macro-task's dataset fits in one core's local caches.
|
||||||
|
|
||||||
|
To achieve spatial locality, we tag each variable with the set of
|
||||||
|
macro-tasks that access it. Let's call this set the "footprint" of that
|
||||||
|
variable. The variables in a given module have a set of footprints. We can
|
||||||
|
order those footprints to minimize the distance between them (distance is
|
||||||
|
the number of macro-tasks that are different across any two footprints) and
|
||||||
|
then emit all variables into the struct in ordered-footprint order.
|
||||||
|
|
||||||
|
The footprint ordering is literally the traveling salesman problem, and we
|
||||||
|
use a TSP-approximation algorithm to get close to an optimal sort.
|
||||||
|
|
||||||
|
This is an old idea. Simulators designed at DEC in the early 1990s used
|
||||||
|
similar techniques to optimize both single-thread and multi-thread modes.
|
||||||
|
(Verilator does not optimize variable placement for spatial locality in
|
||||||
|
serial mode; that is a possible area for improvement.)
|
||||||
|
|
||||||
|
==== Improving Multithreaded Performance Further (a TODO list)
|
||||||
|
|
||||||
|
===== Wave Scheduling
|
||||||
|
|
||||||
|
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 idle while the testbench (everything outside `eval()`) is
|
||||||
|
working. This would involve fundamental changes within the partitioner,
|
||||||
|
however, it's probably the best bet for hiding testbench latency.
|
||||||
|
|
||||||
|
===== Efficient Dynamic Scheduling
|
||||||
|
|
||||||
|
To scale to more than a few threads, we may revisit a fully dynamic
|
||||||
|
scheduler. For large (>16 core) systems it might make sense to dedicate an
|
||||||
|
entire core to scheduling, so that scheduler data structures would fit in
|
||||||
|
its L1 cache and thus the cost of traversing priority-ordered ready lists
|
||||||
|
would not be prohibitive.
|
||||||
|
|
||||||
|
===== Static Scheduling with Runtime Repack
|
||||||
|
|
||||||
|
We could modify the static scheduling approach by gathering actual
|
||||||
|
macro-task execution times at run time, and dynamically re-packing the
|
||||||
|
macro-tasks into the threads also at run time. Say, re-pack once every
|
||||||
|
10,000 cycles or something. This has the potential to do better than our
|
||||||
|
static estimates about macro-task run times. It could potentially react to
|
||||||
|
CPU cores that aren't performing equally, due to NUMA or thermal throttling
|
||||||
|
or nonuniform competing memory traffic or whatever.
|
||||||
|
|
||||||
|
===== Clock Domain Balancing
|
||||||
|
|
||||||
|
Right now Verilator makes no attempt to balance clock domains across
|
||||||
|
macro-tasks. For a multi-domain model, that could lead to bad gantt chart
|
||||||
|
fragmentation. This could be improved if it's a real problem in practice.
|
||||||
|
|
||||||
|
===== Other Forms of MTask Balancing
|
||||||
|
|
||||||
|
The largest source of runtime overhead is idle CPUs, which happens due to
|
||||||
|
variance between our predicted runtime for each MTask and its actual
|
||||||
|
runtime. That variance is magnified if MTasks are homogeneous, containing
|
||||||
|
similar repeating logic which was generally close together in source code
|
||||||
|
and which is still packed together even after going through Verilator's
|
||||||
|
digestive tract.
|
||||||
|
|
||||||
|
If Verilator could avoid doing that, and instead would take source logic
|
||||||
|
that was close together and distribute it across MTasks, that would
|
||||||
|
increase the diversity of any given MTask, and this should reduce variance
|
||||||
|
in the cost estimates.
|
||||||
|
|
||||||
|
One way to do that might be to make various "tie breaker" comparison
|
||||||
|
routines in the sources to rely more heavily on randomness, and generally
|
||||||
|
try harder not to keep input nodes together when we have the option to
|
||||||
|
scramble things.
|
||||||
|
|
||||||
|
===== Performance Regression
|
||||||
|
|
||||||
|
It would be nice if we had a regression of large designs, with some
|
||||||
|
diversity of design styles, to test on both single- and multi-threaded
|
||||||
|
modes. This would help to avoid performance regressions, and also to
|
||||||
|
evaluate the optimizations while minimizing the impact of parasitic noise.
|
||||||
|
|
||||||
|
===== Per-Instance Classes
|
||||||
|
|
||||||
|
If we have multiple instances of the same module, and they partition
|
||||||
|
differently (likely; we make no attempt to partition them the same) then
|
||||||
|
the variable sort will be suboptimal for either instance. A possible
|
||||||
|
improvement would be to emit a unique class for each instance of a module,
|
||||||
|
and sort its variables optimally for that instance's code stream.
|
||||||
|
|
||||||
|
=== Verilated Flow
|
||||||
|
|
||||||
|
The evaluation loop outputted by Verilator is designed to allow a single
|
||||||
|
function to perform evaluation under most situations.
|
||||||
|
|
||||||
|
On the first evaluation, the Verilated code calls initial blocks, and then
|
||||||
|
"settles" the modules, by evaluating functions (from always statements)
|
||||||
|
until all signals are stable.
|
||||||
|
|
||||||
|
On other evaluations, the Verilated code detects what input signals have
|
||||||
|
changes. If any are clocks, it calls the appropriate sequential functions
|
||||||
|
(from `always @ posedge` statements). Interspersed with sequential functions
|
||||||
|
it calls combo functions (from `always @*`). After this is complete, it
|
||||||
|
detects any changes due to combo loops or internally generated clocks, and
|
||||||
|
if one is found must reevaluate the model again.
|
||||||
|
|
||||||
|
For SystemC code, the `eval()` function is wrapped in a SystemC
|
||||||
|
`SC_METHOD`, sensitive to all inputs. (Ideally it would only be sensitive
|
||||||
|
to clocks and combo inputs, but tracing requires all signals to cause
|
||||||
|
evaluation, and the performance difference is small.)
|
||||||
|
|
||||||
|
If tracing is enabled, a callback examines all variables in the design for
|
||||||
|
changes, and writes the trace for each change. To accelerate this process
|
||||||
|
the evaluation process records a bitmask of variables that might have
|
||||||
|
changed; if clear, checking those signals for changes may be skipped.
|
||||||
|
|
||||||
|
== Coding Conventions
|
||||||
|
|
||||||
|
=== Indentation style
|
||||||
|
|
||||||
|
We will work with contributors to fix up indentation style issues, but it
|
||||||
|
is appreciated if you could match our style:
|
||||||
|
|
||||||
|
* All files should contain the magic header to insure standard indentation:
|
||||||
|
+
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
+
|
||||||
|
This sets indentation to the `cc-mode` defaults. (Verilator predates a
|
||||||
|
CC-mode change of several years ago which overrides the defaults with GNU
|
||||||
|
style indentation; the `c-set-style` undoes that.)
|
||||||
|
|
||||||
|
* Use 4 spaces per level, and no tabs.
|
||||||
|
|
||||||
|
* Use 2 spaces between the end of source and the beginning of a comment.
|
||||||
|
|
||||||
|
* Use "mixedCapsSymbols" instead of "underlined_symbls".
|
||||||
|
|
||||||
|
* Comment every member variable.
|
||||||
|
|
||||||
|
=== The `astgen` script
|
||||||
|
|
||||||
|
Some of the code implementing passes is extremely repetitive, and must be
|
||||||
|
implemented for each sub-class of `AstNode`. However, while repetitive,
|
||||||
|
there is more variability than can be handled in C++ macros.
|
||||||
|
|
||||||
|
In Verilator this is implemented by using a Perl script, `astgen` to
|
||||||
|
pre-process the C++ code. For example in `V3Const.cpp` this is used to
|
||||||
|
implement the `visit()` functions for each binary operation using the
|
||||||
|
`TREEOP` macro.
|
||||||
|
|
||||||
|
The original C++ source code is transformed into C++ code in the `obj_opt`
|
||||||
|
and `obj_dbg` sub-directories (the former for the optimized version of
|
||||||
|
Verilator, the latter for the debug version). So for example `V3Const.cpp`
|
||||||
|
into `V3Const__gen.cpp`.
|
||||||
|
|
||||||
|
=== Visitor Functions
|
||||||
|
|
||||||
|
Verilator uses the "Visitor" design pattern to implement its refinement and
|
||||||
|
optimization passes. This allows separation of the pass algorithm from the
|
||||||
|
AST on which it operates. Wikipedia provides an introduction to the concept
|
||||||
|
at http://en.wikipedia.org/wiki/Visitor_pattern.
|
||||||
|
|
||||||
|
As noted above, all visitors are derived classes of `AstNVisitor`. All
|
||||||
|
derived classes of `AstNode` implement the `accept` method, which takes as
|
||||||
|
argument a reference to an instance or a `AstNVisitor` derived class and
|
||||||
|
applies the visit method of the `AstNVisitor` to the invoking AstNode
|
||||||
|
instance (i.e. `this`).
|
||||||
|
|
||||||
|
One possible difficulty is that a call to `accept` may perform an edit
|
||||||
|
which destroys the node it receives as argument. The
|
||||||
|
`acceptSubtreeReturnEdits` method of `AstNode` is provided to apply
|
||||||
|
`accept` and return the resulting node, even if the original node is
|
||||||
|
destroyed (if it is not destroyed it will just return the original node).
|
||||||
|
|
||||||
|
The behavior of the visitor classes is achieved by overloading the `visit`
|
||||||
|
function for the different `AstNode` derived classes. If a specific
|
||||||
|
implementation is not found, the system will look in turn for overloaded
|
||||||
|
implementations up the inheritance hierarchy. For example calling `accept`
|
||||||
|
on `AstIf` will look in turn for:
|
||||||
|
|
||||||
|
void visit(AstIf* nodep)
|
||||||
|
void visit(AstNodeIf* nodep)
|
||||||
|
void visit(AstNodeStmt* nodep)
|
||||||
|
void visit(AstNode* nodep)
|
||||||
|
|
||||||
|
There are three ways data is passed between visitor functions.
|
||||||
|
|
||||||
|
1. A visitor-class member variable. This is generally for passing "parent"
|
||||||
|
information down to children. `m_modp` is a common example. It's set to
|
||||||
|
NULL in the constructor, where that node (`AstModule` visitor) sets it,
|
||||||
|
then the children are iterated, then it's cleared. Children under an
|
||||||
|
`AstModule` will see it set, while nodes elsewhere will see it clear. If
|
||||||
|
there can be nested items (for example an `AstFor` under an `AstFor`) the
|
||||||
|
variable needs to be save-set-restored in the `AstFor` visitor, otherwise
|
||||||
|
exiting the lower for will lose the upper for's setting.
|
||||||
|
|
||||||
|
2. User attributes. Each `AstNode` (*Note.* The AST node, not the visitor)
|
||||||
|
has five user attributes, which may be accessed as an integer using the
|
||||||
|
`user1()` through `user5()` methods, or as a pointer (of type `AstNUser`)
|
||||||
|
using the `user1p()` through `user5p()` methods (a common technique lifted
|
||||||
|
from graph traversal packages).
|
||||||
|
+
|
||||||
|
A visitor first clears the one it wants to use by calling
|
||||||
|
`AstNode::user#ClearTree()`, then it can mark any node's `user#()` with whatever
|
||||||
|
data it wants. Readers just call `nodep->user()`, but may need to cast
|
||||||
|
appropriately, so you'll often see `VN_CAST(nodep->userp(), SOMETYPE)`.
|
||||||
|
At the top of each visitor are comments describing how the `user()` stuff
|
||||||
|
applies to that visitor class. For example:
|
||||||
|
+
|
||||||
|
// NODE STATE
|
||||||
|
// Cleared entire netlist
|
||||||
|
// AstModule::user1p() // bool. True to inline this module
|
||||||
|
+
|
||||||
|
This says that at the `AstNetlist` `user1ClearTree()` is called. Each
|
||||||
|
`AstModule`'s `user1()` is used to indicate if we're going to inline it.
|
||||||
|
+
|
||||||
|
These comments are important to make sure a `user#()` on a given `AstNode`
|
||||||
|
type is never being used for two different purposes.
|
||||||
|
+
|
||||||
|
Note that calling `user#ClearTree` is fast, it doesn't walk the tree, so
|
||||||
|
it's ok to call fairly often. For example, it's commonly called on every
|
||||||
|
module.
|
||||||
|
|
||||||
|
3. Parameters can be passed between the visitors in close to the "normal"
|
||||||
|
function caller to callee way. This is the second `vup` parameter of type
|
||||||
|
`AstNUser` that is ignored on most of the visitor functions. V3Width does
|
||||||
|
this, but it proved more messy than the above and is deprecated. (V3Width
|
||||||
|
was nearly the first module written. Someday this scheme may be removed,
|
||||||
|
as it slows the program down to have to pass vup everywhere.)
|
||||||
|
|
||||||
|
=== Iterators
|
||||||
|
|
||||||
|
`AstNVisitor` provides a set of iterators to facilitate walking over the
|
||||||
|
tree. Each operates on the current `AstNVisitor` class (as this) and takes
|
||||||
|
an argument type `AstNode*`.
|
||||||
|
|
||||||
|
`iterate`::
|
||||||
|
|
||||||
|
Applies the `accept` method of the `AstNode` to the visitor function.
|
||||||
|
|
||||||
|
`iterateAndNextIgnoreEdit`::
|
||||||
|
|
||||||
|
Applies the `accept` method of each `AstNode` in a list (i.e. connected by
|
||||||
|
`nextp` and `backp` pointers).
|
||||||
|
|
||||||
|
`iterateAndNextNull`::
|
||||||
|
|
||||||
|
Applies the `accept` method of each `AstNode` in a list, only if the
|
||||||
|
provided node is non-NULL. If a node is edited by the call to `accept`,
|
||||||
|
apply `accept` again, until the node does not change.
|
||||||
|
|
||||||
|
`iterateListBackwards`::
|
||||||
|
|
||||||
|
Applies the `accept` method of each `AstNode` in a list, starting with the
|
||||||
|
last one.
|
||||||
|
|
||||||
|
`iterateChildren`::
|
||||||
|
|
||||||
|
Applies the `iterateAndNextNull` method on each child `op1p` through `op4p`
|
||||||
|
in turn.
|
||||||
|
|
||||||
|
`iterateChildrenBackwards`::
|
||||||
|
|
||||||
|
Applies the `iterateListBackwards` method on each child `op1p` through
|
||||||
|
`op4p` in turn.
|
||||||
|
|
||||||
|
==== Caution on Using Iterators When Child Changes
|
||||||
|
|
||||||
|
Visitors often replace one node with another node; V3Width and V3Const are
|
||||||
|
major examples. A visitor which is the parent of such a replacement needs
|
||||||
|
to be aware that calling iteration may cause the children to change. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
// nodep->lhsp() is 0x1234000
|
||||||
|
iterateAndNextNull(nodep->lhsp()); // and under covers nodep->lhsp() changes
|
||||||
|
// nodep->lhsp() is 0x5678400
|
||||||
|
iterateAndNextNull(nodep->lhsp());
|
||||||
|
|
||||||
|
Will work fine, as even if the first iterate causes a new node to take the
|
||||||
|
place of the `lhsp()`, that edit will update `nodep->lhsp()` and the second
|
||||||
|
call will correctly see the change. Alternatively:
|
||||||
|
|
||||||
|
lp = nodep->lhsp();
|
||||||
|
// nodep->lhsp() is 0x1234000, lp is 0x1234000
|
||||||
|
iterateAndNextNull(lp); **lhsp=NULL;** // and under covers nodep->lhsp() changes
|
||||||
|
// nodep->lhsp() is 0x5678400, lp is 0x1234000
|
||||||
|
iterateAndNextNull(lp);
|
||||||
|
|
||||||
|
This will cause bugs or a core dump, as lp is a dangling pointer. Thus it
|
||||||
|
is advisable to set lhsp=NULL shown in the *'s above to make sure these
|
||||||
|
dangles are avoided. Another alternative used in special cases mostly in
|
||||||
|
V3Width is to use acceptSubtreeReturnEdits, which operates on a single node
|
||||||
|
and returns the new pointer if any. Note acceptSubtreeReturnEdits does not
|
||||||
|
follow `nextp()` links.
|
||||||
|
|
||||||
|
lp = acceptSubtreeReturnEdits(lp)
|
||||||
|
|
||||||
|
=== Identifying derived classes
|
||||||
|
|
||||||
|
A common requirement is to identify the specific `AstNode` class we are
|
||||||
|
dealing with. For example a visitor might not implement separate `visit`
|
||||||
|
methods for `AstIf` and `AstGenIf`, but just a single method for the base
|
||||||
|
class:
|
||||||
|
|
||||||
|
void visit (AstNodeIf* nodep)
|
||||||
|
|
||||||
|
However that method might want to specify additional code if it is called
|
||||||
|
for `AstGenIf`. Verilator does this by providing a `VN_IS` method for each
|
||||||
|
possible node type, which returns true if the node is of that type (or
|
||||||
|
derived from that type). So our `visit` method could use:
|
||||||
|
|
||||||
|
if (VN_IS(nodep, AstGenIf) {
|
||||||
|
<code specific to AstGenIf>
|
||||||
|
}
|
||||||
|
|
||||||
|
Additionally the `VN_CAST` method converts pointers similar to C++
|
||||||
|
`dynamic_cast`. This either returns a pointer to the object cast to that
|
||||||
|
type (if it is of class `SOMETYPE`, or a derived class of `SOMETYPE`) or
|
||||||
|
else NULL. (However, for true/false tests use `VN_IS` as that is faster.)
|
||||||
|
|
||||||
|
== Testing
|
||||||
|
|
||||||
|
For an overview of how to write a test see the BUGS section of the
|
||||||
|
Verilator primary manual.
|
||||||
|
|
||||||
|
It is important to add tests for failures as well as success (for example
|
||||||
|
to check that an error message is correctly triggered).
|
||||||
|
|
||||||
|
Tests that fail should by convention have the suffix `_bad` in their name,
|
||||||
|
and include `fails = 1` in either their `compile` or `execute` step as
|
||||||
|
appropriate.
|
||||||
|
|
||||||
|
=== Preparing to Run Tests
|
||||||
|
|
||||||
|
For all tests to pass you must install the following packages:
|
||||||
|
|
||||||
|
* SystemC to compile the SystemC outputs, see http://systemc.org
|
||||||
|
|
||||||
|
* Parallel::Forker from CPAN to run tests in parallel, you can install this
|
||||||
|
with e.g. "sudo cpan install Parallel::Forker".
|
||||||
|
|
||||||
|
* vcddiff to find differences in VCD outputs. See the readme at
|
||||||
|
https://github.com/veripool/vcddiff
|
||||||
|
|
||||||
|
=== Controlling the Test Driver
|
||||||
|
|
||||||
|
Test drivers are written in PERL. All invoke the main test driver script,
|
||||||
|
which can provide detailed help on all the features available when writing
|
||||||
|
a test driver.
|
||||||
|
|
||||||
|
test_regress/t/driver.pl --help
|
||||||
|
|
||||||
|
For convenience, a summary of the most commonly used features is provided
|
||||||
|
here. All drivers require a call to `compile` subroutine to compile the
|
||||||
|
test. For run-time tests, this is followed by a call to the `execute`
|
||||||
|
subroutine. Both of these functions can optionally be provided with a hash
|
||||||
|
table as argument specifying additional options.
|
||||||
|
|
||||||
|
The test driver assumes by default that the source Verilog file name
|
||||||
|
matches the PERL driver name. So a test whose driver is `t/t_mytest.pl`
|
||||||
|
will expect a Verilog source file `t/t_mytest.v`. This can be changed
|
||||||
|
using the `top_filename` subroutine, for example
|
||||||
|
|
||||||
|
top_filename("t/t_myothertest.v");
|
||||||
|
|
||||||
|
By default all tests will run with major simulators (Icarus Verilog, NC,
|
||||||
|
VCS, ModelSim, etc) as well as Verilator, to allow results to be
|
||||||
|
compared. However if you wish a test only to be used with Verilator, you
|
||||||
|
can use the following:
|
||||||
|
|
||||||
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
|
Of the many options that can be set through arguments to `compiler` and
|
||||||
|
`execute`, the following are particularly useful:
|
||||||
|
|
||||||
|
`verilator_flags2`::
|
||||||
|
|
||||||
|
A list of flags to be passed to verilator when compiling.
|
||||||
|
|
||||||
|
`fails`::
|
||||||
|
|
||||||
|
Set to 1 to indicate that the compilation or execution is intended to fail.
|
||||||
|
|
||||||
|
For example the following would specify that compilation requires two
|
||||||
|
defines and is expected to fail.
|
||||||
|
|
||||||
|
compile (
|
||||||
|
verilator_flags2 => ["-DSMALL_CLOCK -DGATED_COMMENT"],
|
||||||
|
fails => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
=== Regression Testing for Developers
|
||||||
|
|
||||||
|
Developers will also want to call ./configure with two extra flags:
|
||||||
|
|
||||||
|
`--enable-ccwarn`::
|
||||||
|
|
||||||
|
Causes the build to stop on warnings as well as errors. A good way to
|
||||||
|
ensure no sloppy code gets added, however it can be painful when it comes
|
||||||
|
to testing, since third party code used in the tests (e.g. SystemC) may not
|
||||||
|
be warning free.
|
||||||
|
|
||||||
|
`--enable-longtests`::
|
||||||
|
|
||||||
|
In addition to the standard C, SystemC examples, also run the tests in the
|
||||||
|
`test_regress` directory when using _make test_'. This is disabled by
|
||||||
|
default as SystemC installation problems would otherwise falsely indicate a
|
||||||
|
Verilator problem.
|
||||||
|
|
||||||
|
When enabling the long tests, some additional PERL modules are needed,
|
||||||
|
which you can install using cpan.
|
||||||
|
|
||||||
|
cpan install Unix::Processors
|
||||||
|
|
||||||
|
There are some traps to avoid when running regression tests
|
||||||
|
|
||||||
|
* When checking the MANIFEST, the test will barf on unexpected code in the
|
||||||
|
Verilator tree. So make sure to keep any such code outside the tree.
|
||||||
|
|
||||||
|
* Not all Linux systems install Perldoc by default. This is needed for the
|
||||||
|
`--help_' option to Verilator, and also for regression testing. This can be
|
||||||
|
installed using cpan:
|
||||||
|
+
|
||||||
|
cpan install Pod::Perldoc
|
||||||
|
+
|
||||||
|
Many Linux systems also offer a standard package for this. Red
|
||||||
|
Hat/Fedora/Centos offer _perl-Pod-Perldoc_', while Debian/Ubuntu/Linux Mint
|
||||||
|
offer `perl-doc'.
|
||||||
|
|
||||||
|
* Running regression may exhaust resources on some Linux systems,
|
||||||
|
particularly file handles and user processes. Increase these to
|
||||||
|
respectively 16,384 and 4,096. The method of doing this is system
|
||||||
|
dependent, but on Fedora Linux it would require editing the
|
||||||
|
`/etc/security/limits.conf` file as root.
|
||||||
|
|
||||||
|
=== Continuous Integration
|
||||||
|
|
||||||
|
Verilator has a https://travis-ci.com/verilator/verilator[Travis CI environment]
|
||||||
|
which automatically tests the master branch for test failures on new commits. It
|
||||||
|
also runs a daily cron job to validate all of the tests against different OS and
|
||||||
|
compiler versions.
|
||||||
|
|
||||||
|
Developers can connect Travis CI to their GitHub account so that the CI
|
||||||
|
enviroment can check their branches too by doing the following:
|
||||||
|
|
||||||
|
* Go to https://github.com/marketplace/travis-ci and follow the prompts
|
||||||
|
|
||||||
|
* Only the Open Source (FREE) version of Travis CI is required
|
||||||
|
|
||||||
|
* Under a Travis CI project click More options > Settings in order to set up a
|
||||||
|
cron job on a particular branch
|
||||||
|
|
||||||
|
== Debugging
|
||||||
|
|
||||||
|
=== --debug
|
||||||
|
|
||||||
|
When you run with `--debug` there are two primary output file types placed
|
||||||
|
into the obj_dir, .tree and .dot files.
|
||||||
|
|
||||||
|
=== .dot output
|
||||||
|
|
||||||
|
Dot files are dumps of internal graphs in
|
||||||
|
http://www.graphviz.org/[Graphviz] dot format. When a dot file is dumped,
|
||||||
|
Verilator will also print a line on stdout that can be used to format the
|
||||||
|
output, for example:
|
||||||
|
|
||||||
|
dot -Tps -o ~/a.ps obj_dir/Vtop_foo.dot
|
||||||
|
|
||||||
|
You can then print a.ps. You may prefer gif format, which doesn't get
|
||||||
|
scaled so can be more useful with large graphs.
|
||||||
|
|
||||||
|
For dynamic graph viewing consider ZGRViewer
|
||||||
|
http://zvtm.sourceforge.net/zgrviewer.html. If you know of better
|
||||||
|
viewers let us know; ZGRViewer isn't great for large graphs.
|
||||||
|
|
||||||
|
=== .tree output
|
||||||
|
|
||||||
|
Tree files are dumps of the AST Tree and are produced between every major
|
||||||
|
algorithmic stage. An example:
|
||||||
|
|
||||||
|
NETLIST 0x90fb00 <e1> {a0}
|
||||||
|
1: MODULE 0x912b20 <e8822> {a8} top L2 [P]
|
||||||
|
*1:2: VAR 0x91a780 <e74#> {a22} @dt=0xa2e640(w32) out_wide [O] WIRE
|
||||||
|
1:2:1: BASICDTYPE 0xa2e640 <e2149> {e24} @dt=this(sw32) integer kwd=integer range=[31:0]
|
||||||
|
|
||||||
|
The following summarizes the above example dump, with more detail on each
|
||||||
|
field in the section below.
|
||||||
|
|
||||||
|
[cols="20%,80%"]
|
||||||
|
|===
|
||||||
|
|
||||||
|
|`1:2:` | indicates the hierarchy of the `VAR` is the `op2p` pointer under
|
||||||
|
the `MODULE`, which in turn is the `op1p` pointer under the `NETLIST`
|
||||||
|
|
||||||
|
|`VAR` | is the AstNodeType.
|
||||||
|
|
||||||
|
| `0x91a780` | is the address of this node.
|
||||||
|
|
||||||
|
| `<e74>` | means the 74th edit to the netlist was the last modification to
|
||||||
|
this node.
|
||||||
|
|
||||||
|
| `{a22}` | indicates this node is related to line 22 in the source filename
|
||||||
|
"a", where "a" is the first file read, "z" the 26th, and "aa" the 27th.
|
||||||
|
|
||||||
|
| `@dt=0x...` | indicates the address of the data type this node contains.
|
||||||
|
|
||||||
|
| `w32` | indicates the width is 32 bits.
|
||||||
|
|
||||||
|
| `out_wide` | is the name of the node, in this case the name of the variable.
|
||||||
|
|
||||||
|
| `[O]` | are flags which vary with the type of node, in this case it means the
|
||||||
|
variable is an output.
|
||||||
|
|
||||||
|
|===
|
||||||
|
|
||||||
|
In more detail the following fields are dumped common to all nodes. They
|
||||||
|
are produced by the `AstNode::dump()` method:
|
||||||
|
|
||||||
|
Tree Hierarchy::
|
||||||
|
|
||||||
|
The dump lines begin with numbers and colons to indicate the child node
|
||||||
|
hierarchy. As noted above, `AstNode` has lists of items at the same level
|
||||||
|
in the AST, connected by the `nextp()` and `prevp()` pointers. These appear
|
||||||
|
as nodes at the same level. For example after inlining:
|
||||||
|
|
||||||
|
NETLIST 0x929c1c8 <e1> {a0} w0
|
||||||
|
1: MODULE 0x92bac80 <e3144> {e14} w0 TOP_t L1 [P]
|
||||||
|
1:1: CELLINLINE 0x92bab18 <e3686#> {e14} w0 v -> t
|
||||||
|
1:1: CELLINLINE 0x92bc1d8 <e3688#> {e24} w0 v__DOT__i_test_gen -> test_gen
|
||||||
|
...
|
||||||
|
1: MODULE 0x92b9bb0 <e503> {e47} w0 test_gen L3
|
||||||
|
...
|
||||||
|
|
||||||
|
AstNode type::
|
||||||
|
|
||||||
|
The textual name of this node AST type (always in capitals). Many of these
|
||||||
|
correspond directly to Verilog entities (for example `MODULE` and
|
||||||
|
`TASK`), but others are internal to Verilator (for example `NETLIST` and
|
||||||
|
`BASICDTYPE`).
|
||||||
|
|
||||||
|
Address of the node::
|
||||||
|
|
||||||
|
A hexadecimal address of the node in memory. Useful for examining with the
|
||||||
|
debugger.
|
||||||
|
|
||||||
|
Last edit number::
|
||||||
|
|
||||||
|
Of the form `<ennnn>` or `<ennnn#>` , where `nnnn` is the number
|
||||||
|
of the last edit to modify this node. The trailing `#` indicates the node
|
||||||
|
has been edited since the last tree dump (which typically means in the last
|
||||||
|
refinement or optimization pass). GDB can watch for this, see << /Debugging >>.
|
||||||
|
|
||||||
|
Source file and line::
|
||||||
|
|
||||||
|
Of the form `{xxnnnn}`, where C{xx} is the filename letter (or
|
||||||
|
letters) and `nnnn` is the line number within that file. The first file is
|
||||||
|
`a`, the 26th is `z`, the 27th is `aa` and so on.
|
||||||
|
|
||||||
|
User pointers::
|
||||||
|
|
||||||
|
Shows the value of the node's user1p...user5p, if non-NULL.
|
||||||
|
|
||||||
|
Data type::
|
||||||
|
|
||||||
|
Many nodes have an explicit data type. "@dt=0x..." indicates the address
|
||||||
|
of the data type (AstNodeDType) this node uses.
|
||||||
|
+
|
||||||
|
If a data type is present and is numeric, it then prints the width of the
|
||||||
|
item. This field is a sequence of flag characters and width data as follows:
|
||||||
|
+
|
||||||
|
* `s` if the node is signed.
|
||||||
|
* `d` if the node is a double (i.e a floating point entity).
|
||||||
|
* `w` always present, indicating this is the width field.
|
||||||
|
* `u` if the node is unsized.
|
||||||
|
* `/nnnn` if the node is unsized, where `nnnn` is the minimum width.
|
||||||
|
|
||||||
|
Name of the entity represented by the node if it exists::
|
||||||
|
|
||||||
|
For example for a `VAR` it is the name of the variable.
|
||||||
|
|
||||||
|
Many nodes follow these fields with additional node specific
|
||||||
|
information. Thus the `VARREF` node will print either `[LV]` or `[RV]`
|
||||||
|
to indicate a left value or right value, followed by the node of the
|
||||||
|
variable being referred to. For example:
|
||||||
|
|
||||||
|
1:2:1:1: VARREF 0x92c2598 <e509> {e24} w0 clk [RV] <- VAR 0x92a2e90 <e79> {e18} w0 clk [I] INPUT
|
||||||
|
|
||||||
|
In general, examine the `dump()` method in `V3AstNodes.cpp` of the node
|
||||||
|
type in question to determine additional fields that may be printed.
|
||||||
|
|
||||||
|
The `MODULE` has a list of `CELLINLINE` nodes referred to by its
|
||||||
|
`op1p()` pointer, connected by `nextp()` and `prevp()` pointers.
|
||||||
|
|
||||||
|
Similarly the `NETLIST` has a list of modules referred to by its `op1p()`
|
||||||
|
pointer.
|
||||||
|
|
||||||
|
=== Debugging with GDB
|
||||||
|
|
||||||
|
The test_regress/driver.pl script accepts `--debug --gdb` to start
|
||||||
|
Verilator under gdb and break when an error is hit or the program is about
|
||||||
|
to exit. You can also use `--debug --gdbbt` to just backtrace and then
|
||||||
|
exit gdb. To debug the Verilated executable, use `--gdbsim`.
|
||||||
|
|
||||||
|
If you wish to start Verilator under GDB (or another debugger), then you
|
||||||
|
can use `--debug` and look at the underlying invocation of
|
||||||
|
`verilator_dbg`. For example
|
||||||
|
|
||||||
|
t/t_alw_dly.pl --debug
|
||||||
|
|
||||||
|
shows it invokes the command:
|
||||||
|
|
||||||
|
../verilator_bin_dbg --prefix Vt_alw_dly --x-assign unique --debug
|
||||||
|
-cc -Mdir obj_dir/t_alw_dly --debug-check -f input.vc t/t_alw_dly.v
|
||||||
|
|
||||||
|
Start GDB, then `start` with the remaining arguments.
|
||||||
|
|
||||||
|
gdb ../verilator_bin_dbg
|
||||||
|
...
|
||||||
|
(gdb) start --prefix Vt_alw_dly --x-assign unique --debug -cc -Mdir
|
||||||
|
obj_dir/t_alw_dly --debug-check -f input.vc t/t_alw_dly.v
|
||||||
|
> obj_dir/t_alw_dly/vlt_compile.log
|
||||||
|
...
|
||||||
|
Temporary breakpoint 1, main (argc=13, argv=0xbfffefa4, env=0xbfffefdc)
|
||||||
|
at ../Verilator.cpp:615
|
||||||
|
615 ios::sync_with_stdio();
|
||||||
|
(gdb)
|
||||||
|
|
||||||
|
You can then continue execution with breakpoints as required.
|
||||||
|
|
||||||
|
To break at a specific edit number which changed a node (presumably to find
|
||||||
|
what made a <e####> line in the tree dumps):
|
||||||
|
|
||||||
|
watch AstNode::s_editCntGbl==####
|
||||||
|
|
||||||
|
To print a node:
|
||||||
|
|
||||||
|
pn nodep
|
||||||
|
# or: call nodep->dumpGdb() # aliased to "pn" in src/.gdbinit
|
||||||
|
pnt nodep
|
||||||
|
# or: call nodep->dumpTreeGdb() # aliased to "pnt" in src/.gdbinit
|
||||||
|
|
||||||
|
When GDB halts, it is useful to understand that the backtrace will commonly
|
||||||
|
show the iterator functions between each invocation of `visit` in the
|
||||||
|
backtrace. You will typically see a frame sequence something like
|
||||||
|
|
||||||
|
...
|
||||||
|
visit()
|
||||||
|
iterateChildren()
|
||||||
|
iterateAndNext()
|
||||||
|
accept()
|
||||||
|
visit()
|
||||||
|
...
|
||||||
|
|
||||||
|
== Adding a New Feature
|
||||||
|
|
||||||
|
Generally what would you do to add a new feature?
|
||||||
|
|
||||||
|
. File an issue (if there isn't already) so others know what you're working on.
|
||||||
|
|
||||||
|
. Make a testcase in the test_regress/t/t_EXAMPLE format, see << /TESTING >>.
|
||||||
|
|
||||||
|
. If grammar changes are needed, look at the git version of VerilogPerl's
|
||||||
|
src/VParseGrammar.y, as this grammar supports the full SystemVerilog
|
||||||
|
language and has a lot of back-and-forth with Verilator's grammar. Copy
|
||||||
|
the appropriate rules to src/verilog.y and modify the productions.
|
||||||
|
|
||||||
|
. If a new Ast type is needed, add it to V3AstNodes.h.
|
||||||
|
|
||||||
|
. Now you can run "test_regress/t/t_{new testcase}.pl --debug" and it'll
|
||||||
|
probably fail but you'll see a test_regress/obj_dir/t_{newtestcase}/*.tree
|
||||||
|
file which you can examine to see if the parsing worked. See also the
|
||||||
|
sections above on debugging.
|
||||||
|
|
||||||
|
. Modify the later visitor functions to process the new feature as needed.
|
||||||
|
|
||||||
|
=== Adding a new pass
|
||||||
|
|
||||||
|
For more substantial changes you may need to add a new pass. The simplest
|
||||||
|
way to do this is to copy the `.cpp` and `.h` files from an existing
|
||||||
|
pass. You'll need to add a call into your pass from the `process()`
|
||||||
|
function in `src/verilator.cpp`.
|
||||||
|
|
||||||
|
To get your pass to build you'll need to add its binary filename to the
|
||||||
|
list in `src/Makefile_obj.in` and reconfigure.
|
||||||
|
|
||||||
|
== Distribution
|
||||||
|
|
||||||
|
Copyright 2008-2019 by Wilson Snyder. Verilator is free software; you can
|
||||||
|
redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||||
|
General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||||
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
@@ -24,7 +24,7 @@ endif
|
|||||||
# e.g. examples/tracing_c.
|
# e.g. examples/tracing_c.
|
||||||
|
|
||||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||||
# package inatall, and verilator is in your path. Otherwise find the
|
# package install, and verilator is in your path. Otherwise find the
|
||||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||||
ifeq ($(VERILATOR_ROOT),)
|
ifeq ($(VERILATOR_ROOT),)
|
||||||
VERILATOR = verilator
|
VERILATOR = verilator
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ endif
|
|||||||
# e.g. examples/tracing_sc.
|
# e.g. examples/tracing_sc.
|
||||||
|
|
||||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||||
# package inatall, and verilator is in your path. Otherwise find the
|
# package install, and verilator is in your path. Otherwise find the
|
||||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||||
ifeq ($(VERILATOR_ROOT),)
|
ifeq ($(VERILATOR_ROOT),)
|
||||||
VERILATOR = verilator
|
VERILATOR = verilator
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ endif
|
|||||||
# Set up variables
|
# Set up variables
|
||||||
|
|
||||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||||
# package inatall, and verilator is in your path. Otherwise find the
|
# package install, and verilator is in your path. Otherwise find the
|
||||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||||
ifeq ($(VERILATOR_ROOT),)
|
ifeq ($(VERILATOR_ROOT),)
|
||||||
VERILATOR = verilator
|
VERILATOR = verilator
|
||||||
|
|||||||
@@ -14,18 +14,18 @@ module top
|
|||||||
input fastclk,
|
input fastclk,
|
||||||
input reset_l,
|
input reset_l,
|
||||||
|
|
||||||
output [1:0] out_small,
|
output wire [1:0] out_small,
|
||||||
output [39:0] out_quad,
|
output wire [39:0] out_quad,
|
||||||
output [69:0] out_wide,
|
output wire [69:0] out_wide,
|
||||||
input [1:0] in_small,
|
input [1:0] in_small,
|
||||||
input [39:0] in_quad,
|
input [39:0] in_quad,
|
||||||
input [69:0] in_wide
|
input [69:0] in_wide
|
||||||
);
|
);
|
||||||
|
|
||||||
// Connect up the outputs, using some trivial logic
|
// Connect up the outputs, using some trivial logic
|
||||||
wire [1:0] out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
assign out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
||||||
wire [39:0] out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
assign out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
||||||
wire [69:0] out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
assign out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
||||||
|
|
||||||
// And an example sub module. The submodule will print stuff.
|
// And an example sub module. The submodule will print stuff.
|
||||||
sub sub (/*AUTOINST*/
|
sub sub (/*AUTOINST*/
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ endif
|
|||||||
# Set up variables
|
# Set up variables
|
||||||
|
|
||||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||||
# package inatall, and verilator is in your path. Otherwise find the
|
# package install, and verilator is in your path. Otherwise find the
|
||||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||||
ifeq ($(VERILATOR_ROOT),)
|
ifeq ($(VERILATOR_ROOT),)
|
||||||
VERILATOR = verilator
|
VERILATOR = verilator
|
||||||
|
|||||||
@@ -14,18 +14,18 @@ module top
|
|||||||
input fastclk,
|
input fastclk,
|
||||||
input reset_l,
|
input reset_l,
|
||||||
|
|
||||||
output [1:0] out_small,
|
output wire [1:0] out_small,
|
||||||
output [39:0] out_quad,
|
output wire [39:0] out_quad,
|
||||||
output [69:0] out_wide,
|
output wire [69:0] out_wide,
|
||||||
input [1:0] in_small,
|
input [1:0] in_small,
|
||||||
input [39:0] in_quad,
|
input [39:0] in_quad,
|
||||||
input [69:0] in_wide
|
input [69:0] in_wide
|
||||||
);
|
);
|
||||||
|
|
||||||
// Connect up the outputs, using some trivial logic
|
// Connect up the outputs, using some trivial logic
|
||||||
wire [1:0] out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
assign out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
||||||
wire [39:0] out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
assign out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
||||||
wire [69:0] out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
assign out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
||||||
|
|
||||||
// And an example sub module. The submodule will print stuff.
|
// And an example sub module. The submodule will print stuff.
|
||||||
sub sub (/*AUTOINST*/
|
sub sub (/*AUTOINST*/
|
||||||
|
|||||||
@@ -663,6 +663,7 @@ return(rc);
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FST_DYNAMIC_ALIAS2_DISABLE
|
||||||
static int fstWriterSVarint(FILE *handle, int64_t v)
|
static int fstWriterSVarint(FILE *handle, int64_t v)
|
||||||
{
|
{
|
||||||
unsigned char buf[15]; /* ceil(64/7) = 10 + sign byte padded way up */
|
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);
|
fstFwrite(buf, len, 1, handle);
|
||||||
return(len);
|
return(len);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/***********************/
|
/***********************/
|
||||||
@@ -4810,6 +4812,7 @@ uint32_t cur_blackout = 0;
|
|||||||
int packtype;
|
int packtype;
|
||||||
unsigned char *mc_mem = NULL;
|
unsigned char *mc_mem = NULL;
|
||||||
uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */
|
uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */
|
||||||
|
int dumpvars_state = 0;
|
||||||
|
|
||||||
if(!xc) return(0);
|
if(!xc) return(0);
|
||||||
|
|
||||||
@@ -4819,7 +4822,6 @@ length_remaining = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
|
|||||||
|
|
||||||
if(fv)
|
if(fv)
|
||||||
{
|
{
|
||||||
fprintf(fv, "$dumpvars\n");
|
|
||||||
#ifndef FST_WRITEX_DISABLE
|
#ifndef FST_WRITEX_DISABLE
|
||||||
fflush(fv);
|
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 */
|
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(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);
|
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
|
||||||
fstWritex(xc, wx_buf, wx_len);
|
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))
|
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]);
|
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
|
||||||
fstWritex(xc, wx_buf, wx_len);
|
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))
|
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
|
||||||
{
|
{
|
||||||
|
|||||||
+129
-55
@@ -22,7 +22,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
#define _VERILATED_CPP_
|
#define _VERILATED_CPP_
|
||||||
|
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
@@ -43,9 +43,13 @@
|
|||||||
// Static sanity checks (when get C++11 can use static_assert)
|
// Static sanity checks (when get C++11 can use static_assert)
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
|
// cppcheck-suppress unusedStructMember // Unused as is assertion
|
||||||
char vluint8_incorrect[(sizeof(vluint8_t) == 1) ? 1:-1];
|
char vluint8_incorrect[(sizeof(vluint8_t) == 1) ? 1:-1];
|
||||||
|
// cppcheck-suppress unusedStructMember // Unused as is assertion
|
||||||
char vluint16_incorrect[(sizeof(vluint16_t) == 2) ? 1:-1];
|
char vluint16_incorrect[(sizeof(vluint16_t) == 2) ? 1:-1];
|
||||||
|
// cppcheck-suppress unusedStructMember // Unused as is assertion
|
||||||
char vluint32_incorrect[(sizeof(vluint32_t) == 4) ? 1:-1];
|
char vluint32_incorrect[(sizeof(vluint32_t) == 4) ? 1:-1];
|
||||||
|
// cppcheck-suppress unusedStructMember // Unused as is assertion
|
||||||
char vluint64_incorrect[(sizeof(vluint64_t) == 8) ? 1:-1];
|
char vluint64_incorrect[(sizeof(vluint64_t) == 8) ? 1:-1];
|
||||||
} vl_static_checks_t;
|
} vl_static_checks_t;
|
||||||
|
|
||||||
@@ -71,9 +75,11 @@ VerilatedImp VerilatedImp::s_s;
|
|||||||
#ifndef VL_USER_FINISH ///< Define this to override this function
|
#ifndef VL_USER_FINISH ///< Define this to override this function
|
||||||
void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
||||||
if (0 && hier) {}
|
if (0 && hier) {}
|
||||||
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum); // Not VL_PRINTF_MT, already on main thread
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||||
|
"- %s:%d: Verilog $finish\n", filename, linenum);
|
||||||
if (Verilated::gotFinish()) {
|
if (Verilated::gotFinish()) {
|
||||||
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum); // Not VL_PRINTF_MT, already on main thread
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||||
|
"- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
|
||||||
Verilated::flushCall();
|
Verilated::flushCall();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -93,7 +99,8 @@ void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
|||||||
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
|
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
|
||||||
if (0 && hier) {}
|
if (0 && hier) {}
|
||||||
Verilated::gotFinish(true);
|
Verilated::gotFinish(true);
|
||||||
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg); // Not VL_PRINTF_MT, already on main thread
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||||
|
"%%Error: %s:%d: %s\n", filename, linenum, msg);
|
||||||
Verilated::flushCall();
|
Verilated::flushCall();
|
||||||
|
|
||||||
VL_PRINTF("Aborting...\n"); // Not VL_PRINTF_MT, already on main thread
|
VL_PRINTF("Aborting...\n"); // Not VL_PRINTF_MT, already on main thread
|
||||||
@@ -167,7 +174,8 @@ vluint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
|
|||||||
|
|
||||||
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
|
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
|
||||||
#ifdef VL_THREADED
|
#ifdef VL_THREADED
|
||||||
// Alternative is to use std::this_thread::get_id, but that returns a hard-to-read number and is very slow
|
// Alternative is to use std::this_thread::get_id, but that returns a
|
||||||
|
// hard-to-read number and is very slow
|
||||||
static std::atomic<vluint32_t> s_nextId(0);
|
static std::atomic<vluint32_t> s_nextId(0);
|
||||||
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
|
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
|
||||||
return t_myId;
|
return t_myId;
|
||||||
@@ -281,6 +289,7 @@ IData VL_RANDOM_I(int obits) VL_MT_SAFE {
|
|||||||
QData VL_RANDOM_Q(int obits) VL_MT_SAFE {
|
QData VL_RANDOM_Q(int obits) VL_MT_SAFE {
|
||||||
return vl_rand64() & VL_MASK_Q(obits);
|
return vl_rand64() & VL_MASK_Q(obits);
|
||||||
}
|
}
|
||||||
|
// VL_RANDOM_W currently unused as $random always 32 bits
|
||||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||||
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
||||||
if (i<(VL_WORDS_I(obits)-1)) {
|
if (i<(VL_WORDS_I(obits)-1)) {
|
||||||
@@ -338,7 +347,8 @@ void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp) VL_MT_SAFE {
|
|||||||
//===========================================================================
|
//===========================================================================
|
||||||
// Slow math
|
// Slow math
|
||||||
|
|
||||||
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus) VL_MT_SAFE {
|
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
|
||||||
|
bool is_modulus) VL_MT_SAFE {
|
||||||
// See Knuth Algorithm D. Computes u/v = q.r
|
// See Knuth Algorithm D. Computes u/v = q.r
|
||||||
// This isn't massively tuned, as wide division is rare
|
// This isn't massively tuned, as wide division is rare
|
||||||
// for debug see V3Number version
|
// for debug see V3Number version
|
||||||
@@ -398,7 +408,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
|
|||||||
// Main loop
|
// Main loop
|
||||||
for (int j = uw - vw; j >= 0; --j) {
|
for (int j = uw - vw; j >= 0; --j) {
|
||||||
// Estimate
|
// Estimate
|
||||||
vluint64_t unw64 = (static_cast<vluint64_t>(un[j+vw])<<VL_ULL(32) | static_cast<vluint64_t>(un[j+vw-1]));
|
vluint64_t unw64 = (static_cast<vluint64_t>(un[j+vw])<<VL_ULL(32)
|
||||||
|
| static_cast<vluint64_t>(un[j+vw-1]));
|
||||||
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw-1]);
|
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw-1]);
|
||||||
vluint64_t rhat = unw64 - qhat*static_cast<vluint64_t>(vn[vw-1]);
|
vluint64_t rhat = unw64 - qhat*static_cast<vluint64_t>(vn[vw-1]);
|
||||||
|
|
||||||
@@ -447,7 +458,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
WDataOutP VL_POW_WWW(int obits, int, int rbits,
|
||||||
|
WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
||||||
// obits==lbits, rbits can be different
|
// obits==lbits, rbits can be different
|
||||||
owp[0] = 1;
|
owp[0] = 1;
|
||||||
for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
|
for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
|
||||||
@@ -469,7 +481,8 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDa
|
|||||||
}
|
}
|
||||||
return owp;
|
return owp;
|
||||||
}
|
}
|
||||||
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs) VL_MT_SAFE {
|
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits,
|
||||||
|
WDataOutP owp, WDataInP lwp, QData rhs) VL_MT_SAFE {
|
||||||
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
|
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
|
||||||
return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
|
return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
|
||||||
}
|
}
|
||||||
@@ -512,7 +525,8 @@ WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits,
|
|||||||
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
|
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
|
||||||
return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
|
return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
|
||||||
}
|
}
|
||||||
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
|
QData VL_POWSS_QQW(int obits, int, int rbits,
|
||||||
|
QData lhs, WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
|
||||||
// Skip check for rhs == 0, as short-circuit doesn't save time
|
// Skip check for rhs == 0, as short-circuit doesn't save time
|
||||||
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
||||||
if (lhs==0) return 0; // "X"
|
if (lhs==0) return 0; // "X"
|
||||||
@@ -633,14 +647,28 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
|||||||
}
|
}
|
||||||
case 'e':
|
case 'e':
|
||||||
case 'f':
|
case 'f':
|
||||||
case 'g': {
|
case 'g':
|
||||||
|
case '^': { // Realtime
|
||||||
const int lbits = va_arg(ap, int);
|
const int lbits = va_arg(ap, int);
|
||||||
double d = va_arg(ap, double);
|
double d = va_arg(ap, double);
|
||||||
if (lbits) {} // UNUSED - always 64
|
if (lbits) {} // UNUSED - always 64
|
||||||
strncpy(tmpf, pctp, pos-pctp+1);
|
switch (fmt) {
|
||||||
tmpf[pos-pctp+1] = '\0';
|
case '^': { // Realtime
|
||||||
sprintf(tmp, tmpf, d);
|
int digits = sprintf(tmp, "%g", d/VL_TIME_MULTIPLIER);
|
||||||
output += tmp;
|
int needmore = width-digits;
|
||||||
|
if (needmore>0) output.append(needmore, ' '); // Pre-pad spaces
|
||||||
|
output += tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
strncpy(tmpf, pctp, pos-pctp+1);
|
||||||
|
tmpf[pos-pctp+1] = '\0';
|
||||||
|
sprintf(tmp, tmpf, d);
|
||||||
|
output += tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -805,7 +833,8 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
|||||||
if (fp) fgetc(fp);
|
if (fp) fgetc(fp);
|
||||||
else floc -= 8;
|
else floc -= 8;
|
||||||
}
|
}
|
||||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
|
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp,
|
||||||
|
const std::string& fstr) VL_MT_SAFE {
|
||||||
// Get a character without advancing
|
// Get a character without advancing
|
||||||
if (fp) {
|
if (fp) {
|
||||||
int data = fgetc(fp);
|
int data = fgetc(fp);
|
||||||
@@ -822,7 +851,8 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
|
static inline void _vl_vsss_skipspace(FILE* fp, int& floc,
|
||||||
|
WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
|
||||||
while (1) {
|
while (1) {
|
||||||
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||||
if (c==EOF || !isspace(c)) return;
|
if (c==EOF || !isspace(c)) return;
|
||||||
@@ -845,16 +875,19 @@ static inline void _vl_vsss_read(FILE* fp, int& floc, WDataInP fromp, const std:
|
|||||||
*cp++ = '\0';
|
*cp++ = '\0';
|
||||||
//VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
|
//VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
|
||||||
}
|
}
|
||||||
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
|
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb,
|
||||||
|
int nbits, IData ld) VL_MT_SAFE {
|
||||||
for (; nbits && lsb<obits; nbits--, lsb++, ld>>=1) {
|
for (; nbits && lsb<obits; nbits--, lsb++, ld>>=1) {
|
||||||
VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1);
|
VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2,
|
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2,
|
||||||
const char* strp, size_t posstart, size_t posend) VL_MT_SAFE {
|
const char* strp,
|
||||||
|
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||||
int lsb = 0;
|
int lsb = 0;
|
||||||
for (int i=0, pos=static_cast<int>(posend)-1; i<obits && pos>=static_cast<int>(posstart); --pos) {
|
for (int i=0, pos = static_cast<int>(posend)-1;
|
||||||
|
i<obits && pos>=static_cast<int>(posstart); --pos) {
|
||||||
switch (tolower (strp[pos])) {
|
switch (tolower (strp[pos])) {
|
||||||
case 'x': case 'z': case '?': // FALLTHRU
|
case 'x': case 'z': case '?': // FALLTHRU
|
||||||
case '0': lsb += baseLog2; break;
|
case '0': lsb += baseLog2; break;
|
||||||
@@ -1065,8 +1098,8 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
|||||||
IData bytes = VL_BYTES_I(obits);
|
IData bytes = VL_BYTES_I(obits);
|
||||||
char buffer[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
char buffer[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||||
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
|
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
|
||||||
if (VL_UNLIKELY(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
|
if (VL_UNCOVERABLE(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun");
|
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't use fgets, as we must read \0s.
|
// We don't use fgets, as we must read \0s.
|
||||||
@@ -1289,7 +1322,7 @@ const char* memhFormat(int nBits) {
|
|||||||
case 5: VL_SNPRINTF(buf, 32, "%%06x"); break;
|
case 5: VL_SNPRINTF(buf, 32, "%%06x"); break;
|
||||||
case 6: VL_SNPRINTF(buf, 32, "%%07x"); break;
|
case 6: VL_SNPRINTF(buf, 32, "%%07x"); break;
|
||||||
case 7: VL_SNPRINTF(buf, 32, "%%08x"); break;
|
case 7: VL_SNPRINTF(buf, 32, "%%08x"); break;
|
||||||
default: assert(false); break;
|
default: assert(false); break; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -1303,35 +1336,53 @@ void VL_WRITEMEM_N(
|
|||||||
const std::string& filename, // Output file name
|
const std::string& filename, // Output file name
|
||||||
const void* memp, // Array state
|
const void* memp, // Array state
|
||||||
IData start, // First array row address to write
|
IData start, // First array row address to write
|
||||||
IData end // Last address to write
|
IData end // Last address to write, or ~0 when not specified
|
||||||
) VL_MT_SAFE {
|
) VL_MT_SAFE {
|
||||||
if (VL_UNLIKELY(!hex)) {
|
if (VL_UNLIKELY(!hex)) {
|
||||||
VL_FATAL_MT(filename.c_str(), 0, "",
|
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||||
"VL_WRITEMEM_N only supports hex format for now, sorry!");
|
"VL_WRITEMEM_N only supports hex format for now, sorry!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FILE* fp = fopen(filename.c_str(), "w");
|
|
||||||
if (VL_UNLIKELY(!fp)) {
|
// Calculate row address limits
|
||||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
|
size_t row_min = array_lsb;
|
||||||
|
size_t row_max = row_min + depth - 1;
|
||||||
|
|
||||||
|
// Normalize the last address argument: ~0 => row_max
|
||||||
|
size_t nend = (end == ~0u) ? row_max : end;
|
||||||
|
|
||||||
|
// Bounds check the write address range
|
||||||
|
if (VL_UNLIKELY((start < row_min) || (start > row_max)
|
||||||
|
|| (nend < row_min) || (nend > row_max))) {
|
||||||
|
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||||
|
"$writemem specified address out-of-bounds");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int row_addr = start; row_addr <= end; ++row_addr) {
|
if (VL_UNLIKELY(start > nend)) {
|
||||||
if ((row_addr < array_lsb)
|
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||||
|| (row_addr > array_lsb + depth - 1)) {
|
"$writemem invalid address range");
|
||||||
vluint32_t endmax = ~0;
|
return;
|
||||||
if (end != endmax) {
|
}
|
||||||
VL_FATAL_MT(filename.c_str(), 0, "",
|
|
||||||
"$writemem specified address out-of-bounds");
|
|
||||||
}
|
|
||||||
// else, it's not an error to overflow due to end == endmax,
|
|
||||||
// just return cleanly.
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the offset into the memp array.
|
// Calculate row offset range
|
||||||
int row_offset = row_addr - array_lsb;
|
size_t row_start = start - row_min;
|
||||||
|
size_t row_end = nend - row_min;
|
||||||
|
|
||||||
|
// Bail out on possible 32-bit size_t overflow
|
||||||
|
if (VL_UNLIKELY(row_end + 1 == 0)) {
|
||||||
|
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem address is too large");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fp = fopen(filename.c_str(), "w");
|
||||||
|
if (VL_UNLIKELY(!fp)) {
|
||||||
|
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
|
||||||
|
// cppcheck-suppress resourceLeak // fp is NULL - bug in cppcheck
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t row_offset = row_start; row_offset <= row_end; ++row_offset) {
|
||||||
if (width <= 8) {
|
if (width <= 8) {
|
||||||
const CData* datap
|
const CData* datap
|
||||||
= &(reinterpret_cast<const CData*>(memp))[row_offset];
|
= &(reinterpret_cast<const CData*>(memp))[row_offset];
|
||||||
@@ -1380,7 +1431,6 @@ void VL_WRITEMEM_N(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1466,6 +1516,7 @@ void VL_READMEM_N(
|
|||||||
if (VL_UNLIKELY(!fp)) {
|
if (VL_UNLIKELY(!fp)) {
|
||||||
// We don't report the Verilog source filename as it slow to have to pass it down
|
// We don't report the Verilog source filename as it slow to have to pass it down
|
||||||
VL_FATAL_MT(filename.c_str(), 0, "", "$readmem file not found");
|
VL_FATAL_MT(filename.c_str(), 0, "", "$readmem file not found");
|
||||||
|
// cppcheck-suppress resourceLeak // fp is NULL - bug in cppcheck
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Prep for reading
|
// Prep for reading
|
||||||
@@ -1485,8 +1536,15 @@ void VL_READMEM_N(
|
|||||||
if (VL_UNLIKELY(c==EOF)) break;
|
if (VL_UNLIKELY(c==EOF)) break;
|
||||||
//printf("%d: Got '%c' Addr%x IN%d IgE%d IgC%d ninc%d\n",
|
//printf("%d: Got '%c' Addr%x IN%d IgE%d IgC%d ninc%d\n",
|
||||||
// linenum, c, addr, innum, ignore_to_eol, ignore_to_cmt, needinc);
|
// linenum, c, addr, innum, ignore_to_eol, ignore_to_cmt, needinc);
|
||||||
if (c=='\n') { linenum++; ignore_to_eol=false; if (innum) reading_addr=false; innum=false; }
|
if (c=='\n') {
|
||||||
else if (c=='\t' || c==' ' || c=='\r' || c=='\f') { if (innum) reading_addr=false; innum=false; }
|
linenum++; ignore_to_eol = false;
|
||||||
|
if (innum) reading_addr = false;
|
||||||
|
innum = false;
|
||||||
|
}
|
||||||
|
else if (c=='\t' || c==' ' || c=='\r' || c=='\f') {
|
||||||
|
if (innum) reading_addr = false;
|
||||||
|
innum = false;
|
||||||
|
}
|
||||||
// Skip // comments and detect /* comments
|
// Skip // comments and detect /* comments
|
||||||
else if (ignore_to_cmt && lastc=='*' && c=='/') {
|
else if (ignore_to_cmt && lastc=='*' && c=='/') {
|
||||||
ignore_to_cmt = false; if (innum) reading_addr=false; innum=false;
|
ignore_to_cmt = false; if (innum) reading_addr=false; innum=false;
|
||||||
@@ -1537,7 +1595,8 @@ void VL_READMEM_N(
|
|||||||
*datap = ((*datap << static_cast<QData>(shift))
|
*datap = ((*datap << static_cast<QData>(shift))
|
||||||
+ static_cast<QData>(value)) & VL_MASK_Q(width);
|
+ static_cast<QData>(value)) & VL_MASK_Q(width);
|
||||||
} else {
|
} else {
|
||||||
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))[ entry*VL_WORDS_I(width) ];
|
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))
|
||||||
|
[ entry*VL_WORDS_I(width) ];
|
||||||
if (!innum) { VL_ZERO_RESET_W(width, datap); }
|
if (!innum) { VL_ZERO_RESET_W(width, datap); }
|
||||||
_VL_SHIFTL_INPLACE_W(width, datap, static_cast<IData>(shift));
|
_VL_SHIFTL_INPLACE_W(width, datap, static_cast<IData>(shift));
|
||||||
datap[0] |= value;
|
datap[0] |= value;
|
||||||
@@ -1722,9 +1781,11 @@ void Verilated::debug(int level) VL_MT_SAFE {
|
|||||||
s_s.s_debug = level;
|
s_s.s_debug = level;
|
||||||
if (level) {
|
if (level) {
|
||||||
#ifdef VL_DEBUG
|
#ifdef VL_DEBUG
|
||||||
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on. Message prefix indicates {<thread>,<sequence_number>}.\n"););
|
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on."
|
||||||
|
" Message prefix indicates {<thread>,<sequence_number>}.\n"););
|
||||||
#else
|
#else
|
||||||
VL_PRINTF_MT("- Verilated::debug attempted, but compiled without VL_DEBUG, so messages suppressed.\n");
|
VL_PRINTF_MT("- Verilated::debug attempted,"
|
||||||
|
" but compiled without VL_DEBUG, so messages suppressed.\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1788,9 +1849,9 @@ void Verilated::flushCb(VerilatedVoidCb cb) VL_MT_SAFE {
|
|||||||
VerilatedLockGuard lock(m_mutex);
|
VerilatedLockGuard lock(m_mutex);
|
||||||
if (s_flushCb == cb) {} // Ok - don't duplicate
|
if (s_flushCb == cb) {} // Ok - don't duplicate
|
||||||
else if (!s_flushCb) { s_flushCb=cb; }
|
else if (!s_flushCb) { s_flushCb=cb; }
|
||||||
else {
|
else { // LCOV_EXCL_LINE
|
||||||
// Someday we may allow multiple callbacks ala atexit(), but until then
|
// Someday we may allow multiple callbacks ala atexit(), but until then
|
||||||
VL_FATAL_MT("unknown", 0, "",
|
VL_FATAL_MT("unknown", 0, "", // LCOV_EXCL_LINE
|
||||||
"Verilated::flushCb called twice with different callbacks");
|
"Verilated::flushCb called twice with different callbacks");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1951,11 +2012,13 @@ void VerilatedImp::commandArgVl(const std::string& arg) {
|
|||||||
}
|
}
|
||||||
else if (arg == "+verilator+V") {
|
else if (arg == "+verilator+V") {
|
||||||
versionDump(); // Someday more info too
|
versionDump(); // Someday more info too
|
||||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
||||||
|
"Exiting due to command line argument (not an error)");
|
||||||
}
|
}
|
||||||
else if (arg == "+verilator+version") {
|
else if (arg == "+verilator+version") {
|
||||||
versionDump();
|
versionDump();
|
||||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
||||||
|
"Exiting due to command line argument (not an error)");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
|
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
|
||||||
@@ -2044,9 +2107,11 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
|
|||||||
VerilatedScope::VerilatedScope() {
|
VerilatedScope::VerilatedScope() {
|
||||||
m_callbacksp = NULL;
|
m_callbacksp = NULL;
|
||||||
m_namep = NULL;
|
m_namep = NULL;
|
||||||
|
m_identifierp = NULL;
|
||||||
m_funcnumMax = 0;
|
m_funcnumMax = 0;
|
||||||
m_symsp = NULL;
|
m_symsp = NULL;
|
||||||
m_varsp = NULL;
|
m_varsp = NULL;
|
||||||
|
m_type = SCOPE_OTHER;
|
||||||
}
|
}
|
||||||
|
|
||||||
VerilatedScope::~VerilatedScope() {
|
VerilatedScope::~VerilatedScope() {
|
||||||
@@ -2058,15 +2123,19 @@ VerilatedScope::~VerilatedScope() {
|
|||||||
m_funcnumMax = 0; // Force callback table to empty
|
m_funcnumMax = 0; // Force callback table to empty
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) VL_MT_UNSAFE {
|
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp,
|
||||||
|
const char* suffixp, const char* identifier,
|
||||||
|
const Type type) VL_MT_UNSAFE {
|
||||||
// Slowpath - called once/scope at construction
|
// Slowpath - called once/scope at construction
|
||||||
// We don't want the space and reference-count access overhead of strings.
|
// We don't want the space and reference-count access overhead of strings.
|
||||||
m_symsp = symsp;
|
m_symsp = symsp;
|
||||||
|
m_type = type;
|
||||||
char* namep = new char[strlen(prefixp)+strlen(suffixp)+2];
|
char* namep = new char[strlen(prefixp)+strlen(suffixp)+2];
|
||||||
strcpy(namep, prefixp);
|
strcpy(namep, prefixp);
|
||||||
if (*prefixp && *suffixp) strcat(namep, ".");
|
if (*prefixp && *suffixp) strcat(namep, ".");
|
||||||
strcat(namep, suffixp);
|
strcat(namep, suffixp);
|
||||||
m_namep = namep;
|
m_namep = namep;
|
||||||
|
m_identifierp = identifier;
|
||||||
VerilatedImp::scopeInsert(this);
|
VerilatedImp::scopeInsert(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2079,8 +2148,8 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
|
|||||||
// Alternative is to dynamically stretch the array, which is more code, and slower.
|
// Alternative is to dynamically stretch the array, which is more code, and slower.
|
||||||
if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum+1; }
|
if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum+1; }
|
||||||
} else {
|
} else {
|
||||||
if (VL_UNLIKELY(funcnum >= m_funcnumMax)) {
|
if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) {
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE
|
||||||
"Internal: Bad funcnum vs. pre-finalize maximum");
|
"Internal: Bad funcnum vs. pre-finalize maximum");
|
||||||
}
|
}
|
||||||
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
|
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
|
||||||
@@ -2094,7 +2163,8 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
|
|||||||
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
||||||
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE {
|
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE {
|
||||||
// Grab dimensions
|
// Grab dimensions
|
||||||
// In the future we may just create a large table at emit time and statically construct from that.
|
// In the future we may just create a large table at emit time and
|
||||||
|
// statically construct from that.
|
||||||
if (!finalize) return;
|
if (!finalize) return;
|
||||||
|
|
||||||
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
|
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
|
||||||
@@ -2170,6 +2240,10 @@ void VerilatedScope::scopeDump() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VerilatedHierarchy::add(VerilatedScope* fromp, VerilatedScope* top) {
|
||||||
|
VerilatedImp::hierarchyAdd(fromp, top);
|
||||||
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// VerilatedOneThreaded:: Methods
|
// VerilatedOneThreaded:: Methods
|
||||||
|
|
||||||
|
|||||||
+38
-14
@@ -23,7 +23,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_H_
|
#ifndef _VERILATED_H_
|
||||||
#define _VERILATED_H_ 1 ///< Header Guard
|
#define _VERILATED_H_ 1 ///< Header Guard
|
||||||
@@ -189,8 +189,8 @@ public:
|
|||||||
// METHODS
|
// METHODS
|
||||||
/// Check that the current thread ID is the same as the construction thread ID
|
/// Check that the current thread ID is the same as the construction thread ID
|
||||||
void check() VL_MT_UNSAFE_ONE {
|
void check() VL_MT_UNSAFE_ONE {
|
||||||
if (!VL_LIKELY(m_threadid == VL_THREAD_ID())) {
|
if (VL_UNCOVERABLE(m_threadid != VL_THREAD_ID())) {
|
||||||
fatal_different();
|
fatal_different(); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void fatal_different() VL_MT_SAFE;
|
static void fatal_different() VL_MT_SAFE;
|
||||||
@@ -203,6 +203,8 @@ public:
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
/// Base class for all Verilated module classes
|
/// Base class for all Verilated module classes
|
||||||
|
|
||||||
|
class VerilatedScope;
|
||||||
|
|
||||||
class VerilatedModule {
|
class VerilatedModule {
|
||||||
VL_UNCOPYABLE(VerilatedModule);
|
VL_UNCOPYABLE(VerilatedModule);
|
||||||
private:
|
private:
|
||||||
@@ -290,6 +292,11 @@ public: // But for internal use only
|
|||||||
/// This class is initialized by main thread only. Reading post-init is thread safe.
|
/// This class is initialized by main thread only. Reading post-init is thread safe.
|
||||||
|
|
||||||
class VerilatedScope {
|
class VerilatedScope {
|
||||||
|
public:
|
||||||
|
typedef enum {
|
||||||
|
SCOPE_MODULE, SCOPE_OTHER
|
||||||
|
} Type; // Type of a scope, currently module is only interesting
|
||||||
|
private:
|
||||||
// Fastpath:
|
// Fastpath:
|
||||||
VerilatedSyms* m_symsp; ///< Symbol table
|
VerilatedSyms* m_symsp; ///< Symbol table
|
||||||
void** m_callbacksp; ///< Callback table pointer (Fastpath)
|
void** m_callbacksp; ///< Callback table pointer (Fastpath)
|
||||||
@@ -297,16 +304,20 @@ class VerilatedScope {
|
|||||||
// 4 bytes padding (on -m64), for rent.
|
// 4 bytes padding (on -m64), for rent.
|
||||||
VerilatedVarNameMap* m_varsp; ///< Variable map
|
VerilatedVarNameMap* m_varsp; ///< Variable map
|
||||||
const char* m_namep; ///< Scope name (Slowpath)
|
const char* m_namep; ///< Scope name (Slowpath)
|
||||||
|
const char* m_identifierp; ///< Identifier of scope (with escapes removed)
|
||||||
|
Type m_type; ///< Type of the scope
|
||||||
|
|
||||||
public: // But internals only - called from VerilatedModule's
|
public: // But internals only - called from VerilatedModule's
|
||||||
VerilatedScope();
|
VerilatedScope();
|
||||||
~VerilatedScope();
|
~VerilatedScope();
|
||||||
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) VL_MT_UNSAFE;
|
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffix,
|
||||||
|
const char* identifier, const Type type) VL_MT_UNSAFE;
|
||||||
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
||||||
void varInsert(int finalize, const char* namep, void* datap,
|
void varInsert(int finalize, const char* namep, void* datap,
|
||||||
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
|
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
|
||||||
// ACCESSORS
|
// ACCESSORS
|
||||||
const char* name() const { return m_namep; }
|
const char* name() const { return m_namep; }
|
||||||
|
const char* identifier() const { return m_identifierp; }
|
||||||
inline VerilatedSyms* symsp() const { return m_symsp; }
|
inline VerilatedSyms* symsp() const { return m_symsp; }
|
||||||
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||||
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
||||||
@@ -318,10 +329,16 @@ public: // But internals only - called from VerilatedModule's
|
|||||||
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
||||||
// m_callbacksp must be declared, as Max'es are > 0
|
// m_callbacksp must be declared, as Max'es are > 0
|
||||||
return scopep->m_callbacksp[funcnum];
|
return scopep->m_callbacksp[funcnum];
|
||||||
} else {
|
} else { // LCOV_EXCL_LINE
|
||||||
return scopep->exportFindError(funcnum);
|
return scopep->exportFindError(funcnum); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Type type() const { return m_type; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class VerilatedHierarchy {
|
||||||
|
public:
|
||||||
|
void add(VerilatedScope* fromp, VerilatedScope* top);
|
||||||
};
|
};
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
@@ -404,7 +421,8 @@ public:
|
|||||||
static void debug(int level) VL_MT_SAFE;
|
static void debug(int level) VL_MT_SAFE;
|
||||||
#ifdef VL_DEBUG
|
#ifdef VL_DEBUG
|
||||||
/// Return debug level
|
/// Return debug level
|
||||||
/// When multithreaded this may not immediately react to another thread changing the level (no mutex)
|
/// When multithreaded this may not immediately react to another thread
|
||||||
|
/// changing the level (no mutex)
|
||||||
static inline int debug() VL_MT_SAFE { return s_s.s_debug; }
|
static inline int debug() VL_MT_SAFE { return s_s.s_debug; }
|
||||||
#else
|
#else
|
||||||
static inline int debug() VL_PURE { return 0; } ///< Return constant 0 debug level, so C++'s optimizer rips up
|
static inline int debug() VL_PURE { return 0; } ///< Return constant 0 debug level, so C++'s optimizer rips up
|
||||||
@@ -581,7 +599,8 @@ inline QData VL_RDTSC_Q() { vluint64_t val; VL_RDTSC(val); return val; }
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Math
|
/// Math
|
||||||
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus);
|
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp,
|
||||||
|
WDataInP lwp, WDataInP rwp, bool is_modulus);
|
||||||
|
|
||||||
/// File I/O
|
/// File I/O
|
||||||
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi);
|
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi);
|
||||||
@@ -734,7 +753,7 @@ extern double sc_time_stamp();
|
|||||||
# define SP_AUTO_COVER3(what,file,line)
|
# define SP_AUTO_COVER3(what,file,line)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
// Functional macros/routines
|
// Functional macros/routines
|
||||||
// These all take the form
|
// These all take the form
|
||||||
@@ -1292,7 +1311,8 @@ static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP lwp, WDataIn
|
|||||||
static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
||||||
QData carry = 0;
|
QData carry = 0;
|
||||||
for (int i=0; i<words; ++i) {
|
for (int i=0; i<words; ++i) {
|
||||||
carry = carry + static_cast<QData>(lwp[i]) + static_cast<QData>(static_cast<IData>(~rwp[i]));
|
carry = (carry + static_cast<QData>(lwp[i])
|
||||||
|
+ static_cast<QData>(static_cast<IData>(~rwp[i])));
|
||||||
if (i==0) ++carry; // Negation of temp2
|
if (i==0) ++carry; // Negation of temp2
|
||||||
owp[i] = (carry & VL_ULL(0xffffffff));
|
owp[i] = (carry & VL_ULL(0xffffffff));
|
||||||
carry = (carry >> VL_ULL(32)) & VL_ULL(0xffffffff);
|
carry = (carry >> VL_ULL(32)) & VL_ULL(0xffffffff);
|
||||||
@@ -1344,7 +1364,8 @@ static inline QData VL_MULS_QQQ(int, int lbits, int, QData lhs, QData rhs) VL_PU
|
|||||||
return lhs_signed * rhs_signed;
|
return lhs_signed * rhs_signed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline WDataOutP VL_MULS_WWW(int, int lbits, int, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
static inline WDataOutP VL_MULS_WWW(int, int lbits, int,
|
||||||
|
WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
||||||
int words = VL_WORDS_I(lbits);
|
int words = VL_WORDS_I(lbits);
|
||||||
// cppcheck-suppress variableScope
|
// cppcheck-suppress variableScope
|
||||||
WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
|
WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||||
@@ -1774,7 +1795,8 @@ static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) VL_P
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
|
static inline WDataOutP VL_STREAML_WWI(int, int lbits, int,
|
||||||
|
WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
|
||||||
VL_ZERO_W(lbits, owp);
|
VL_ZERO_W(lbits, owp);
|
||||||
// Slice size should never exceed the lhs width
|
// Slice size should never exceed the lhs width
|
||||||
int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
|
int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
|
||||||
@@ -1888,7 +1910,8 @@ static inline void _VL_SHIFTL_INPLACE_W(int obits, WDataOutP iowp, IData rd/*1 o
|
|||||||
// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean;
|
// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean;
|
||||||
// Important: Unlike most other funcs, the shift might well be a computed
|
// Important: Unlike most other funcs, the shift might well be a computed
|
||||||
// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?)
|
// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?)
|
||||||
static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
|
static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int,
|
||||||
|
WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
|
||||||
int word_shift = VL_BITWORD_I(rd);
|
int word_shift = VL_BITWORD_I(rd);
|
||||||
int bit_shift = VL_BITBIT_I(rd);
|
int bit_shift = VL_BITBIT_I(rd);
|
||||||
if (rd >= static_cast<IData>(obits)) { // rd may be huge with MSB set
|
if (rd >= static_cast<IData>(obits)) { // rd may be huge with MSB set
|
||||||
@@ -2142,7 +2165,8 @@ static inline QData VL_SEL_QWII(int, int lbits, int, int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int,
|
static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int,
|
||||||
WDataOutP owp, WDataInP lwp, IData lsb, IData width) VL_MT_SAFE {
|
WDataOutP owp, WDataInP lwp,
|
||||||
|
IData lsb, IData width) VL_MT_SAFE {
|
||||||
int msb = lsb+width-1;
|
int msb = lsb+width-1;
|
||||||
int word_shift = VL_BITWORD_I(lsb);
|
int word_shift = VL_BITWORD_I(lsb);
|
||||||
if (VL_UNLIKELY(msb>lbits)) { // Outside bounds,
|
if (VL_UNLIKELY(msb>lbits)) { // Outside bounds,
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
//
|
//
|
||||||
// DESCRIPTION: Verilator: Include in verilog files to hide verilator defines
|
// DESCRIPTION: Verilator: Include in verilog files to hide verilator defines
|
||||||
|
|
||||||
`ifdef _VERILATED_V_ `else
|
`ifdef _VERILATED_V_ `else
|
||||||
`define _VERILATED_V_ 1
|
`define _VERILATED_V_ 1
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
///**** Product and Version name
|
///**** Product and Version name
|
||||||
|
|
||||||
|
|||||||
@@ -316,7 +316,9 @@ public:
|
|||||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||||
addKeynum++;
|
addKeynum++;
|
||||||
if (!legalKey(key)) {
|
if (!legalKey(key)) {
|
||||||
std::string msg = "%Error: Coverage keys of one character, or letter+digit are illegal: "+key;
|
std::string msg
|
||||||
|
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
|
||||||
|
+key);
|
||||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
#define _VERILATED_DPI_CPP_
|
#define _VERILATED_DPI_CPP_
|
||||||
|
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
@@ -320,7 +320,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default:
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,7 +349,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default:
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,7 +372,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default:
|
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;
|
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
|
// Verilator doesn't support X/Z so can just call Bit version
|
||||||
svPutBitArrElem1(d, value, indx1);
|
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
|
// Verilator doesn't support X/Z so can just call Bit version
|
||||||
svPutBitArrElem2(d, value, indx1, indx2);
|
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
|
// Verilator doesn't support X/Z so can just call Bit version
|
||||||
svPutBitArrElem3(d, value, indx1, indx2, indx3);
|
svPutBitArrElem3(d, value, indx1, indx2, indx3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_DPI_H_
|
#ifndef _VERILATED_DPI_H_
|
||||||
#define _VERILATED_DPI_H_ 1 ///< Header Guard
|
#define _VERILATED_DPI_H_ 1 ///< Header Guard
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ protected:
|
|||||||
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
|
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
|
||||||
VerilatedFstCallback_t changecb,
|
VerilatedFstCallback_t changecb,
|
||||||
void* ut, vluint32_t code)
|
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() {}
|
~VerilatedFstCallInfo() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,7 +174,8 @@ void VerilatedFst::addCallback(
|
|||||||
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||||
m_assertOne.check();
|
m_assertOne.check();
|
||||||
if (VL_UNLIKELY(isOpen())) {
|
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());
|
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||||
}
|
}
|
||||||
VerilatedFstCallInfo* vci = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis, 1);
|
VerilatedFstCallInfo* vci = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis, 1);
|
||||||
|
|||||||
+18
-12
@@ -77,11 +77,11 @@ public:
|
|||||||
fstWriterClose(m_fst);
|
fstWriterClose(m_fst);
|
||||||
m_fst = NULL;
|
m_fst = NULL;
|
||||||
}
|
}
|
||||||
// void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
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_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 char* unitp) { if (unitp) {} }
|
||||||
// void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||||
|
|
||||||
// double timescaleToDouble(const char* unitp);
|
// double timescaleToDouble(const char* unitp);
|
||||||
// std::string doubleToTimescale(double value);
|
// std::string doubleToTimescale(double value);
|
||||||
@@ -157,12 +157,18 @@ public:
|
|||||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], array2Str(newval, bits));
|
fstWriterEmitValueChange(m_fst, m_code2symbol[code], array2Str(newval, bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
void fullBit(vluint32_t code, const vluint32_t newval) { chgBit(code, newval); }
|
void fullBit(vluint32_t code, const vluint32_t newval) {
|
||||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) { chgBus(code, newval, bits); }
|
chgBit(code, newval); }
|
||||||
void fullDouble(vluint32_t code, const double newval) { chgDouble(code, newval); }
|
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||||
void fullFloat(vluint32_t code, const float newval) { chgFloat(code, newval); }
|
chgBus(code, newval, bits); }
|
||||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) { chgQuad(code, newval, bits); }
|
void fullDouble(vluint32_t code, const double newval) {
|
||||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) { chgArray(code, newval, bits); }
|
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 declTriBit (vluint32_t code, const char* name, int arraynum);
|
||||||
void declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
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)); }
|
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||||
/// Set time units (s/ms, defaults to ns)
|
/// Set time units (s/ms, defaults to ns)
|
||||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
/// 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()); }
|
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||||
/// Set time resolution (s/ms, defaults to ns)
|
/// Set time resolution (s/ms, defaults to ns)
|
||||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
/// 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()); }
|
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||||
|
|
||||||
/// Internal class access
|
/// Internal class access
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_HEAVY_H_
|
#ifndef _VERILATED_HEAVY_H_
|
||||||
#define _VERILATED_HEAVY_H_ 1 ///< Header Guard
|
#define _VERILATED_HEAVY_H_ 1 ///< Header Guard
|
||||||
@@ -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,
|
extern void VL_WRITEMEM_N(bool hex, int width, int depth, int array_lsb,
|
||||||
const std::string& filename,
|
const std::string& filename,
|
||||||
const void* memp, IData start, IData end) VL_MT_SAFE;
|
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 IData VL_SSCANF_INX(int lbits, const std::string& ld,
|
||||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE;
|
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 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;
|
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 {
|
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
|
||||||
|
|||||||
+18
-2
@@ -19,12 +19,12 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_IMP_H_
|
#ifndef _VERILATED_IMP_H_
|
||||||
#define _VERILATED_IMP_H_ 1 ///< Header Guard
|
#define _VERILATED_IMP_H_ 1 ///< Header Guard
|
||||||
|
|
||||||
#if !defined(_VERILATED_CPP_) && !defined(_VERILATED_DPI_CPP_)
|
#if !defined(_VERILATED_CPP_) && !defined(_VERILATED_DPI_CPP_) && !defined(_VERILATED_VPI_CPP_)
|
||||||
# error "verilated_imp.h only to be included by verilated*.cpp internals"
|
# error "verilated_imp.h only to be included by verilated*.cpp internals"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -187,6 +187,10 @@ class VerilatedImp {
|
|||||||
|
|
||||||
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
|
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
|
||||||
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); ///< Map of <scope_name, scope pointer>
|
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); ///< Map of <scope_name, scope pointer>
|
||||||
|
|
||||||
|
VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap
|
||||||
|
VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); ///< Map the represents scope hierarchy
|
||||||
|
|
||||||
// Slow - somewhat static:
|
// Slow - somewhat static:
|
||||||
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
|
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
|
||||||
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of <export_func_proto, func number>
|
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of <export_func_proto, func number>
|
||||||
@@ -321,6 +325,18 @@ public: // But only for verilated*.cpp
|
|||||||
return &s_s.m_nameMap;
|
return &s_s.m_nameMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public: // But only for verilated*.cpp
|
||||||
|
// METHODS - hierarchy
|
||||||
|
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
|
||||||
|
// Slow ok - called at construction for VPI accessible elements
|
||||||
|
VerilatedLockGuard lock(s_s.m_hierMapMutex);
|
||||||
|
s_s.m_hierMap[fromp].push_back(top);
|
||||||
|
}
|
||||||
|
static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT {
|
||||||
|
// Thread save only assuming this is called only after model construction completed
|
||||||
|
return &s_s.m_hierMap;
|
||||||
|
}
|
||||||
|
|
||||||
public: // But only for verilated*.cpp
|
public: // But only for verilated*.cpp
|
||||||
// METHODS - export names
|
// METHODS - export names
|
||||||
|
|
||||||
|
|||||||
@@ -61,10 +61,12 @@ bool VerilatedDeserialize::readDiffers(const void* __restrict datap, size_t size
|
|||||||
return (miss!=0);
|
return (miss!=0);
|
||||||
}
|
}
|
||||||
|
|
||||||
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
|
||||||
|
size_t size) VL_MT_UNSAFE_ONE {
|
||||||
if (VL_UNLIKELY(readDiffers(datap, size))) {
|
if (VL_UNLIKELY(readDiffers(datap, size))) {
|
||||||
std::string fn = filename();
|
std::string fn = filename();
|
||||||
std::string msg = std::string("Can't deserialize save-restore file as was made from different model");
|
std::string msg = "Can't deserialize save-restore file as was made from different model:"
|
||||||
|
+filename();
|
||||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
@@ -85,7 +87,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
|||||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||||
std::string fn = filename();
|
std::string fn = filename();
|
||||||
std::string msg = std::string("Can't deserialize; file has wrong header signature");
|
std::string msg = std::string("Can't deserialize; file has wrong header signature: ")
|
||||||
|
+filename();
|
||||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
@@ -102,7 +105,8 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
|||||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||||
std::string fn = filename();
|
std::string fn = filename();
|
||||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature");
|
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
|
||||||
|
+filename();
|
||||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_SC_H_
|
#ifndef _VERILATED_SC_H_
|
||||||
#define _VERILATED_SC_H_ 1 ///< Header Guard
|
#define _VERILATED_SC_H_ 1 ///< Header Guard
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_SYM_PROPS_H_
|
#ifndef _VERILATED_SYM_PROPS_H_
|
||||||
#define _VERILATED_SYM_PROPS_H_ 1 ///< Header Guard
|
#define _VERILATED_SYM_PROPS_H_ 1 ///< Header Guard
|
||||||
@@ -142,22 +142,28 @@ public:
|
|||||||
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
|
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
|
||||||
// DPI accessors
|
// DPI accessors
|
||||||
int left(int dim) const {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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)
|
/// Total size in bytes (note DPI limited to 4GB)
|
||||||
size_t totalSize() const;
|
size_t totalSize() const;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATED_SYMS_H_
|
#ifndef _VERILATED_SYMS_H_
|
||||||
#define _VERILATED_SYMS_H_ 1 ///< Header Guard
|
#define _VERILATED_SYMS_H_ 1 ///< Header Guard
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "verilated_sym_props.h"
|
#include "verilated_sym_props.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
/// Types
|
/// Types
|
||||||
@@ -63,4 +64,13 @@ public:
|
|||||||
~VerilatedVarNameMap() {}
|
~VerilatedVarNameMap() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::vector<const VerilatedScope*> VerilatedScopeVector;
|
||||||
|
|
||||||
|
class VerilatedHierarchyMap
|
||||||
|
: public std::map<const VerilatedScope*, VerilatedScopeVector> {
|
||||||
|
public:
|
||||||
|
VerilatedHierarchyMap() {}
|
||||||
|
~VerilatedHierarchyMap() {}
|
||||||
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
@@ -119,8 +119,11 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling)
|
|||||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||||
unsigned cpus = std::thread::hardware_concurrency();
|
unsigned cpus = std::thread::hardware_concurrency();
|
||||||
if (cpus < nThreads+1) {
|
if (cpus < nThreads+1) {
|
||||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
static int warnedOnce = 0;
|
||||||
" --threads %d; may run slow.\n", cpus, nThreads+1);
|
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
|
// Create'em
|
||||||
for (int i=0; i<nThreads; ++i) {
|
for (int i=0; i<nThreads; ++i) {
|
||||||
@@ -218,8 +221,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
|
|||||||
eit->m_cpu,
|
eit->m_cpu,
|
||||||
thread_id);
|
thread_id);
|
||||||
break;
|
break;
|
||||||
default: assert(false);
|
default: assert(false); break; // LCOV_EXCL_LINE
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ public:
|
|||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
static vluint64_t yields() { return s_yields; }
|
static vluint64_t yields() { return s_yields; }
|
||||||
|
static void yieldThread() {
|
||||||
|
++s_yields; // Statistics
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
// Block until notify() has occurred, then return.
|
// Block until notify() has occurred, then return.
|
||||||
// If notify() has already occurred, return immediately.
|
// If notify() has already occurred, return immediately.
|
||||||
@@ -63,11 +67,10 @@ public:
|
|||||||
unsigned ct = 0;
|
unsigned ct = 0;
|
||||||
while (VL_UNLIKELY(!notified())) {
|
while (VL_UNLIKELY(!notified())) {
|
||||||
VL_CPU_RELAX();
|
VL_CPU_RELAX();
|
||||||
ct++;
|
++ct;
|
||||||
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
||||||
ct = 0;
|
ct = 0;
|
||||||
++s_yields; // Statistics
|
yieldThread();
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,8 +152,14 @@ public:
|
|||||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||||
}
|
}
|
||||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||||
|
unsigned ct = 0;
|
||||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||||
VL_CPU_RELAX();
|
VL_CPU_RELAX();
|
||||||
|
++ct;
|
||||||
|
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
||||||
|
ct = 0;
|
||||||
|
VlNotification::yieldThread();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -177,6 +186,7 @@ public:
|
|||||||
m_mtaskId = 0;
|
m_mtaskId = 0;
|
||||||
m_predictTime = 0;
|
m_predictTime = 0;
|
||||||
m_startTime = 0;
|
m_startTime = 0;
|
||||||
|
m_endTime = 0;
|
||||||
m_cpu = getcpu();
|
m_cpu = getcpu();
|
||||||
}
|
}
|
||||||
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
|
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
|
||||||
|
|||||||
+16
-10
@@ -68,14 +68,17 @@ public:
|
|||||||
}
|
}
|
||||||
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||||
VerilatedLockGuard lock(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); }
|
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||||
}
|
}
|
||||||
static void flush_all() VL_EXCLUDES(singleton().s_vcdMutex) VL_MT_UNSAFE_ONE {
|
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
|
// Thread safety: Although this function is protected by a mutex so
|
||||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
// perhaps in the future we can allow tracing in separate threads,
|
||||||
|
// vcdp->flush() assumes call from single thread
|
||||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
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;
|
VerilatedVcd* vcdp = *it;
|
||||||
vcdp->flush();
|
vcdp->flush();
|
||||||
}
|
}
|
||||||
@@ -102,7 +105,7 @@ protected:
|
|||||||
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||||
VerilatedVcdCallback_t changecb,
|
VerilatedVcdCallback_t changecb,
|
||||||
void* ut, vluint32_t code)
|
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() {}
|
~VerilatedVcdCallInfo() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -322,7 +325,7 @@ void VerilatedVcd::printQuad(vluint64_t n) {
|
|||||||
|
|
||||||
void VerilatedVcd::printTime(vluint64_t timeui) {
|
void VerilatedVcd::printTime(vluint64_t timeui) {
|
||||||
// VCD file format specification does not allow non-integers for timestamps
|
// VCD file format specification does not allow non-integers for timestamps
|
||||||
// Dinotrace doesn't mind, but Cadence vvision seems to choke
|
// Dinotrace doesn't mind, but Cadence Vision seems to choke
|
||||||
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
|
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
|
||||||
timeui = m_timeLastDump;
|
timeui = m_timeLastDump;
|
||||||
static VL_THREAD_LOCAL bool backTime = false;
|
static VL_THREAD_LOCAL bool backTime = false;
|
||||||
@@ -523,7 +526,8 @@ void VerilatedVcd::module(const std::string& name) {
|
|||||||
|
|
||||||
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
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 bits = ((msb>lsb)?(msb-lsb):(lsb-msb))+1;
|
||||||
int codesNeeded = 1+int(bits/32);
|
int codesNeeded = 1+int(bits/32);
|
||||||
@@ -644,10 +648,12 @@ void VerilatedVcd::addCallback(
|
|||||||
{
|
{
|
||||||
m_assertOne.check();
|
m_assertOne.check();
|
||||||
if (VL_UNLIKELY(isOpen())) {
|
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__
|
||||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
+" 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);
|
m_callbacks.push_back(vci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-11
@@ -162,7 +162,7 @@ public:
|
|||||||
|
|
||||||
// ACCESSORS
|
// ACCESSORS
|
||||||
/// Set size in megabytes after which new file should be created
|
/// 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?
|
/// Is file open?
|
||||||
bool isOpen() const { return m_isOpen; }
|
bool isOpen() const { return m_isOpen; }
|
||||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||||
@@ -268,7 +268,8 @@ public:
|
|||||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||||
bufferCheck();
|
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]))) = newval;
|
||||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
|
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
|
||||||
*m_writep++='b';
|
*m_writep++='b';
|
||||||
@@ -279,7 +280,8 @@ public:
|
|||||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||||
bufferCheck();
|
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) {
|
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||||
m_sigs_oldvalp[code+word*2] = newvalp[word];
|
m_sigs_oldvalp[code+word*2] = newvalp[word];
|
||||||
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
|
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
|
||||||
@@ -319,7 +321,8 @@ public:
|
|||||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||||
if (VL_UNLIKELY(diff)) {
|
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?
|
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||||
fullBit(code, newval);
|
fullBit(code, newval);
|
||||||
}
|
}
|
||||||
@@ -349,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)
|
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||||
if (VL_UNLIKELY(diff)) {
|
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?
|
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||||
fullTriBit(code, newval, newtri);
|
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)
|
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||||
if (VL_UNLIKELY(diff)) {
|
if (VL_UNLIKELY(diff)) {
|
||||||
@@ -368,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)
|
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
|
||||||
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
|
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
|
||||||
if (VL_UNLIKELY(diff)) {
|
if (VL_UNLIKELY(diff)) {
|
||||||
@@ -377,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) {
|
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||||
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
||||||
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
||||||
@@ -432,7 +440,7 @@ public:
|
|||||||
/// "cat" to be used to combine the header plus any number of data files.
|
/// "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); }
|
void openNext(bool incFilename=true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); }
|
||||||
/// Set size in megabytes after which new file should be created
|
/// 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
|
/// Close dump
|
||||||
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
||||||
/// Flush dump
|
/// Flush dump
|
||||||
@@ -454,7 +462,7 @@ public:
|
|||||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||||
|
|
||||||
/// Internal class access
|
/// Internal class access
|
||||||
inline VerilatedVcd* spTrace() { return &m_sptrace; };
|
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // guard
|
#endif // guard
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ public:
|
|||||||
sc_get_curr_simcontext()->add_trace_file(this);
|
sc_get_curr_simcontext()->add_trace_file(this);
|
||||||
# if (SYSTEMC_VERSION>=20060505)
|
# if (SYSTEMC_VERSION>=20060505)
|
||||||
// We want to avoid a depreciated warning, but still be back compatible.
|
// 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);
|
sc_time t1sec(1,SC_SEC);
|
||||||
if (t1sec.to_default_time_units()!=0) {
|
if (t1sec.to_default_time_units()!=0) {
|
||||||
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
|
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
|
||||||
@@ -69,7 +70,8 @@ public:
|
|||||||
# if (SYSTEMC_VERSION>20011000)
|
# if (SYSTEMC_VERSION>20011000)
|
||||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||||
# else
|
# 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()); }
|
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|||||||
+149
-51
@@ -24,17 +24,20 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
|
#define _VERILATED_VPI_CPP_
|
||||||
|
|
||||||
#if VM_SC
|
#if VM_SC
|
||||||
# include "verilated_sc.h"
|
# include "verilated_sc.h"
|
||||||
#endif
|
#endif
|
||||||
#include "verilated.h"
|
#include "verilated.h"
|
||||||
#include "verilated_vpi.h"
|
#include "verilated_vpi.h"
|
||||||
|
#include "verilated_imp.h"
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Internal constants
|
// Internal constants
|
||||||
@@ -74,7 +77,7 @@ public:
|
|||||||
// We reserve word zero for the next pointer, as that's safer in case a
|
// We reserve word zero for the next pointer, as that's safer in case a
|
||||||
// dangling reference to the original remains around.
|
// dangling reference to the original remains around.
|
||||||
static const size_t chunk = 96;
|
static const size_t chunk = 96;
|
||||||
if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
if (VL_UNCOVERABLE(size>chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||||
if (VL_LIKELY(t_freeHead)) {
|
if (VL_LIKELY(t_freeHead)) {
|
||||||
vluint8_t* newp = t_freeHead;
|
vluint8_t* newp = t_freeHead;
|
||||||
t_freeHead = *((vluint8_t**)newp);
|
t_freeHead = *((vluint8_t**)newp);
|
||||||
@@ -161,6 +164,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class VerilatedVpioScope : public VerilatedVpio {
|
class VerilatedVpioScope : public VerilatedVpio {
|
||||||
|
protected:
|
||||||
const VerilatedScope* m_scopep;
|
const VerilatedScope* m_scopep;
|
||||||
public:
|
public:
|
||||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||||
@@ -210,7 +214,6 @@ public:
|
|||||||
vluint32_t entSize() const { return m_entSize; }
|
vluint32_t entSize() const { return m_entSize; }
|
||||||
vluint32_t index() { return m_index; }
|
vluint32_t index() { return m_index; }
|
||||||
virtual vluint32_t type() const {
|
virtual vluint32_t type() const {
|
||||||
if (varp()->vldir() != vpiNoDirection) return vpiPort;
|
|
||||||
return (varp()->dims()>1) ? vpiMemory : vpiReg; // but might be wire, logic
|
return (varp()->dims()>1) ? vpiMemory : vpiReg; // but might be wire, logic
|
||||||
}
|
}
|
||||||
virtual vluint32_t size() const { return get_range().elements(); }
|
virtual vluint32_t size() const { return get_range().elements(); }
|
||||||
@@ -308,6 +311,43 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VerilatedVpioModule : public VerilatedVpioScope {
|
||||||
|
const char* m_name;
|
||||||
|
const char* m_fullname;
|
||||||
|
public:
|
||||||
|
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
||||||
|
: VerilatedVpioScope(modulep) {
|
||||||
|
m_fullname = m_scopep->name();
|
||||||
|
if (strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
||||||
|
m_name = m_scopep->identifier();
|
||||||
|
}
|
||||||
|
static inline VerilatedVpioModule* castp(vpiHandle h) {
|
||||||
|
return dynamic_cast<VerilatedVpioModule*>((VerilatedVpio*)h); }
|
||||||
|
virtual vluint32_t type() const { return vpiModule; }
|
||||||
|
virtual const char* name() const { return m_name; }
|
||||||
|
virtual const char* fullname() const { return m_fullname; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class VerilatedVpioModuleIter : public VerilatedVpio {
|
||||||
|
const std::vector<const VerilatedScope*> *m_vec;
|
||||||
|
std::vector<const VerilatedScope*>::const_iterator m_it;
|
||||||
|
public:
|
||||||
|
explicit VerilatedVpioModuleIter(const std::vector<const VerilatedScope*>& vec) : m_vec(&vec) {
|
||||||
|
m_it = m_vec->begin();
|
||||||
|
}
|
||||||
|
virtual ~VerilatedVpioModuleIter() {}
|
||||||
|
static inline VerilatedVpioModuleIter* castp(vpiHandle h) {
|
||||||
|
return dynamic_cast<VerilatedVpioModuleIter*>((VerilatedVpio*) h); }
|
||||||
|
virtual vluint32_t type() const { return vpiIterator; }
|
||||||
|
virtual vpiHandle dovpi_scan() {
|
||||||
|
if (m_it == m_vec->end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const VerilatedScope* modp = *m_it++;
|
||||||
|
return (new VerilatedVpioModule(modp))->castVpiHandle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
|
|
||||||
struct VerilatedVpiTimedCbsCmp {
|
struct VerilatedVpiTimedCbsCmp {
|
||||||
@@ -348,7 +388,7 @@ public:
|
|||||||
varop->createPrevDatap();
|
varop->createPrevDatap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) {
|
if (VL_UNCOVERABLE(vop->reason() >= CB_ENUM_MAX_VALUE)) {
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
||||||
}
|
}
|
||||||
s_s.m_cbObjLists[vop->reason()].push_back(vop);
|
s_s.m_cbObjLists[vop->reason()].push_back(vop);
|
||||||
@@ -390,8 +430,9 @@ public:
|
|||||||
}
|
}
|
||||||
return ~VL_ULL(0); // maxquad
|
return ~VL_ULL(0); // maxquad
|
||||||
}
|
}
|
||||||
static void callCbs(vluint32_t reason) {
|
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||||
VpioCbList& cbObjList = s_s.m_cbObjLists[reason];
|
VpioCbList& cbObjList = s_s.m_cbObjLists[reason];
|
||||||
|
bool called = false;
|
||||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) {
|
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) {
|
||||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
||||||
it = cbObjList.erase(it);
|
it = cbObjList.erase(it);
|
||||||
@@ -400,7 +441,9 @@ public:
|
|||||||
VerilatedVpioCb* vop = *it++;
|
VerilatedVpioCb* vop = *it++;
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback %d %p\n", reason, vop););
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback %d %p\n", reason, vop););
|
||||||
(vop->cb_rtnp()) (vop->cb_datap());
|
(vop->cb_rtnp()) (vop->cb_datap());
|
||||||
|
called = true;
|
||||||
}
|
}
|
||||||
|
return called;
|
||||||
}
|
}
|
||||||
static void callValueCbs() VL_MT_UNSAFE_ONE {
|
static void callValueCbs() VL_MT_UNSAFE_ONE {
|
||||||
assertOneCheck();
|
assertOneCheck();
|
||||||
@@ -417,8 +460,9 @@ public:
|
|||||||
void* newDatap = varop->varDatap();
|
void* newDatap = varop->varDatap();
|
||||||
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||||
varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap),
|
varop->fullname(),
|
||||||
newDatap, prevDatap););
|
*((CData*)newDatap), *((CData*)prevDatap),
|
||||||
|
newDatap, prevDatap););
|
||||||
if (memcmp(prevDatap, newDatap, varop->entSize())) {
|
if (memcmp(prevDatap, newDatap, varop->entSize())) {
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %p %s v[0]=%d\n",
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %p %s v[0]=%d\n",
|
||||||
vop, varop->fullname(), *((CData*)newDatap)););
|
vop, varop->fullname(), *((CData*)newDatap)););
|
||||||
@@ -454,8 +498,9 @@ class VerilatedVpiError {
|
|||||||
// Stop on vpi error/unsupported
|
// Stop on vpi error/unsupported
|
||||||
vpi_unsupported();
|
vpi_unsupported();
|
||||||
}
|
}
|
||||||
// We need to run above code first because in the case that the callback executes further vpi
|
// We need to run above code first because in the case that the
|
||||||
// functions we will loose the error as it will be overwritten.
|
// callback executes further vpi functions we will loose the error
|
||||||
|
// as it will be overwritten.
|
||||||
VerilatedVpiImp::callCbs(cbPLIError);
|
VerilatedVpiImp::callCbs(cbPLIError);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@@ -519,6 +564,10 @@ void VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE {
|
|||||||
VerilatedVpiImp::callValueCbs();
|
VerilatedVpiImp::callValueCbs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VerilatedVpi::callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||||
|
return VerilatedVpiImp::callCbs(reason);
|
||||||
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// VerilatedVpiImp implementation
|
// VerilatedVpiImp implementation
|
||||||
|
|
||||||
@@ -937,16 +986,16 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
|||||||
VerilatedVpiImp::cbTimedAdd(vop);
|
VerilatedVpiImp::cbTimedAdd(vop);
|
||||||
return vop->castVpiHandle();
|
return vop->castVpiHandle();
|
||||||
}
|
}
|
||||||
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
|
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
|
||||||
case cbReadOnlySynch: // FALLTHRU // Supported via vlt_main.cpp
|
case cbReadOnlySynch: // FALLTHRU // Supported via vlt_main.cpp
|
||||||
case cbNextSimTime: // FALLTHRU // Supported via vlt_main.cpp
|
case cbNextSimTime: // FALLTHRU // Supported via vlt_main.cpp
|
||||||
case cbStartOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
|
case cbStartOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
|
||||||
case cbEndOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
|
case cbEndOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
|
||||||
case cbValueChange: // FALLTHRU // Supported via vlt_main.cpp
|
case cbValueChange: // FALLTHRU // Supported via vlt_main.cpp
|
||||||
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
|
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
|
||||||
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||||
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||||
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
|
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
|
||||||
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0);
|
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0);
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p\n", cb_data_p->reason, vop););
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p\n", cb_data_p->reason, vop););
|
||||||
VerilatedVpiImp::cbReasonAdd(vop);
|
VerilatedVpiImp::cbReasonAdd(vop);
|
||||||
@@ -990,9 +1039,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
|||||||
_VL_VPI_ERROR_RESET();
|
_VL_VPI_ERROR_RESET();
|
||||||
if (VL_UNLIKELY(!namep)) return NULL;
|
if (VL_UNLIKELY(!namep)) return NULL;
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
|
||||||
VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope);
|
const VerilatedVar* varp = NULL;
|
||||||
const VerilatedVar* varp;
|
|
||||||
const VerilatedScope* scopep;
|
const VerilatedScope* scopep;
|
||||||
|
VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope);
|
||||||
std::string scopeAndName = namep;
|
std::string scopeAndName = namep;
|
||||||
if (voScopep) {
|
if (voScopep) {
|
||||||
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
|
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
|
||||||
@@ -1002,7 +1051,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
|||||||
// This doesn't yet follow the hierarchy in the proper way
|
// This doesn't yet follow the hierarchy in the proper way
|
||||||
scopep = Verilated::scopeFind(namep);
|
scopep = Verilated::scopeFind(namep);
|
||||||
if (scopep) { // Whole thing found as a scope
|
if (scopep) { // Whole thing found as a scope
|
||||||
return (new VerilatedVpioScope(scopep))->castVpiHandle();
|
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
|
||||||
|
return (new VerilatedVpioModule(scopep))->castVpiHandle();
|
||||||
|
} else {
|
||||||
|
return (new VerilatedVpioScope(scopep))->castVpiHandle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const char* baseNamep = scopeAndName.c_str();
|
const char* baseNamep = scopeAndName.c_str();
|
||||||
std::string scopename;
|
std::string scopename;
|
||||||
@@ -1011,9 +1064,19 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
|||||||
baseNamep = dotp+1;
|
baseNamep = dotp+1;
|
||||||
scopename = std::string(namep, dotp-namep);
|
scopename = std::string(namep, dotp-namep);
|
||||||
}
|
}
|
||||||
scopep = Verilated::scopeFind(scopename.c_str());
|
|
||||||
if (!scopep) return NULL;
|
if (scopename.find(".") == std::string::npos) {
|
||||||
varp = scopep->varFind(baseNamep);
|
// This is a toplevel, hence search in our TOP ports first.
|
||||||
|
scopep = Verilated::scopeFind("TOP");
|
||||||
|
if (scopep) {
|
||||||
|
varp = scopep->varFind(baseNamep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!varp) {
|
||||||
|
scopep = Verilated::scopeFind(scopename.c_str());
|
||||||
|
if (!scopep) return NULL;
|
||||||
|
varp = scopep->varFind(baseNamep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!varp) return NULL;
|
if (!varp) return NULL;
|
||||||
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
|
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
|
||||||
@@ -1099,7 +1162,8 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
|||||||
if (VL_UNLIKELY(!vop)) return 0;
|
if (VL_UNLIKELY(!vop)) return 0;
|
||||||
if (vop->varp()->dims() < 2) return 0;
|
if (vop->varp()->dims() < 2) return 0;
|
||||||
if (vop->varp()->dims() > 2) {
|
if (vop->varp()->dims() > 2) {
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)",
|
_VL_VPI_WARNING(__FILE__, __LINE__,
|
||||||
|
"%s: %s, object %s has unsupported number of indices (%d)",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type),
|
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type),
|
||||||
vop->fullname() , vop->varp()->dims());
|
vop->fullname() , vop->varp()->dims());
|
||||||
}
|
}
|
||||||
@@ -1111,7 +1175,8 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
|||||||
if (vop->varp()->dims() < 2) return 0;
|
if (vop->varp()->dims() < 2) return 0;
|
||||||
// Unsupported is multidim list
|
// Unsupported is multidim list
|
||||||
if (vop->varp()->dims() > 2) {
|
if (vop->varp()->dims() > 2) {
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)",
|
_VL_VPI_WARNING(__FILE__, __LINE__,
|
||||||
|
"%s: %s, object %s has unsupported number of indices (%d)",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type),
|
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type),
|
||||||
vop->fullname() , vop->varp()->dims());
|
vop->fullname() , vop->varp()->dims());
|
||||||
}
|
}
|
||||||
@@ -1123,6 +1188,14 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
|||||||
return ((new VerilatedVpioVarIter(vop->scopep()))
|
return ((new VerilatedVpioVarIter(vop->scopep()))
|
||||||
->castVpiHandle());
|
->castVpiHandle());
|
||||||
}
|
}
|
||||||
|
case vpiModule: {
|
||||||
|
VerilatedVpioModule* vop = VerilatedVpioModule::castp(object);
|
||||||
|
const VerilatedHierarchyMap* map = VerilatedImp::hierarchyMap();
|
||||||
|
const VerilatedScope *mod = vop ? vop->scopep() : NULL;
|
||||||
|
VerilatedHierarchyMap::const_iterator it = map->find((VerilatedScope*) mod);
|
||||||
|
if (it == map->end()) return 0;
|
||||||
|
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiObjType(type));
|
VL_FUNC, VerilatedVpiError::strFromVpiObjType(type));
|
||||||
@@ -1150,7 +1223,7 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
|||||||
return VL_TIME_PRECISION;
|
return VL_TIME_PRECISION;
|
||||||
}
|
}
|
||||||
case vpiType: {
|
case vpiType: {
|
||||||
VerilatedVpio* vop = VerilatedVpioVar::castp(object);
|
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||||
if (VL_UNLIKELY(!vop)) return 0;
|
if (VL_UNLIKELY(!vop)) return 0;
|
||||||
return vop->type();
|
return vop->type();
|
||||||
}
|
}
|
||||||
@@ -1199,6 +1272,9 @@ PLI_BYTE8 *vpi_get_str(PLI_INT32 property, vpiHandle object) {
|
|||||||
case vpiDefName: {
|
case vpiDefName: {
|
||||||
return (PLI_BYTE8*)vop->defname();
|
return (PLI_BYTE8*)vop->defname();
|
||||||
}
|
}
|
||||||
|
case vpiType: {
|
||||||
|
return (PLI_BYTE8*) VerilatedVpiError::strFromVpiObjType(vop->type());
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiProp(property));
|
VL_FUNC, VerilatedVpiError::strFromVpiProp(property));
|
||||||
@@ -1251,8 +1327,9 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
return;
|
return;
|
||||||
case VLVT_WDATA: {
|
case VLVT_WDATA: {
|
||||||
int words = VL_WORDS_I(vop->varp()->packed().elements());
|
int words = VL_WORDS_I(vop->varp()->packed().elements());
|
||||||
if (VL_UNLIKELY(words >= VL_MULS_MAX_WORDS)) {
|
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||||
|
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||||
}
|
}
|
||||||
WDataInP datap = (reinterpret_cast<IData*>(vop->varDatap()));
|
WDataInP datap = (reinterpret_cast<IData*>(vop->varDatap()));
|
||||||
for (int i=0; i<words; ++i) {
|
for (int i=0; i<words; ++i) {
|
||||||
@@ -1271,7 +1348,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1354,7 +1432,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
default:
|
default:
|
||||||
strcpy(outStr, "0");
|
strcpy(outStr, "0");
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (value_p->format == vpiDecStrVal) {
|
} else if (value_p->format == vpiDecStrVal) {
|
||||||
@@ -1363,19 +1442,24 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
// outStrSz does not include NULL termination so add one
|
// outStrSz does not include NULL termination so add one
|
||||||
case VLVT_UINT8:
|
case VLVT_UINT8:
|
||||||
VL_SNPRINTF(outStr, outStrSz+1, "%hhu",
|
VL_SNPRINTF(outStr, outStrSz+1, "%hhu",
|
||||||
static_cast<unsigned char>(*(reinterpret_cast<CData*>(vop->varDatap())))); return;
|
static_cast<unsigned char>(*(reinterpret_cast<CData*>(vop->varDatap()))));
|
||||||
|
return;
|
||||||
case VLVT_UINT16:
|
case VLVT_UINT16:
|
||||||
VL_SNPRINTF(outStr, outStrSz+1, "%hu",
|
VL_SNPRINTF(outStr, outStrSz+1, "%hu",
|
||||||
static_cast<unsigned short>(*(reinterpret_cast<SData*>(vop->varDatap())))); return;
|
static_cast<unsigned short>(*(reinterpret_cast<SData*>(vop->varDatap()))));
|
||||||
|
return;
|
||||||
case VLVT_UINT32:
|
case VLVT_UINT32:
|
||||||
VL_SNPRINTF(outStr, outStrSz+1, "%u",
|
VL_SNPRINTF(outStr, outStrSz+1, "%u",
|
||||||
static_cast<unsigned int>(*(reinterpret_cast<IData*>(vop->varDatap())))); return;
|
static_cast<unsigned int>(*(reinterpret_cast<IData*>(vop->varDatap()))));
|
||||||
|
return;
|
||||||
case VLVT_UINT64:
|
case VLVT_UINT64:
|
||||||
VL_SNPRINTF(outStr, outStrSz+1, "%llu",
|
VL_SNPRINTF(outStr, outStrSz+1, "%llu",
|
||||||
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(vop->varDatap())))); return;
|
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(vop->varDatap()))));
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
strcpy(outStr, "-1");
|
strcpy(outStr, "-1");
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
|
_VL_VPI_ERROR(__FILE__, __LINE__,
|
||||||
|
"%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1416,7 +1500,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (value_p->format == vpiStringVal) {
|
} else if (value_p->format == vpiStringVal) {
|
||||||
@@ -1448,7 +1533,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (value_p->format == vpiIntVal) {
|
} else if (value_p->format == vpiIntVal) {
|
||||||
@@ -1467,7 +1553,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
|||||||
default:
|
default:
|
||||||
value_p->value.integer = 0;
|
value_p->value.integer = 0;
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (value_p->format == vpiSuppressVal) {
|
} else if (value_p->format == vpiSuppressVal) {
|
||||||
@@ -1505,7 +1592,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
|
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
|
||||||
vop->varp()->datap(), vop->varDatap()););
|
vop->varp()->datap(), vop->varDatap()););
|
||||||
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value to signal marked read-only,"
|
_VL_VPI_WARNING(__FILE__, __LINE__,
|
||||||
|
"Ignoring vpi_put_value to signal marked read-only,"
|
||||||
" use public_flat_rw instead: ",
|
" use public_flat_rw instead: ",
|
||||||
vop->fullname());
|
vop->fullname());
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1544,7 +1632,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1572,7 +1661,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else if (value_p->format == vpiOctStrVal) {
|
} else if (value_p->format == vpiOctStrVal) {
|
||||||
@@ -1600,9 +1690,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
if (digit >= '0' && digit <= '7') {
|
if (digit >= '0' && digit <= '7') {
|
||||||
val.half = digit-'0';
|
val.half = digit-'0';
|
||||||
} else {
|
} else {
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Non octal character '%c' in '%s' as value %s for %s",
|
_VL_VPI_WARNING(__FILE__, __LINE__,
|
||||||
|
"%s: Non octal character '%c' in '%s' as value %s for %s",
|
||||||
VL_FUNC, digit, value_p->value.str,
|
VL_FUNC, digit, value_p->value.str,
|
||||||
VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
val.half = 0;
|
val.half = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1634,7 +1726,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else if (value_p->format == vpiDecStrVal) {
|
} else if (value_p->format == vpiDecStrVal) {
|
||||||
@@ -1642,12 +1735,15 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
int success = sscanf(value_p->value.str, "%30llu%15s", &val, remainder);
|
int success = sscanf(value_p->value.str, "%30llu%15s", &val, remainder);
|
||||||
if (success < 1) {
|
if (success < 1) {
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
|
_VL_VPI_ERROR(__FILE__, __LINE__,
|
||||||
VL_FUNC, value_p->value.str, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
"%s: Parsing failed for '%s' as value %s for %s",
|
||||||
|
VL_FUNC, value_p->value.str,
|
||||||
|
VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (success > 1) {
|
if (success > 1) {
|
||||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s",
|
_VL_VPI_WARNING(__FILE__, __LINE__,
|
||||||
|
"%s: Trailing garbage '%s' in '%s' as value %s for %s",
|
||||||
VL_FUNC, remainder, value_p->value.str,
|
VL_FUNC, remainder, value_p->value.str,
|
||||||
VerilatedVpiError::strFromVpiVal(value_p->format),
|
VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
vop->fullname());
|
vop->fullname());
|
||||||
@@ -1663,8 +1759,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
|||||||
(reinterpret_cast<IData*>(vop->varDatap()))[1] &= vop->mask(); break;
|
(reinterpret_cast<IData*>(vop->varDatap()))[1] &= vop->mask(); break;
|
||||||
case VLVT_WDATA:
|
case VLVT_WDATA:
|
||||||
default:
|
default:
|
||||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
|
_VL_VPI_ERROR(__FILE__, __LINE__,
|
||||||
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
"%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
|
||||||
|
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
|
||||||
|
vop->fullname());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
#ifndef _VERILATED_VPI_H_
|
#ifndef _VERILATED_VPI_H_
|
||||||
#define _VERILATED_VPI_H_ 1 ///< Header Guard
|
#define _VERILATED_VPI_H_ 1 ///< Header Guard
|
||||||
|
|
||||||
@@ -45,6 +45,9 @@ public:
|
|||||||
/// Call value based callbacks
|
/// Call value based callbacks
|
||||||
/// Users should call this from their main loops
|
/// Users should call this from their main loops
|
||||||
static void callValueCbs() VL_MT_UNSAFE_ONE;
|
static void callValueCbs() VL_MT_UNSAFE_ONE;
|
||||||
|
/// Call callbacks of arbitrary types
|
||||||
|
/// Users can call this from their application code
|
||||||
|
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE;
|
||||||
/// Self test, for internal use only
|
/// Self test, for internal use only
|
||||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-6
@@ -25,7 +25,7 @@
|
|||||||
/// Code available from: http://www.veripool.org/verilator
|
/// Code available from: http://www.veripool.org/verilator
|
||||||
///
|
///
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
||||||
#ifndef _VERILATEDOS_H_
|
#ifndef _VERILATEDOS_H_
|
||||||
#define _VERILATEDOS_H_ 1 ///< Header Guard
|
#define _VERILATEDOS_H_ 1 ///< Header Guard
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
# define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment)))
|
# define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment)))
|
||||||
# define VL_ATTR_ALWINLINE __attribute__ ((always_inline))
|
# 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))
|
# define VL_ATTR_NORETURN __attribute__ ((noreturn))
|
||||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
|
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
|
||||||
# define VL_ATTR_PURE __attribute__ ((pure))
|
# define VL_ATTR_PURE __attribute__ ((pure))
|
||||||
@@ -70,6 +72,12 @@
|
|||||||
#ifndef VL_ATTR_ALWINLINE
|
#ifndef VL_ATTR_ALWINLINE
|
||||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||||
#endif
|
#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
|
#ifndef VL_ATTR_NORETURN
|
||||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||||
#endif
|
#endif
|
||||||
@@ -102,6 +110,7 @@
|
|||||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
# 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_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||||
#endif
|
#endif
|
||||||
|
#define VL_UNCOVERABLE(x) VL_UNLIKELY(x) ///< Boolean expression never hit by users (no coverage)
|
||||||
#ifndef VL_UNREACHABLE
|
#ifndef VL_UNREACHABLE
|
||||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||||
#endif
|
#endif
|
||||||
@@ -198,6 +207,11 @@
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
// Basic integer types
|
// 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__)
|
#if defined(__CYGWIN__)
|
||||||
|
|
||||||
# include <stdint.h>
|
# include <stdint.h>
|
||||||
@@ -270,10 +284,6 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# define VL_PRI64 "I64"
|
# define VL_PRI64 "I64"
|
||||||
#else // use standard C99 format specifiers
|
#else // use standard C99 format specifiers
|
||||||
# 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(__WORDSIZE) && (__WORDSIZE == 64)
|
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
|
||||||
# define VL_PRI64 "l"
|
# define VL_PRI64 "l"
|
||||||
# else
|
# else
|
||||||
@@ -397,4 +407,4 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
#endif /*guard*/
|
#endif // Guard
|
||||||
|
|||||||
-1006
File diff suppressed because it is too large
Load Diff
Executable
+263
@@ -0,0 +1,263 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
# See copyright, etc in below POD section.
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
use Cwd;
|
||||||
|
use File::Copy qw(cp);
|
||||||
|
use File::Path qw(mkpath);
|
||||||
|
use Getopt::Long;
|
||||||
|
use IO::File;
|
||||||
|
use Pod::Usage;
|
||||||
|
use strict;
|
||||||
|
use vars qw($Debug);
|
||||||
|
|
||||||
|
our $Exclude_Line_Regexp;
|
||||||
|
our @Remove_Sources;
|
||||||
|
our @Source_Globs;
|
||||||
|
|
||||||
|
#======================================================================
|
||||||
|
# main
|
||||||
|
|
||||||
|
our $Opt_Stage = 0;
|
||||||
|
|
||||||
|
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,
|
||||||
|
)) {
|
||||||
|
die "%Error: Bad usage, try 'install_test --help'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
sub test {
|
||||||
|
-r "nodist/code_coverage.dat" or die "%Error: Run from the top of the verilator kit,";
|
||||||
|
require "./nodist/code_coverage.dat";
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 0) {
|
||||||
|
print "Stage 0: configure (coverage on)\n";
|
||||||
|
run("make distclean");
|
||||||
|
run("./configure --enable-longtests CXX='g++ --coverage'");
|
||||||
|
run("make -k");
|
||||||
|
# The optimized versions will not collect good coverage, overwrite them
|
||||||
|
run("cp bin/verilator_bin_dbg bin/verilator_bin");
|
||||||
|
run("cp bin/verilator_coverage_bin_dbg bin/verilator_coverage_bin");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 1) {
|
||||||
|
print "Stage 1: make examples (with coverage on)\n";
|
||||||
|
run("make examples");
|
||||||
|
run("make test_regress");
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cc_dir = "nodist/obj_dir/coverage";
|
||||||
|
if ($Opt_Stage <= 2) {
|
||||||
|
print "Stage 2: Create info files under $cc_dir\n";
|
||||||
|
mkpath($cc_dir);
|
||||||
|
mkpath("$cc_dir/info");
|
||||||
|
my $dats = `find . -print | grep .gcda`;
|
||||||
|
my %dirs;
|
||||||
|
foreach my $dat (split '\n', $dats) {
|
||||||
|
$dat =~ s!/[^/]+$!!;
|
||||||
|
$dirs{$dat} = 1;
|
||||||
|
}
|
||||||
|
foreach my $dir (sort keys %dirs) {
|
||||||
|
(my $outname = $dir) =~ s![^a-zA-Z0-9]+!_!g;
|
||||||
|
run("cd $cc_dir/info ; lcov -c -d ../../../../$dir -o app_test_${outname}.info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 3) {
|
||||||
|
# lcov doesn't have a control file to override single lines, so replicate the sources
|
||||||
|
print "Stage 3: Clone sources under $cc_dir\n";
|
||||||
|
clone_sources($cc_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 4) {
|
||||||
|
print "Stage 4: Copy .gcno files\n";
|
||||||
|
my $dats = `find . -print | grep .gcno`;
|
||||||
|
foreach my $dat (split '\n', $dats) {
|
||||||
|
next if $dat =~ /$cc_dir/;
|
||||||
|
my $outdat = $cc_dir."/".$dat;
|
||||||
|
#print "cp $dat, $outdat);\n";
|
||||||
|
cp($dat, $outdat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 5) {
|
||||||
|
print "Stage 5: Combine data files\n";
|
||||||
|
run("cd $cc_dir ; lcov -c -i -d src/obj_dbg -o app_base.info");
|
||||||
|
run("cd $cc_dir ; lcov -a app_base.info -o app_total.info");
|
||||||
|
my $infos = `cd $cc_dir ; find info -print | grep .info`;
|
||||||
|
my $comb = "";
|
||||||
|
my @infos = (split /\n/, $infos);
|
||||||
|
foreach my $info (@infos) {
|
||||||
|
$comb .= " -a $info";
|
||||||
|
# Need to batch them to avoid overrunning shell command length limit
|
||||||
|
if (length($comb) > 10000 || $info eq $infos[$#infos]) {
|
||||||
|
run("cd $cc_dir ; lcov -a app_total.info $comb -o app_total.info");
|
||||||
|
$comb = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 6) {
|
||||||
|
print "Stage 6: Filter processed source files\n";
|
||||||
|
my $cmd = '';
|
||||||
|
foreach my $glob (@Remove_Sources) {
|
||||||
|
$cmd .= " '$glob'";
|
||||||
|
}
|
||||||
|
run("cd $cc_dir ; lcov --remove app_total.info $cmd -o app_total.info");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 7) {
|
||||||
|
print "Stage 7: Create HTML\n";
|
||||||
|
cleanup_abs_paths($cc_dir, "$cc_dir/app_total.info", "$cc_dir/app_total.info");
|
||||||
|
run("cd $cc_dir ; genhtml app_total.info --demangle-cpp"
|
||||||
|
." --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 --output-directory html");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Opt_Stage <= 9) {
|
||||||
|
print "*-* All Finished *-*\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clone_sources {
|
||||||
|
my $cc_dir = shift;
|
||||||
|
my $excluded_lines = 0;
|
||||||
|
foreach my $glob (@Source_Globs) {
|
||||||
|
foreach my $infile (glob $glob) {
|
||||||
|
$infile !~ m!^/!
|
||||||
|
or die "%Error: source globs should be relative not absolute filenames,";
|
||||||
|
my $outfile = $cc_dir."/".$infile;
|
||||||
|
(my $outpath = $outfile) =~ s!/[^/]*$!!;
|
||||||
|
mkpath($outpath);
|
||||||
|
my $fh = IO::File->new("<$infile") or die "%Error: $! $infile,";
|
||||||
|
my $ofh = IO::File->new(">$outfile") or die "%Error: $! $outfile,";
|
||||||
|
my $lineno = 0;
|
||||||
|
while (defined(my $line = $fh->getline)) {
|
||||||
|
$lineno++;
|
||||||
|
chomp $line;
|
||||||
|
if ($line !~ m!// LCOV_EXCL_LINE!
|
||||||
|
&& $line =~ /$Exclude_Line_Regexp/) {
|
||||||
|
$line .= " //code_coverage: // LCOV_EXCL_LINE";
|
||||||
|
$excluded_lines++;
|
||||||
|
#print "$infile:$lineno: $line";
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
$ofh->print("$line\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print "Source code lines automatically LCOV_EXCL_LINE'ed: $excluded_lines\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cleanup_abs_paths {
|
||||||
|
my $cc_dir = shift;
|
||||||
|
my $infile = shift;
|
||||||
|
my $outfile = shift;
|
||||||
|
my $fh = IO::File->new("<$infile") or die "%Error: $! $infile,";
|
||||||
|
my @lines;
|
||||||
|
while (defined(my $line = $fh->getline)) {
|
||||||
|
if ($line =~ m!^SF:!) {
|
||||||
|
$line =~ s!$ENV{VERILATOR_ROOT}/!!;
|
||||||
|
$line =~ s!$cc_dir/!!;
|
||||||
|
$line =~ s!obj_dbg/verilog.y$!verilog.y!;
|
||||||
|
#print "Remaining SF: ",$line;
|
||||||
|
}
|
||||||
|
push @lines, $line;
|
||||||
|
}
|
||||||
|
my $ofh = IO::File->new(">$outfile") or die "%Error: $! $outfile,";
|
||||||
|
$ofh->print(@lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# .dat file callbacks
|
||||||
|
|
||||||
|
sub exclude_line_regexp {
|
||||||
|
$Exclude_Line_Regexp = shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub remove_source {
|
||||||
|
my @srcs = @_;
|
||||||
|
push @Remove_Sources, @srcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub source_globs {
|
||||||
|
my @dirs = @_;
|
||||||
|
push @Source_Globs, @dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
sub run {
|
||||||
|
# Run a system command, check errors
|
||||||
|
my $command = shift;
|
||||||
|
print "\t$command\n";
|
||||||
|
system "$command";
|
||||||
|
my $status = $?;
|
||||||
|
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
code_coverage - Build and collect Verilator coverage
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
cd $VERILATOR_ROOT
|
||||||
|
nodist/code_coverage
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
code_coverage builds Verilator with C++ coverage support and runs tests
|
||||||
|
with coverage enabled.
|
||||||
|
|
||||||
|
This will rebuild the current object files.
|
||||||
|
|
||||||
|
=head1 ARGUMENTS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item --help
|
||||||
|
|
||||||
|
Displays this message and program version and exits.
|
||||||
|
|
||||||
|
=item -stage I<stage>
|
||||||
|
|
||||||
|
Runs a specific stage (see the script).
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 DISTRIBUTION
|
||||||
|
|
||||||
|
Copyright 2019-2019 by Wilson Snyder. This package is free software; you
|
||||||
|
can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
Version 2.0.
|
||||||
|
|
||||||
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
Wilson Snyder <[email protected]>
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
C<lcov>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
### Local Variables:
|
||||||
|
### compile-command: "cd .. ; nodist/code_coverage "
|
||||||
|
### End:
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- Perl -*-
|
||||||
|
# DESCRIPTION: Verilator: Internal C++ code lcov control file
|
||||||
|
#
|
||||||
|
# Copyright 2019-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.
|
||||||
|
|
||||||
|
source_globs("src/*.cpp",
|
||||||
|
"src/*.h",
|
||||||
|
"src/*.l",
|
||||||
|
"src/*.y",
|
||||||
|
"src/obj_dbg/*.h",
|
||||||
|
"src/obj_dbg/*.cpp",
|
||||||
|
"include/*.c",
|
||||||
|
"include/*.cpp",
|
||||||
|
"include/*.h",
|
||||||
|
"include/*/*.h",
|
||||||
|
"include/*/*.cpp",
|
||||||
|
"include/*/*.c",
|
||||||
|
);
|
||||||
|
|
||||||
|
remove_source("/usr/include/*");
|
||||||
|
remove_source("*/include/sysc/*");
|
||||||
|
remove_source("*/V3ClkGater.cpp");
|
||||||
|
remove_source("*/V3ClkGater.h");
|
||||||
|
remove_source("*/V3GraphDfa.cpp");
|
||||||
|
remove_source("*/V3GraphDfa.h");
|
||||||
|
remove_source("*/V3Lexer_pregen.yy.cpp");
|
||||||
|
remove_source("*/V3PreLex_pregen.yy.cpp");
|
||||||
|
remove_source("*/verilog.c");
|
||||||
|
remove_source("*include/gtkwave/*");
|
||||||
|
# Something wrong in generation, unfortunately as would like this
|
||||||
|
#genhtml: ERROR: cannot read /svaha/wsnyder/SandBox/homecvs/v4/verilator/src/obj_dbg/verilog.y
|
||||||
|
#remove_source("*/src/obj_dbg/verilog.y");
|
||||||
|
remove_source("*test_regress/*");
|
||||||
|
remove_source("*examples/*");
|
||||||
|
|
||||||
|
|
||||||
|
exclude_line_regexp(qr/(\bv3fatalSrc\b|\bVL_UNCOVERABLE\b)/);
|
||||||
|
|
||||||
|
1;
|
||||||
+2
-2
@@ -38,8 +38,8 @@ cwrite("graph_export.cpp");
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
exit(1);
|
exit(1); # Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
|
|||||||
+2
-2
@@ -43,8 +43,8 @@ dotwrite();
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
exit(1);
|
exit(1); # Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
|
|||||||
+25
-21
@@ -22,7 +22,8 @@ Getopt::Long::config("no_auto_abbrev");
|
|||||||
if (! GetOptions (
|
if (! GetOptions (
|
||||||
"debug" => sub { $Debug = 1; },
|
"debug" => sub { $Debug = 1; },
|
||||||
"help" => sub { print "Version $VERSION\n";
|
"help" => sub { print "Version $VERSION\n";
|
||||||
pod2usage(-verbose=>2, -exitval => 2, output=>\*STDOUT, -noperldoc=>1); },
|
pod2usage(-verbose=>2, -exitval => 0,
|
||||||
|
output=>\*STDOUT, -noperldoc=>1); },
|
||||||
"narrow!" => sub { $Opt_Widen = 0; },
|
"narrow!" => sub { $Opt_Widen = 0; },
|
||||||
"version" => sub { print "Version $VERSION\n"; exit(0); },
|
"version" => sub { print "Version $VERSION\n"; exit(0); },
|
||||||
"widen!" => sub { $Opt_Widen = 1; }, # Default, undocumented
|
"widen!" => sub { $Opt_Widen = 1; }, # Default, undocumented
|
||||||
@@ -87,27 +88,30 @@ sub find_edits {
|
|||||||
my $hunk = $editlines->{$lineno}{hunk};
|
my $hunk = $editlines->{$lineno}{hunk};
|
||||||
$hunk_lastlines{$hunk} ||= $lineno if $editlines->{$lineno}{user_edit};
|
$hunk_lastlines{$hunk} ||= $lineno if $editlines->{$lineno}{user_edit};
|
||||||
}
|
}
|
||||||
# Expand to include }'s that finish basic block
|
|
||||||
foreach my $hunk (keys %hunk_lastlines) {
|
if ($Opt_Widen) {
|
||||||
while (my $lineno = $hunk_lastlines{$hunk}) {
|
# Expand to include }'s that finish basic block
|
||||||
++$lineno;
|
foreach my $hunk (keys %hunk_lastlines) {
|
||||||
if (($editlines->{$lineno}{line} || "")
|
while (my $lineno = $hunk_lastlines{$hunk}) {
|
||||||
=~ /^[+ ]\s+}[ \t};]*$/) {
|
++$lineno;
|
||||||
$hunk_lastlines{$hunk} = $lineno;
|
if (($editlines->{$lineno}{line} || "")
|
||||||
} else {
|
=~ /^[+ ]\s+}[ \t};]*$/) {
|
||||||
last;
|
$hunk_lastlines{$hunk} = $lineno;
|
||||||
|
} else {
|
||||||
|
last;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
# Expand to always untabify at least 3 lines (so that future diff will
|
||||||
# Expand to always untabify at least 3 lines (so that future diff will
|
# have non-tabs within a edit hunk distance
|
||||||
# have non-tabs within a edit hunk distance
|
foreach my $hunk (keys %hunk_firstlines) {
|
||||||
foreach my $hunk (keys %hunk_firstlines) {
|
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
|
||||||
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
|
--$hunk_firstlines{$hunk};
|
||||||
--$hunk_firstlines{$hunk};
|
++$hunk_lastlines{$hunk};
|
||||||
++$hunk_lastlines{$hunk};
|
}
|
||||||
}
|
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
|
||||||
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
|
++$hunk_lastlines{$hunk};
|
||||||
++$hunk_lastlines{$hunk};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +133,7 @@ sub edit_file {
|
|||||||
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
|
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
|
||||||
push @editits, $lineno if $editlines->{$lineno}{editit};
|
push @editits, $lineno if $editlines->{$lineno}{editit};
|
||||||
}
|
}
|
||||||
|
|
||||||
return if $#editits < 0;
|
return if $#editits < 0;
|
||||||
if (ignore($filename)) {
|
if (ignore($filename)) {
|
||||||
print "%Warning: Ignoring $filename\n";
|
print "%Warning: Ignoring $filename\n";
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ print '(query-replace-regexp "(\\([0-9a-z_]+\\))" "\\1" nil nil nil)',"\n";
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||||
exit(1);
|
exit(1); # Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
sub debug {
|
sub debug {
|
||||||
|
|||||||
+9
-8
@@ -60,6 +60,8 @@ pkgdatadir = @pkgdatadir@
|
|||||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||||
CPPFLAGS += @CPPFLAGS@
|
CPPFLAGS += @CPPFLAGS@
|
||||||
|
CFLAGS += @CFLAGS@
|
||||||
|
CXXFLAGS += @CXXFLAGS@
|
||||||
LDFLAGS += @LDFLAGS@
|
LDFLAGS += @LDFLAGS@
|
||||||
EXEEXT = @EXEEXT@
|
EXEEXT = @EXEEXT@
|
||||||
CFG_CXXFLAGS_SRC = @CFG_CXXFLAGS_SRC@
|
CFG_CXXFLAGS_SRC = @CFG_CXXFLAGS_SRC@
|
||||||
@@ -135,7 +137,7 @@ VLCOVGEN = $(srcdir)/vlcovgen
|
|||||||
all: make_info $(TGT)
|
all: make_info $(TGT)
|
||||||
|
|
||||||
make_info:
|
make_info:
|
||||||
@echo " Compile flags: " $(CXX) ${CPPFLAGS}
|
@echo " Compile flags: " $(CXX) ${CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
|
||||||
|
|
||||||
clean mostlyclean distclean maintainer-clean::
|
clean mostlyclean distclean maintainer-clean::
|
||||||
-rm -f *.o *.d perlxsi.c *_gen_*
|
-rm -f *.o *.d perlxsi.c *_gen_*
|
||||||
@@ -167,7 +169,6 @@ RAW_OBJS = \
|
|||||||
V3Cdc.o \
|
V3Cdc.o \
|
||||||
V3Changed.o \
|
V3Changed.o \
|
||||||
V3Clean.o \
|
V3Clean.o \
|
||||||
V3ClkGater.o \
|
|
||||||
V3Clock.o \
|
V3Clock.o \
|
||||||
V3Combine.o \
|
V3Combine.o \
|
||||||
V3Config.o \
|
V3Config.o \
|
||||||
@@ -280,21 +281,21 @@ V3Number_test: V3Number_test.o
|
|||||||
$(PERL) $(ASTGEN) -I$(srcdir) $*.cpp
|
$(PERL) $(ASTGEN) -I$(srcdir) $*.cpp
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
$(OBJCACHE) ${CXX} ${CPPFLAGSWALL} -c $<
|
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $<
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(OBJCACHE) ${CC} ${CPPFLAGSWALL} -c $<
|
$(OBJCACHE) ${CC} ${CFLAGS} ${CPPFLAGSWALL} -c $<
|
||||||
|
|
||||||
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
|
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
|
||||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||||
|
|
||||||
V3ParseGrammar.o: V3ParseGrammar.cpp V3ParseBison.c
|
V3ParseGrammar.o: V3ParseGrammar.cpp V3ParseBison.c
|
||||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||||
|
|
||||||
V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
|
V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
|
||||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||||
|
|
||||||
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||||
|
|
||||||
#### Generated files
|
#### Generated files
|
||||||
|
|
||||||
|
|||||||
+239
-232
@@ -21,15 +21,15 @@
|
|||||||
//
|
//
|
||||||
// Note this can be called multiple times.
|
// Note this can be called multiple times.
|
||||||
// Create a IACTIVE(initial), SACTIVE(combo)
|
// Create a IACTIVE(initial), SACTIVE(combo)
|
||||||
// ALWAYS: Remove any-edges from sense list
|
// ALWAYS: Remove any-edges from sense list
|
||||||
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
|
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
|
||||||
// Else fold into SACTIVE(sequent).
|
// Else fold into SACTIVE(sequent).
|
||||||
// OPTIMIZE: When support async clocks, fold into that active if possible
|
// OPTIMIZE: When support async clocks, fold into that active if possible
|
||||||
// INITIAL: Move into IACTIVE
|
// INITIAL: Move into IACTIVE
|
||||||
// WIRE: Move into SACTIVE(combo)
|
// WIRE: Move into SACTIVE(combo)
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -56,66 +56,68 @@ class ActiveNamer : public ActiveBaseVisitor {
|
|||||||
private:
|
private:
|
||||||
typedef std::map<string,AstActive*> ActiveNameMap;
|
typedef std::map<string,AstActive*> ActiveNameMap;
|
||||||
// STATE
|
// STATE
|
||||||
AstScope* m_scopep; // Current scope to add statement to
|
AstScope* m_scopep; // Current scope to add statement to
|
||||||
AstActive* m_iActivep; // For current scope, the IActive we're building
|
AstActive* m_iActivep; // For current scope, the IActive we're building
|
||||||
AstActive* m_cActivep; // For current scope, the SActive(combo) 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;
|
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
|
||||||
ActiveMap m_activeMap; // Map sentree to active, for folding.
|
ActiveMap m_activeMap; // Map sentree to active, for folding.
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void addActive(AstActive* nodep) {
|
void addActive(AstActive* nodep) {
|
||||||
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
|
UASSERT_OBJ(m_scopep, nodep, "NULL scope");
|
||||||
m_scopep->addActivep(nodep);
|
m_scopep->addActivep(nodep);
|
||||||
}
|
}
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstScope* nodep) {
|
virtual void visit(AstScope* nodep) {
|
||||||
m_scopep = nodep;
|
m_scopep = nodep;
|
||||||
m_iActivep = NULL;
|
m_iActivep = NULL;
|
||||||
m_cActivep = NULL;
|
m_cActivep = NULL;
|
||||||
m_activeSens.clear();
|
m_activeSens.clear();
|
||||||
m_activeMap.clear();
|
m_activeMap.clear();
|
||||||
iterateChildren(nodep);
|
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) {
|
virtual void visit(AstSenTree* nodep) {
|
||||||
// Simplify sensitivity list
|
// Simplify sensitivity list
|
||||||
V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep);
|
V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
// Empty visitors, speed things up
|
// Empty visitors, speed things up
|
||||||
virtual void visit(AstNodeStmt* nodep) { }
|
virtual void visit(AstNodeStmt* nodep) { }
|
||||||
//--------------------
|
//--------------------
|
||||||
// Default
|
// Default
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
// Default: Just iterate
|
// Default: Just iterate
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
// METHODS
|
// METHODS
|
||||||
public:
|
public:
|
||||||
AstScope* scopep() { return m_scopep; }
|
AstScope* scopep() { return m_scopep; }
|
||||||
AstActive* getCActive(FileLine* fl) {
|
AstActive* getCActive(FileLine* fl) {
|
||||||
if (!m_cActivep) {
|
if (!m_cActivep) {
|
||||||
m_cActivep = new AstActive(fl, "combo",
|
m_cActivep = new AstActive(
|
||||||
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Combo())));
|
fl, "combo",
|
||||||
m_cActivep->sensesStorep(m_cActivep->sensesp());
|
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
|
||||||
addActive(m_cActivep);
|
m_cActivep->sensesStorep(m_cActivep->sensesp());
|
||||||
}
|
addActive(m_cActivep);
|
||||||
return m_cActivep;
|
}
|
||||||
|
return m_cActivep;
|
||||||
}
|
}
|
||||||
AstActive* getIActive(FileLine* fl) {
|
AstActive* getIActive(FileLine* fl) {
|
||||||
if (!m_iActivep) {
|
if (!m_iActivep) {
|
||||||
m_iActivep = new AstActive(fl, "initial",
|
m_iActivep = new AstActive(
|
||||||
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Initial())));
|
fl, "initial",
|
||||||
m_iActivep->sensesStorep(m_iActivep->sensesp());
|
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
|
||||||
addActive(m_iActivep);
|
m_iActivep->sensesStorep(m_iActivep->sensesp());
|
||||||
}
|
addActive(m_iActivep);
|
||||||
return m_iActivep;
|
}
|
||||||
|
return m_iActivep;
|
||||||
}
|
}
|
||||||
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
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);
|
AstSenTree* activeSenp = m_activeSens.find(sensesp);
|
||||||
if (activeSenp) {
|
if (activeSenp) {
|
||||||
ActiveMap::iterator it = m_activeMap.find(activeSenp);
|
ActiveMap::iterator it = m_activeMap.find(activeSenp);
|
||||||
@@ -123,26 +125,26 @@ public:
|
|||||||
activep = it->second;
|
activep = it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not found, form a new one
|
// Not found, form a new one
|
||||||
if (!activep) {
|
if (!activep) {
|
||||||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||||
activep = new AstActive(fl, "sequent", newsenp);
|
activep = new AstActive(fl, "sequent", newsenp);
|
||||||
activep->sensesStorep(activep->sensesp());
|
activep->sensesStorep(activep->sensesp());
|
||||||
UINFO(8," New ACTIVE "<<activep<<endl);
|
UINFO(8," New ACTIVE "<<activep<<endl);
|
||||||
// Form the sensitivity list
|
// Form the sensitivity list
|
||||||
addActive(activep);
|
addActive(activep);
|
||||||
m_activeMap[newsenp] = activep;
|
m_activeMap[newsenp] = activep;
|
||||||
m_activeSens.add(newsenp);
|
m_activeSens.add(newsenp);
|
||||||
// Note actives may have also been added above in the Active visitor
|
// Note actives may have also been added above in the Active visitor
|
||||||
}
|
}
|
||||||
return activep;
|
return activep;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
ActiveNamer() {
|
ActiveNamer() {
|
||||||
m_scopep = NULL;
|
m_scopep = NULL;
|
||||||
m_iActivep = NULL;
|
m_iActivep = NULL;
|
||||||
m_cActivep = NULL;
|
m_cActivep = NULL;
|
||||||
}
|
}
|
||||||
virtual ~ActiveNamer() {}
|
virtual ~ActiveNamer() {}
|
||||||
void main(AstScope* nodep) {
|
void main(AstScope* nodep) {
|
||||||
@@ -157,66 +159,66 @@ class ActiveDlyVisitor : public ActiveBaseVisitor {
|
|||||||
public:
|
public:
|
||||||
enum CheckType { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
|
enum CheckType { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
|
||||||
private:
|
private:
|
||||||
CheckType m_check; // Combo logic or other
|
CheckType m_check; // Combo logic or other
|
||||||
AstNode* m_alwaysp; // Always we're under
|
AstNode* m_alwaysp; // Always we're under
|
||||||
AstNode* m_assignp; // In assign
|
AstNode* m_assignp; // In assign
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstAssignDly* nodep) {
|
virtual void visit(AstAssignDly* nodep) {
|
||||||
if (m_check != CT_SEQ) {
|
if (m_check != CT_SEQ) {
|
||||||
// Convert to a non-delayed assignment
|
// Convert to a non-delayed assignment
|
||||||
UINFO(5," ASSIGNDLY "<<nodep<<endl);
|
UINFO(5," ASSIGNDLY "<<nodep<<endl);
|
||||||
if (m_check == CT_INITIAL) {
|
if (m_check == CT_INITIAL) {
|
||||||
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial"
|
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial or final block\n"
|
||||||
" or final block; suggest blocking assignments (=).");
|
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
|
||||||
} else if (m_check == CT_LATCH) {
|
} else if (m_check == CT_LATCH) {
|
||||||
// Suppress. Shouldn't matter that the interior of the latch races
|
// Suppress. Shouldn't matter that the interior of the latch races
|
||||||
} else {
|
} else {
|
||||||
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
||||||
" (non flop or latch) block; suggest blocking assignments (=).");
|
" (non flop or latch) block\n"
|
||||||
}
|
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
|
||||||
|
}
|
||||||
AstNode* newp = new AstAssign(nodep->fileline(),
|
AstNode* newp = new AstAssign(nodep->fileline(),
|
||||||
nodep->lhsp()->unlinkFrBack(),
|
nodep->lhsp()->unlinkFrBack(),
|
||||||
nodep->rhsp()->unlinkFrBack());
|
nodep->rhsp()->unlinkFrBack());
|
||||||
nodep->replaceWith(newp);
|
nodep->replaceWith(newp);
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstAssign* nodep) {
|
virtual void visit(AstAssign* nodep) {
|
||||||
if (m_check == CT_SEQ) {
|
if (m_check == CT_SEQ) {
|
||||||
AstNode* las = m_assignp;
|
AstNode* las = m_assignp;
|
||||||
m_assignp = nodep;
|
m_assignp = nodep;
|
||||||
iterateAndNextNull(nodep->lhsp());
|
iterateAndNextNull(nodep->lhsp());
|
||||||
m_assignp = las;
|
m_assignp = las;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarRef* nodep) {
|
virtual void visit(AstVarRef* nodep) {
|
||||||
AstVar* varp=nodep->varp();
|
AstVar* varp = nodep->varp();
|
||||||
if (m_check == CT_SEQ
|
if (m_check == CT_SEQ
|
||||||
&& m_assignp
|
&& m_assignp
|
||||||
&& !varp->isUsedLoopIdx() // Ignore loop indicies
|
&& !varp->isUsedLoopIdx() // Ignore loop indices
|
||||||
&& !varp->isTemp()
|
&& !varp->isTemp()) {
|
||||||
) {
|
// Allow turning off warnings on the always, or the variable also
|
||||||
// Allow turning off warnings on the always, or the variable also
|
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||||
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||||
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)) {
|
||||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
m_assignp->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block\n"
|
||||||
) {
|
<<m_assignp->warnMore()<<"... Suggest delayed assignments (<=)");
|
||||||
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
||||||
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
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) {
|
virtual void visit(AstNode* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
ActiveDlyVisitor(AstNode* nodep, CheckType check) {
|
ActiveDlyVisitor(AstNode* nodep, CheckType check) {
|
||||||
m_alwaysp = nodep;
|
m_alwaysp = nodep;
|
||||||
m_check = check;
|
m_check = check;
|
||||||
m_assignp = NULL;
|
m_assignp = NULL;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~ActiveDlyVisitor() {}
|
virtual ~ActiveDlyVisitor() {}
|
||||||
@@ -229,183 +231,188 @@ class ActiveVisitor : public ActiveBaseVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Each call to V3Const::constify
|
// Each call to V3Const::constify
|
||||||
// AstNode::user4() Used by V3Const::constify, called below
|
// AstNode::user4() Used by V3Const::constify, called below
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
ActiveNamer m_namer; // Tracking of active names
|
ActiveNamer m_namer; // Tracking of active names
|
||||||
AstCFunc* m_scopeFinalp; // Final function for this scope
|
AstCFunc* m_scopeFinalp; // Final function for this scope
|
||||||
bool m_itemCombo; // Found a SenItem combo
|
bool m_itemCombo; // Found a SenItem combo
|
||||||
bool m_itemSequent; // Found a SenItem sequential
|
bool m_itemSequent; // Found a SenItem sequential
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstScope* nodep) {
|
virtual void visit(AstScope* nodep) {
|
||||||
// Create required actives and add to scope
|
// Create required actives and add to scope
|
||||||
UINFO(4," SCOPE "<<nodep<<endl);
|
UINFO(4," SCOPE "<<nodep<<endl);
|
||||||
// Clear last scope's names, and collect this scope's existing names
|
// Clear last scope's names, and collect this scope's existing names
|
||||||
m_namer.main(nodep);
|
m_namer.main(nodep);
|
||||||
m_scopeFinalp = NULL;
|
m_scopeFinalp = NULL;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstActive* 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) {
|
virtual void visit(AstInitial* nodep) {
|
||||||
// Relink to IACTIVE, unless already under it
|
// Relink to IACTIVE, unless already under it
|
||||||
UINFO(4," INITIAL "<<nodep<<endl);
|
UINFO(4," INITIAL "<<nodep<<endl);
|
||||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||||
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
wantactivep->addStmtsp(nodep);
|
wantactivep->addStmtsp(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstAssignAlias* nodep) {
|
virtual void visit(AstAssignAlias* nodep) {
|
||||||
// Relink to CACTIVE, unless already under it
|
// Relink to CACTIVE, unless already under it
|
||||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
wantactivep->addStmtsp(nodep);
|
wantactivep->addStmtsp(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstAssignW* nodep) {
|
virtual void visit(AstAssignW* nodep) {
|
||||||
// Relink to CACTIVE, unless already under it
|
// Relink to CACTIVE, unless already under it
|
||||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
wantactivep->addStmtsp(nodep);
|
wantactivep->addStmtsp(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstCoverToggle* nodep) {
|
virtual void visit(AstCoverToggle* nodep) {
|
||||||
// Relink to CACTIVE, unless already under it
|
// Relink to CACTIVE, unless already under it
|
||||||
UINFO(4," COVERTOGGLE "<<nodep<<endl);
|
UINFO(4," COVERTOGGLE "<<nodep<<endl);
|
||||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
wantactivep->addStmtsp(nodep);
|
wantactivep->addStmtsp(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstFinal* nodep) {
|
virtual void visit(AstFinal* nodep) {
|
||||||
// Relink to CFUNC for the final
|
// Relink to CFUNC for the final
|
||||||
UINFO(4," FINAL "<<nodep<<endl);
|
UINFO(4," FINAL "<<nodep<<endl);
|
||||||
if (!nodep->bodysp()) { // Empty, Kill it.
|
if (!nodep->bodysp()) { // Empty, Kill it.
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||||
if (!m_scopeFinalp) {
|
if (!m_scopeFinalp) {
|
||||||
m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(), m_namer.scopep());
|
m_scopeFinalp = new AstCFunc(
|
||||||
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(),
|
||||||
m_scopeFinalp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
m_namer.scopep());
|
||||||
m_scopeFinalp->dontCombine(true);
|
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||||
m_scopeFinalp->formCallTree(true);
|
m_scopeFinalp->addInitsp(
|
||||||
m_scopeFinalp->slow(true);
|
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||||
m_namer.scopep()->addActivep(m_scopeFinalp);
|
m_scopeFinalp->dontCombine(true);
|
||||||
}
|
m_scopeFinalp->formCallTree(true);
|
||||||
nodep->unlinkFrBack();
|
m_scopeFinalp->slow(true);
|
||||||
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName()));
|
m_namer.scopep()->addActivep(m_scopeFinalp);
|
||||||
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
|
}
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->unlinkFrBack();
|
||||||
|
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName(), true));
|
||||||
|
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
|
||||||
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
||||||
// Move always to appropriate ACTIVE based on its sense list
|
// Move always to appropriate ACTIVE based on its sense list
|
||||||
if (oldsensesp
|
if (oldsensesp
|
||||||
&& oldsensesp->sensesp()
|
&& oldsensesp->sensesp()
|
||||||
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
||||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||||
// Never executing. Kill it.
|
// Never executing. Kill it.
|
||||||
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
"Never senitem should be alone, else the never should be eliminated.");
|
||||||
return;
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Read sensitivitues
|
// Read sensitivities
|
||||||
m_itemCombo = false;
|
m_itemCombo = false;
|
||||||
m_itemSequent = false;
|
m_itemSequent = false;
|
||||||
iterateAndNextNull(oldsensesp);
|
iterateAndNextNull(oldsensesp);
|
||||||
bool combo = m_itemCombo;
|
bool combo = m_itemCombo;
|
||||||
bool sequent = m_itemSequent;
|
bool sequent = m_itemSequent;
|
||||||
|
|
||||||
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
|
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
|
||||||
if (combo && sequent) {
|
if (combo && sequent) {
|
||||||
if (!v3Global.opt.bboxUnsup()) {
|
if (!v3Global.opt.bboxUnsup()) {
|
||||||
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
|
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
|
||||||
}
|
}
|
||||||
sequent = false;
|
sequent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstActive* wantactivep = NULL;
|
AstActive* wantactivep = NULL;
|
||||||
if (combo && !sequent) {
|
if (combo && !sequent) {
|
||||||
// Combo: Relink to ACTIVE(combo)
|
// Combo: Relink to ACTIVE(combo)
|
||||||
wantactivep = m_namer.getCActive(nodep->fileline());
|
wantactivep = m_namer.getCActive(nodep->fileline());
|
||||||
} else {
|
} else {
|
||||||
// Sequential: Build a ACTIVE(name)
|
// Sequential: Build a ACTIVE(name)
|
||||||
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
|
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
|
||||||
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
||||||
// Summarize a long list of combo inputs as just "combo"
|
// Summarize a long list of combo inputs as just "combo"
|
||||||
#ifndef __COVERITY__ // Else dead code on next line.
|
#ifndef __COVERITY__ // Else dead code on next line.
|
||||||
if (combo) oldsensesp->addSensesp
|
if (combo) oldsensesp->addSensesp
|
||||||
(new AstSenItem(nodep->fileline(),AstSenItem::Combo()));
|
(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
||||||
#endif
|
#endif
|
||||||
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
|
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete sensitivity list
|
// Delete sensitivity list
|
||||||
if (oldsensesp) {
|
if (oldsensesp) {
|
||||||
oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp);
|
oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move node to new active
|
// Move node to new active
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
wantactivep->addStmtsp(nodep);
|
wantactivep->addStmtsp(nodep);
|
||||||
|
|
||||||
// Warn and/or convert any delayed assignments
|
// Warn and/or convert any delayed assignments
|
||||||
if (combo && !sequent) {
|
if (combo && !sequent) {
|
||||||
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
|
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
|
||||||
} else {
|
} else {
|
||||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
|
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!combo && sequent) {
|
else if (!combo && sequent) {
|
||||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
|
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlways* nodep) {
|
virtual void visit(AstAlways* nodep) {
|
||||||
// Move always to appropriate ACTIVE based on its sense list
|
// Move always to appropriate ACTIVE based on its sense list
|
||||||
UINFO(4," ALW "<<nodep<<endl);
|
UINFO(4," ALW "<<nodep<<endl);
|
||||||
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
|
//if (debug()>=9) nodep->dumpTree(cout, " Alw: ");
|
||||||
|
|
||||||
if (!nodep->bodysp()) {
|
if (!nodep->bodysp()) {
|
||||||
// Empty always. Kill it.
|
// Empty always. Kill it.
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
|
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlwaysPublic* nodep) {
|
virtual void visit(AstAlwaysPublic* nodep) {
|
||||||
// Move always to appropriate ACTIVE based on its sense list
|
// Move always to appropriate ACTIVE based on its sense list
|
||||||
UINFO(4," ALWPub "<<nodep<<endl);
|
UINFO(4," ALWPub "<<nodep<<endl);
|
||||||
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
|
//if (debug()>=9) nodep->dumpTree(cout, " Alw: ");
|
||||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||||
}
|
}
|
||||||
virtual void visit(AstSenGate* nodep) {
|
virtual void visit(AstSenGate* nodep) {
|
||||||
AstSenItem* subitemp = nodep->sensesp();
|
AstSenItem* subitemp = nodep->sensesp();
|
||||||
if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE
|
UASSERT_OBJ(subitemp->edgeType() == VEdgeType::ET_ANYEDGE
|
||||||
&& subitemp->edgeType() != AstEdgeType::ET_POSEDGE
|
|| subitemp->edgeType() == VEdgeType::ET_POSEDGE
|
||||||
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
|
|| subitemp->edgeType() == VEdgeType::ET_NEGEDGE,
|
||||||
nodep->v3fatalSrc("Strange activity type under SenGate");
|
nodep, "Strange activity type under SenGate");
|
||||||
}
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstSenItem* nodep) {
|
virtual void visit(AstSenItem* nodep) {
|
||||||
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
|
if (nodep->edgeType() == VEdgeType::ET_ANYEDGE) {
|
||||||
m_itemCombo = true;
|
m_itemCombo = true;
|
||||||
// Delete the sensitivity
|
// Delete the sensitivity
|
||||||
// We'll add it as a generic COMBO SenItem in a moment.
|
// We'll add it as a generic COMBO SenItem in a moment.
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
} else if (nodep->varrefp()) {
|
} else if (nodep->varrefp()) {
|
||||||
// V3LinkResolve should have cleaned most of these up
|
// V3LinkResolve should have cleaned most of these up
|
||||||
if (!nodep->varrefp()->width1()) nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
if (!nodep->varrefp()->width1()) {
|
||||||
<<nodep->varrefp()->prettyName());
|
nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
||||||
m_itemSequent = true;
|
<<nodep->varrefp()->prettyNameQ());
|
||||||
nodep->varrefp()->varp()->usedClock(true);
|
}
|
||||||
}
|
m_itemSequent = true;
|
||||||
|
nodep->varrefp()->varp()->usedClock(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty visitors, speed things up
|
// Empty visitors, speed things up
|
||||||
@@ -416,11 +423,11 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit ActiveVisitor(AstNetlist* nodep) {
|
explicit ActiveVisitor(AstNetlist* nodep) {
|
||||||
m_scopeFinalp = NULL;
|
m_scopeFinalp = NULL;
|
||||||
m_itemCombo = false;
|
m_itemCombo = false;
|
||||||
m_itemSequent = false;
|
m_itemSequent = false;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~ActiveVisitor() {}
|
virtual ~ActiveVisitor() {}
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3ACTIVE_H_
|
#ifndef _V3ACTIVE_H_
|
||||||
#define _V3ACTIVE_H_ 1
|
#define _V3ACTIVE_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void activeAll(AstNetlist* nodep);
|
static void activeAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+68
-66
@@ -21,12 +21,12 @@
|
|||||||
//
|
//
|
||||||
// Note this can be called multiple times.
|
// Note this can be called multiple times.
|
||||||
// Across all ACTIVES
|
// Across all ACTIVES
|
||||||
// SenTrees are now under each ACTIVE statement, we want them global:
|
// SenTrees are now under each ACTIVE statement, we want them global:
|
||||||
// Find SenTree in under global TopScope, or create it there
|
// Find SenTree in under global TopScope, or create it there
|
||||||
// Move SenTree the global SenTree
|
// Move SenTree the global SenTree
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -43,91 +43,93 @@ class ActiveTopVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist
|
// Entire netlist
|
||||||
// AstNode::user() bool. True if processed
|
// AstNode::user() bool. True if processed
|
||||||
// Each call to V3Const::constify
|
// Each call to V3Const::constify
|
||||||
// AstNode::user4() Used by V3Const::constify, called below
|
// AstNode::user4() Used by V3Const::constify, called below
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
AstTopScope* m_topscopep; // Top scope for adding sentrees under
|
AstTopScope* m_topscopep; // Top scope for adding sentrees under
|
||||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstTopScope* nodep) {
|
virtual void visit(AstTopScope* nodep) {
|
||||||
m_topscopep = nodep;
|
m_topscopep = nodep;
|
||||||
m_finder.main(m_topscopep);
|
m_finder.main(m_topscopep);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_topscopep = NULL;
|
m_topscopep = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
// Create required actives and add to module
|
// Create required actives and add to module
|
||||||
// We can start ordering at a module, or a scope
|
// We can start ordering at a module, or a scope
|
||||||
UINFO(4," MOD "<<nodep<<endl);
|
UINFO(4," MOD "<<nodep<<endl);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstActive* nodep) {
|
virtual void visit(AstActive* nodep) {
|
||||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||||
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
|
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
|
||||||
AstSenTree* sensesp = nodep->sensesp();
|
AstSenTree* sensesp = nodep->sensesp();
|
||||||
if (!sensesp) nodep->v3fatalSrc("NULL");
|
UASSERT_OBJ(sensesp, nodep, "NULL");
|
||||||
if (sensesp->sensesp()
|
if (sensesp->sensesp()
|
||||||
&& VN_IS(sensesp->sensesp(), SenItem)
|
&& VN_IS(sensesp->sensesp(), SenItem)
|
||||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||||
// Never executing. Kill it.
|
// Never executing. Kill it.
|
||||||
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep,
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
"Never senitem should be alone, else the never should be eliminated.");
|
||||||
return;
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
return;
|
||||||
// Copy combo tree to settlement tree with duplicated statements
|
}
|
||||||
if (sensesp->hasCombo()) {
|
// Copy combo tree to settlement tree with duplicated statements
|
||||||
AstSenTree* newsentreep
|
if (sensesp->hasCombo()) {
|
||||||
|
AstSenTree* newsentreep
|
||||||
= new AstSenTree(nodep->fileline(),
|
= new AstSenTree(nodep->fileline(),
|
||||||
new AstSenItem (nodep->fileline(), AstSenItem::Settle()));
|
new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
||||||
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
|
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
|
||||||
newp->sensesStorep(newsentreep);
|
newp->sensesStorep(newsentreep);
|
||||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||||
nodep->addNextHere(newp);
|
nodep->addNextHere(newp);
|
||||||
}
|
}
|
||||||
// Move the SENTREE for each active up to the global level.
|
// Move the SENTREE for each active up to the global level.
|
||||||
// This way we'll easily see what clock domains are identical
|
// This way we'll easily see what clock domains are identical
|
||||||
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
|
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
|
||||||
UINFO(4," lookdone\n");
|
UINFO(4," lookdone\n");
|
||||||
if (wantp != sensesp) {
|
if (wantp != sensesp) {
|
||||||
// Move the active's contents to the other active
|
// Move the active's contents to the other active
|
||||||
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
|
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
|
||||||
if (nodep->sensesStorep()) {
|
if (nodep->sensesStorep()) {
|
||||||
if (sensesp != nodep->sensesStorep()) nodep->v3fatalSrc("sensesStore should have been deleted earlier if different");
|
UASSERT_OBJ(sensesp == nodep->sensesStorep(), nodep,
|
||||||
sensesp->unlinkFrBack();
|
"sensesStore should have been deleted earlier if different");
|
||||||
// There may be other references to same sense tree,
|
sensesp->unlinkFrBack();
|
||||||
// we'll be removing all references when we get to them,
|
// There may be other references to same sense tree,
|
||||||
// but don't dangle our pointer yet!
|
// we'll be removing all references when we get to them,
|
||||||
pushDeletep(sensesp);
|
// but don't dangle our pointer yet!
|
||||||
}
|
pushDeletep(sensesp);
|
||||||
nodep->sensesp(wantp);
|
}
|
||||||
}
|
nodep->sensesp(wantp);
|
||||||
// No need to do statements under it, they're already moved.
|
}
|
||||||
|
// No need to do statements under it, they're already moved.
|
||||||
//iterateChildren(nodep);
|
//iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstInitial* nodep) {
|
virtual void visit(AstInitial* nodep) { // LCOV_EXCL_LINE
|
||||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||||
}
|
}
|
||||||
virtual void visit(AstAssignAlias* nodep) {
|
virtual void visit(AstAssignAlias* nodep) { // LCOV_EXCL_LINE
|
||||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||||
}
|
}
|
||||||
virtual void visit(AstAssignW* nodep) {
|
virtual void visit(AstAssignW* nodep) { // LCOV_EXCL_LINE
|
||||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlways* nodep) {
|
virtual void visit(AstAlways* nodep) { // LCOV_EXCL_LINE
|
||||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlwaysPublic* nodep) {
|
virtual void visit(AstAlwaysPublic* nodep) { // LCOV_EXCL_LINE
|
||||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||||
}
|
}
|
||||||
virtual void visit(AstFinal* nodep) {
|
virtual void visit(AstFinal* nodep) { // LCOV_EXCL_LINE
|
||||||
nodep->v3fatalSrc("Node should have been deleted");
|
nodep->v3fatalSrc("Node should have been deleted");
|
||||||
}
|
}
|
||||||
// Empty visitors, speed things up
|
// Empty visitors, speed things up
|
||||||
virtual void visit(AstNodeMath* nodep) {}
|
virtual void visit(AstNodeMath* nodep) {}
|
||||||
@@ -137,9 +139,9 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit ActiveTopVisitor(AstNetlist* nodep) {
|
explicit ActiveTopVisitor(AstNetlist* nodep) {
|
||||||
m_topscopep = NULL;
|
m_topscopep = NULL;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~ActiveTopVisitor() {}
|
virtual ~ActiveTopVisitor() {}
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3ACTIVETOP_H_
|
#ifndef _V3ACTIVETOP_H_
|
||||||
#define _V3ACTIVETOP_H_ 1
|
#define _V3ACTIVETOP_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void activeTopAll(AstNetlist* nodep);
|
static void activeTopAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+211
-205
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -37,46 +37,46 @@ class AssertVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE/TYPES
|
// NODE STATE/TYPES
|
||||||
// Cleared on netlist
|
// Cleared on netlist
|
||||||
// AstNode::user() -> bool. True if processed
|
// AstNode::user() -> bool. True if processed
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
AstNodeModule* m_modp; // Last module
|
AstNodeModule* m_modp; // Last module
|
||||||
AstBegin* m_beginp; // Last begin
|
AstBegin* m_beginp; // Last begin
|
||||||
unsigned m_modPastNum; // Module past numbering
|
unsigned m_modPastNum; // Module past numbering
|
||||||
V3Double0 m_statAsCover; // Statistic tracking
|
VDouble0 m_statAsCover; // Statistic tracking
|
||||||
V3Double0 m_statAsPsl; // Statistic tracking
|
VDouble0 m_statAsPsl; // Statistic tracking
|
||||||
V3Double0 m_statAsFull; // Statistic tracking
|
VDouble0 m_statAsFull; // Statistic tracking
|
||||||
V3Double0 m_statAsSV; // Statistic tracking
|
VDouble0 m_statAsSV; // Statistic tracking
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
|
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
|
||||||
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
|
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
|
||||||
+":"+cvtToStr(nodep->fileline()->lineno())
|
+":"+cvtToStr(nodep->fileline()->lineno())
|
||||||
+": Assertion failed in %m"
|
+": Assertion failed in %m"
|
||||||
+((message != "")?": ":"")+message
|
+((message != "")?": ":"")+message
|
||||||
+"\n");
|
+"\n");
|
||||||
}
|
}
|
||||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||||
nodep->displayType(AstDisplayType::DT_WRITE);
|
nodep->displayType(AstDisplayType::DT_WRITE);
|
||||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||||
// cppcheck-suppress nullPointer
|
// cppcheck-suppress nullPointer
|
||||||
AstNode* timenewp = new AstTime(nodep->fileline());
|
AstNode* timenewp = new AstTime(nodep->fileline());
|
||||||
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
||||||
timesp->unlinkFrBackWithNext();
|
timesp->unlinkFrBackWithNext();
|
||||||
timenewp->addNext(timesp);
|
timenewp->addNext(timesp);
|
||||||
}
|
}
|
||||||
nodep->fmtp()->addExprsp(timenewp);
|
nodep->fmtp()->addExprsp(timenewp);
|
||||||
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
||||||
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode* newIfAssertOn(AstNode* nodep) {
|
AstNode* newIfAssertOn(AstNode* nodep) {
|
||||||
// Add a internal if to check assertions are on.
|
// 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.
|
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||||
FileLine* fl = nodep->fileline();
|
FileLine* fl = nodep->fileline();
|
||||||
AstNode* newp
|
AstNode* newp
|
||||||
= new AstIf(fl,
|
= new AstIf(fl,
|
||||||
// If assertions are off, have constant propagation rip them out later
|
// If assertions are off, have constant propagation rip them out later
|
||||||
// This allows syntax errors and such to be detected normally.
|
// 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 AstCMath(fl, "Verilated::assertOn()", 1))
|
||||||
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
|
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
|
||||||
nodep, NULL);
|
nodep, NULL);
|
||||||
newp->user1(true); // Don't assert/cover this if
|
newp->user1(true); // Don't assert/cover this if
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
|
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
|
||||||
// Like newFireAssert() but omits the asserts-on check
|
// Like newFireAssert() but omits the asserts-on check
|
||||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL);
|
AstDisplay* dispp = new AstDisplay(nodep->fileline(),
|
||||||
AstNode* bodysp = dispp;
|
AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
AstNode* bodysp = dispp;
|
||||||
|
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||||
bodysp->addNext(new AstStop(nodep->fileline()));
|
bodysp->addNext(new AstStop(nodep->fileline()));
|
||||||
return bodysp;
|
return bodysp;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode* newFireAssert(AstNode* nodep, const string& message) {
|
AstNode* newFireAssert(AstNode* nodep, const string& message) {
|
||||||
AstNode* bodysp = newFireAssertUnchecked(nodep, message);
|
AstNode* bodysp = newFireAssertUnchecked(nodep, message);
|
||||||
bodysp = newIfAssertOn(bodysp);
|
bodysp = newIfAssertOn(bodysp);
|
||||||
return bodysp;
|
return bodysp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void newPslAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep,
|
void newPslAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep,
|
||||||
AstNode* stmtsp, const string& message) {
|
AstNode* stmtsp, const string& message) {
|
||||||
propp->unlinkFrBack();
|
propp->unlinkFrBack();
|
||||||
sentreep->unlinkFrBack();
|
sentreep->unlinkFrBack();
|
||||||
if (stmtsp) stmtsp->unlinkFrBack();
|
if (stmtsp) stmtsp->unlinkFrBack();
|
||||||
//
|
//
|
||||||
AstNode* bodysp = NULL;
|
AstNode* bodysp = NULL;
|
||||||
bool selfDestruct = false;
|
bool selfDestruct = false;
|
||||||
AstIf* ifp = NULL;
|
AstIf* ifp = NULL;
|
||||||
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
|
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
|
||||||
++m_statAsCover;
|
++m_statAsCover;
|
||||||
if (!v3Global.opt.coverageUser()) {
|
if (!v3Global.opt.coverageUser()) {
|
||||||
selfDestruct = true;
|
selfDestruct = true;
|
||||||
} else {
|
} else {
|
||||||
// V3Coverage assigned us a bucket to increment.
|
// V3Coverage assigned us a bucket to increment.
|
||||||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||||
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
|
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||||
covincp->unlinkFrBack();
|
covincp->unlinkFrBack();
|
||||||
if (message!="") covincp->declp()->comment(message);
|
if (message!="") covincp->declp()->comment(message);
|
||||||
bodysp = covincp;
|
bodysp = covincp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp);
|
if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp);
|
||||||
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
|
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
|
||||||
@@ -142,169 +143,173 @@ private:
|
|||||||
ifp = new AstIf(nodep->fileline(), propp, NULL, stmtsp);
|
ifp = new AstIf(nodep->fileline(), propp, NULL, stmtsp);
|
||||||
// It's more LIKELY that we'll take the NULL if clause
|
// It's more LIKELY that we'll take the NULL if clause
|
||||||
// than the sim-killing else clause:
|
// than the sim-killing else clause:
|
||||||
ifp->branchPred(AstBranchPred::BP_LIKELY);
|
ifp->branchPred(VBranchPred::BP_LIKELY);
|
||||||
bodysp = newIfAssertOn(ifp);
|
bodysp = newIfAssertOn(ifp);
|
||||||
} else if (VN_IS(nodep, PslRestrict)) {
|
} else if (VN_IS(nodep, PslRestrict)) {
|
||||||
// IEEE says simulator ignores these
|
// IEEE says simulator ignores these
|
||||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
nodep->v3fatalSrc("Unknown node type");
|
nodep->v3fatalSrc("Unknown node type");
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode* newp = new AstAlways(nodep->fileline(),
|
AstNode* newp = new AstAlways(nodep->fileline(),
|
||||||
VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
||||||
// Install it
|
// Install it
|
||||||
if (selfDestruct) {
|
if (selfDestruct) {
|
||||||
// Delete it after making the tree. This way we can tell the user
|
// 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.
|
// if it wasn't constructed nicely or has other errors without needing --coverage.
|
||||||
newp->deleteTree();
|
newp->deleteTree();
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
} else {
|
} else {
|
||||||
nodep->replaceWith(newp);
|
nodep->replaceWith(newp);
|
||||||
}
|
}
|
||||||
// Bye
|
// Bye
|
||||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void newVAssertion(AstVAssert* nodep, AstNode* propp) {
|
void newVAssertion(AstVAssert* nodep, AstNode* propp) {
|
||||||
propp->unlinkFrBackWithNext();
|
propp->unlinkFrBackWithNext();
|
||||||
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
|
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
|
||||||
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
|
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
|
||||||
//
|
//
|
||||||
if (VN_IS(nodep, VAssert)) {
|
if (VN_IS(nodep, VAssert)) {
|
||||||
if (passsp) passsp = newIfAssertOn(passsp);
|
if (passsp) passsp = newIfAssertOn(passsp);
|
||||||
if (failsp) failsp = newIfAssertOn(failsp);
|
if (failsp) failsp = newIfAssertOn(failsp);
|
||||||
} else {
|
} else {
|
||||||
nodep->v3fatalSrc("Unknown node type");
|
nodep->v3fatalSrc("Unknown node type");
|
||||||
}
|
}
|
||||||
|
|
||||||
AstIf* ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
|
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);
|
if (VN_IS(nodep, VAssert)) ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||||
//
|
//
|
||||||
// Install it
|
// Install it
|
||||||
nodep->replaceWith(newp);
|
nodep->replaceWith(newp);
|
||||||
// Bye
|
// Bye
|
||||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstIf* nodep) {
|
virtual void visit(AstIf* nodep) {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||||
AstNodeIf* ifp = nodep;
|
AstNodeIf* ifp = nodep;
|
||||||
AstNode* propp = NULL;
|
AstNode* propp = NULL;
|
||||||
bool hasDefaultElse = false;
|
bool hasDefaultElse = false;
|
||||||
do {
|
do {
|
||||||
// If this statement ends with 'else if', then nextIf will point to the
|
// If this statement ends with 'else if', then nextIf will point to the
|
||||||
// nextIf statement. Otherwise it will be null.
|
// nextIf statement. Otherwise it will be null.
|
||||||
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||||
iterateAndNextNull(ifp->condp());
|
iterateAndNextNull(ifp->condp());
|
||||||
|
|
||||||
// Recurse into the true case.
|
// Recurse into the true case.
|
||||||
iterateAndNextNull(ifp->ifsp());
|
iterateAndNextNull(ifp->ifsp());
|
||||||
|
|
||||||
// If the last else is not an else if, recurse into that too.
|
// If the last else is not an else if, recurse into that too.
|
||||||
if (ifp->elsesp() && !nextifp) {
|
if (ifp->elsesp() && !nextifp) {
|
||||||
iterateAndNextNull(ifp->elsesp());
|
iterateAndNextNull(ifp->elsesp());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a bitmask of the true predicates
|
// Build a bitmask of the true predicates
|
||||||
AstNode* predp = ifp->condp()->cloneTree(false);
|
AstNode* predp = ifp->condp()->cloneTree(false);
|
||||||
if (propp) {
|
if (propp) {
|
||||||
propp = new AstConcat(nodep->fileline(), predp, propp);
|
propp = new AstConcat(nodep->fileline(), predp, propp);
|
||||||
} else {
|
} else {
|
||||||
propp = predp;
|
propp = predp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record if this ends with an 'else' that does not have an if
|
// Record if this ends with an 'else' that does not have an if
|
||||||
if (ifp->elsesp() && !nextifp) {
|
if (ifp->elsesp() && !nextifp) {
|
||||||
hasDefaultElse = true;
|
hasDefaultElse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ifp = nextifp;
|
ifp = nextifp;
|
||||||
} while (ifp);
|
} while (ifp);
|
||||||
|
|
||||||
AstNode *newifp = nodep->cloneTree(false);
|
AstNode *newifp = nodep->cloneTree(false);
|
||||||
bool allow_none = nodep->unique0Pragma();
|
bool allow_none = nodep->unique0Pragma();
|
||||||
|
|
||||||
// Empty case means no property
|
// Empty case means no property
|
||||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
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
|
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||||
// predicates evaluates to true.
|
// predicates evaluates to true.
|
||||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||||
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
|
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||||
AstIf* checkifp = new AstIf(nodep->fileline(),
|
AstIf* checkifp = new AstIf(nodep->fileline(),
|
||||||
new AstLogNot(nodep->fileline(), ohot),
|
new AstLogNot(nodep->fileline(), ohot),
|
||||||
newFireAssert(nodep, "'unique if' statement violated"),
|
newFireAssert(nodep, "'unique if' statement violated"),
|
||||||
newifp);
|
newifp);
|
||||||
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||||
nodep->replaceWith(checkifp);
|
nodep->replaceWith(checkifp);
|
||||||
pushDeletep(nodep);
|
pushDeletep(nodep);
|
||||||
} else {
|
} else {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//========== Case assertions
|
//========== Case assertions
|
||||||
virtual void visit(AstCase* nodep) {
|
virtual void visit(AstCase* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (!nodep->user1SetOnce()) {
|
if (!nodep->user1SetOnce()) {
|
||||||
bool has_default=false;
|
bool has_default = false;
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
for (AstCaseItem* itemp = nodep->itemsp();
|
||||||
if (itemp->isDefault()) has_default=true;
|
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
|
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||||
++m_statAsFull;
|
// Simply need to add a default if there isn't one already
|
||||||
if (!has_default) {
|
++m_statAsFull;
|
||||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
|
if (!has_default) {
|
||||||
newFireAssert(nodep, "synthesis full_case, but non-match found")));
|
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 (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||||
// If there's a default, we allow none to match, else exactly one must match
|
// Need to check that one, and only one of the case items match at any moment
|
||||||
++m_statAsFull;
|
// If there's a default, we allow none to match, else exactly one must match
|
||||||
if (!has_default && !nodep->itemsp()) {
|
++m_statAsFull;
|
||||||
// Not parallel, but harmlessly so.
|
if (!has_default && !nodep->itemsp()) {
|
||||||
} else {
|
// Not parallel, but harmlessly so.
|
||||||
AstNode* propp = NULL;
|
} else {
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
AstNode* propp = NULL;
|
||||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
for (AstCaseItem* itemp = nodep->itemsp();
|
||||||
AstNode* onep;
|
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||||
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
for (AstNode* icondp = itemp->condsp();
|
||||||
onep = AstEqWild::newTyped(itemp->fileline(),
|
icondp!=NULL; icondp=icondp->nextp()) {
|
||||||
nodep->exprp()->cloneTree(false),
|
AstNode* onep;
|
||||||
icondp->cloneTree(false));
|
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
||||||
} else {
|
onep = AstEqWild::newTyped(itemp->fileline(),
|
||||||
onep = AstEq::newTyped(icondp->fileline(),
|
nodep->exprp()->cloneTree(false),
|
||||||
nodep->exprp()->cloneTree(false),
|
icondp->cloneTree(false));
|
||||||
icondp->cloneTree(false));
|
} else {
|
||||||
}
|
onep = AstEq::newTyped(icondp->fileline(),
|
||||||
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
|
nodep->exprp()->cloneTree(false),
|
||||||
else propp = onep;
|
icondp->cloneTree(false));
|
||||||
}
|
}
|
||||||
}
|
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
|
||||||
// Empty case means no property
|
else propp = onep;
|
||||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
}
|
||||||
|
}
|
||||||
|
// Empty case means no property
|
||||||
|
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||||
|
|
||||||
bool allow_none = has_default || nodep->unique0Pragma();
|
bool allow_none = has_default || nodep->unique0Pragma();
|
||||||
AstNode* ohot = (allow_none
|
AstNode* ohot
|
||||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
= (allow_none
|
||||||
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
|
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||||
|
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||||
AstIf* ifp = new AstIf(nodep->fileline(),
|
AstIf* ifp = new AstIf(nodep->fileline(),
|
||||||
new AstLogNot(nodep->fileline(), ohot),
|
new AstLogNot(nodep->fileline(), ohot),
|
||||||
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
|
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
|
||||||
NULL);
|
NULL);
|
||||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||||
nodep->addNotParallelp(ifp);
|
nodep->addNotParallelp(ifp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//========== Past
|
//========== Past
|
||||||
@@ -312,10 +317,11 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
uint32_t ticks = 1;
|
uint32_t ticks = 1;
|
||||||
if (nodep->ticksp()) {
|
if (nodep->ticksp()) {
|
||||||
if (!VN_IS(nodep->ticksp(), Const)) nodep->v3fatalSrc("Expected constant ticks, checked in V3Width");
|
UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
|
||||||
|
"Expected constant ticks, checked in V3Width");
|
||||||
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
|
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
|
||||||
}
|
}
|
||||||
if (ticks<1) nodep->v3fatalSrc("0 tick should have been checked in V3Width");
|
UASSERT_OBJ(ticks>=1, nodep, "0 tick should have been checked in V3Width");
|
||||||
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
||||||
AstVar* invarp = NULL;
|
AstVar* invarp = NULL;
|
||||||
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
|
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
|
||||||
@@ -341,46 +347,46 @@ private:
|
|||||||
//========== Statements
|
//========== Statements
|
||||||
virtual void visit(AstDisplay* nodep) {
|
virtual void visit(AstDisplay* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
// Replace the special types with standard text
|
// Replace the special types with standard text
|
||||||
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
||||||
replaceDisplay(nodep, "-Info");
|
replaceDisplay(nodep, "-Info");
|
||||||
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
|
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
|
||||||
replaceDisplay(nodep, "%%Warning");
|
replaceDisplay(nodep, "%%Warning");
|
||||||
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|
||||||
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
|
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
|
||||||
replaceDisplay(nodep, "%%Error");
|
replaceDisplay(nodep, "%%Error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||||
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
|
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
|
||||||
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
|
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstVAssert* nodep) {
|
virtual void visit(AstVAssert* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
|
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
|
||||||
++m_statAsSV;
|
++m_statAsSV;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_modPastNum = 0;
|
m_modPastNum = 0;
|
||||||
//
|
//
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
// Reset defaults
|
// Reset defaults
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstBegin* nodep) {
|
virtual void visit(AstBegin* nodep) {
|
||||||
// This code is needed rather than a visitor in V3Begin,
|
// This code is needed rather than a visitor in V3Begin,
|
||||||
// because V3Assert is called before V3Begin
|
// because V3Assert is called before V3Begin
|
||||||
AstBegin* lastp = m_beginp;
|
AstBegin* lastp = m_beginp;
|
||||||
{
|
{
|
||||||
m_beginp = nodep;
|
m_beginp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
m_beginp = lastp;
|
m_beginp = lastp;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
@@ -389,17 +395,17 @@ private:
|
|||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
explicit AssertVisitor(AstNetlist* nodep) {
|
explicit AssertVisitor(AstNetlist* nodep) {
|
||||||
m_beginp = NULL;
|
m_beginp = NULL;
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
m_modPastNum = 0;
|
m_modPastNum = 0;
|
||||||
// Process
|
// Process
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~AssertVisitor() {
|
virtual ~AssertVisitor() {
|
||||||
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
|
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
|
||||||
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
|
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
|
||||||
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
|
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
|
||||||
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
|
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3ASSERT_H_
|
#ifndef _V3ASSERT_H_
|
||||||
#define _V3ASSERT_H_ 1
|
#define _V3ASSERT_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void assertAll(AstNetlist* nodep);
|
static void assertAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+48
-48
@@ -18,9 +18,9 @@
|
|||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// Pre steps:
|
// Pre steps:
|
||||||
// Attach clocks to each assertion
|
// Attach clocks to each assertion
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -51,37 +51,37 @@ private:
|
|||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
AstSenTree* newSenTree(AstNode* nodep) {
|
AstSenTree* newSenTree(AstNode* nodep) {
|
||||||
// Create sentree based on clocked or default clock
|
// Create sentree based on clocked or default clock
|
||||||
// Return NULL for always
|
// Return NULL for always
|
||||||
AstSenTree* newp = NULL;
|
AstSenTree* newp = NULL;
|
||||||
AstNodeSenItem* senip = m_senip;
|
AstNodeSenItem* senip = m_senip;
|
||||||
if (!senip) senip = m_seniDefaultp;
|
if (!senip) senip = m_seniDefaultp;
|
||||||
if (!senip) senip = m_seniAlwaysp;
|
if (!senip) senip = m_seniAlwaysp;
|
||||||
if (!senip) {
|
if (!senip) {
|
||||||
nodep->v3error("Unsupported: Unclocked assertion");
|
nodep->v3error("Unsupported: Unclocked assertion");
|
||||||
newp = new AstSenTree(nodep->fileline(), NULL);
|
newp = new AstSenTree(nodep->fileline(), NULL);
|
||||||
} else {
|
} else {
|
||||||
newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
|
newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
|
||||||
}
|
}
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
void clearAssertInfo() {
|
void clearAssertInfo() {
|
||||||
m_senip = NULL;
|
m_senip = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
//========== Statements
|
//========== Statements
|
||||||
virtual void visit(AstClocking* nodep) {
|
virtual void visit(AstClocking* nodep) {
|
||||||
UINFO(8," CLOCKING"<<nodep<<endl);
|
UINFO(8," CLOCKING"<<nodep<<endl);
|
||||||
// Store the new default clock, reset on new module
|
// Store the new default clock, reset on new module
|
||||||
m_seniDefaultp = nodep->sensesp();
|
m_seniDefaultp = nodep->sensesp();
|
||||||
// Trash it, keeping children
|
// Trash it, keeping children
|
||||||
if (nodep->bodysp()) {
|
if (nodep->bodysp()) {
|
||||||
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
|
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
|
||||||
} else {
|
} else {
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
}
|
}
|
||||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlways* nodep) {
|
virtual void visit(AstAlways* nodep) {
|
||||||
iterateAndNextNull(nodep->sensesp());
|
iterateAndNextNull(nodep->sensesp());
|
||||||
@@ -93,12 +93,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||||
if (nodep->sentreep()) return; // Already processed
|
if (nodep->sentreep()) return; // Already processed
|
||||||
clearAssertInfo();
|
clearAssertInfo();
|
||||||
// Find PslClocking's burried under nodep->exprsp
|
// Find PslClocking's buried under nodep->exprsp
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->sentreep(newSenTree(nodep));
|
nodep->sentreep(newSenTree(nodep));
|
||||||
clearAssertInfo();
|
clearAssertInfo();
|
||||||
}
|
}
|
||||||
virtual void visit(AstPast* nodep) {
|
virtual void visit(AstPast* nodep) {
|
||||||
if (nodep->sentreep()) return; // Already processed
|
if (nodep->sentreep()) return; // Already processed
|
||||||
@@ -108,26 +108,26 @@ private:
|
|||||||
virtual void visit(AstPslClocked* nodep) {
|
virtual void visit(AstPslClocked* nodep) {
|
||||||
// No need to iterate the body, once replace will get iterated
|
// No need to iterate the body, once replace will get iterated
|
||||||
iterateAndNextNull(nodep->sensesp());
|
iterateAndNextNull(nodep->sensesp());
|
||||||
if (m_senip) {
|
if (m_senip) {
|
||||||
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||||
}
|
}
|
||||||
// Block is the new expression to evaluate
|
// Block is the new expression to evaluate
|
||||||
AstNode* blockp = nodep->propp()->unlinkFrBack();
|
AstNode* blockp = nodep->propp()->unlinkFrBack();
|
||||||
if (nodep->disablep()) {
|
if (nodep->disablep()) {
|
||||||
blockp = new AstAnd(nodep->disablep()->fileline(),
|
blockp = new AstAnd(nodep->disablep()->fileline(),
|
||||||
new AstNot(nodep->disablep()->fileline(),
|
new AstNot(nodep->disablep()->fileline(),
|
||||||
nodep->disablep()->unlinkFrBack()),
|
nodep->disablep()->unlinkFrBack()),
|
||||||
blockp);
|
blockp);
|
||||||
}
|
}
|
||||||
// Unlink and just keep a pointer to it, convert to sentree as needed
|
// Unlink and just keep a pointer to it, convert to sentree as needed
|
||||||
m_senip = nodep->sensesp();
|
m_senip = nodep->sensesp();
|
||||||
nodep->replaceWith(blockp);
|
nodep->replaceWith(blockp);
|
||||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
// Reset defaults
|
// Reset defaults
|
||||||
m_seniDefaultp = NULL;
|
m_seniDefaultp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
@@ -136,10 +136,10 @@ private:
|
|||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
explicit AssertPreVisitor(AstNetlist* nodep) {
|
explicit AssertPreVisitor(AstNetlist* nodep) {
|
||||||
m_seniDefaultp = NULL;
|
m_seniDefaultp = NULL;
|
||||||
m_seniAlwaysp = NULL;
|
m_seniAlwaysp = NULL;
|
||||||
clearAssertInfo();
|
clearAssertInfo();
|
||||||
// Process
|
// Process
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~AssertPreVisitor() {}
|
virtual ~AssertPreVisitor() {}
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3ASSERTPRE_H_
|
#ifndef _V3ASSERTPRE_H_
|
||||||
#define _V3ASSERTPRE_H_ 1
|
#define _V3ASSERTPRE_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void assertPreAll(AstNetlist* nodep);
|
static void assertPreAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+461
-409
File diff suppressed because it is too large
Load Diff
+1010
-964
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3ASTCONSTONLY_H_
|
#ifndef _V3ASTCONSTONLY_H_
|
||||||
#define _V3ASTCONSTONLY_H_ 1
|
#define _V3ASTCONSTONLY_H_ 1
|
||||||
|
|
||||||
@@ -30,4 +30,4 @@
|
|||||||
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
|
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
|
||||||
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
|
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+227
-242
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ void AstNodeVarRef::cloneRelink() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int AstNodeSel::bitConst() const {
|
int AstNodeSel::bitConst() const {
|
||||||
AstConst* constp=VN_CAST(bitp(), Const);
|
AstConst* constp = VN_CAST(bitp(), Const);
|
||||||
return (constp ? constp->toSInt() : 0);
|
return (constp ? constp->toSInt() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,25 +65,30 @@ void AstNodeClassDType::repairMemberCache() {
|
|||||||
clearCache();
|
clearCache();
|
||||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||||
if (m_members.find(itemp->name())!=m_members.end()) {
|
if (m_members.find(itemp->name())!=m_members.end()) {
|
||||||
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
|
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyNameQ()); }
|
||||||
else m_members.insert(make_pair(itemp->name(), itemp));
|
else m_members.insert(make_pair(itemp->name(), itemp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* AstNodeClassDType::broken() const {
|
const char* AstNodeClassDType::broken() const {
|
||||||
vl_unordered_set<AstMemberDType*> exists;
|
vl_unordered_set<AstMemberDType*> exists;
|
||||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
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) {
|
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
|
||||||
if (VL_UNLIKELY(exists.find(it->second) == exists.end())) {
|
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
|
||||||
this->v3error("Internal: Structure member broken: "<<it->first);
|
this->v3error("Internal: Structure member broken: "<<it->first);
|
||||||
return "member broken";
|
return "member broken";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
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 {
|
int AstBasicDType::widthAlignBytes() const {
|
||||||
if (width()<=8) return 1;
|
if (width()<=8) return 1;
|
||||||
else if (width()<=16) return 2;
|
else if (width()<=16) return 2;
|
||||||
@@ -115,38 +120,46 @@ int AstNodeClassDType::widthAlignBytes() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
if (lhsp->isString() && rhsp->isString()) {
|
||||||
return new AstEqD(fl, lhsp, rhsp);
|
return new AstEqN(fl, lhsp, rhsp);
|
||||||
|
} else if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||||
|
return new AstEqD(fl, lhsp, rhsp);
|
||||||
} else {
|
} else {
|
||||||
return new AstEq(fl, lhsp, rhsp);
|
return new AstEq(fl, lhsp, rhsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
if (lhsp->isString() && rhsp->isString()) {
|
||||||
return new AstGteD(fl, lhsp, rhsp);
|
return new AstGteN(fl, lhsp, rhsp);
|
||||||
|
} else if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||||
|
return new AstGteD(fl, lhsp, rhsp);
|
||||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||||
return new AstGteS(fl, lhsp, rhsp);
|
return new AstGteS(fl, lhsp, rhsp);
|
||||||
} else {
|
} else {
|
||||||
return new AstGte(fl, lhsp, rhsp);
|
return new AstGte(fl, lhsp, rhsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
if (lhsp->isString() && rhsp->isString()) {
|
||||||
return new AstLteD(fl, lhsp, rhsp);
|
return new AstLteN(fl, lhsp, rhsp);
|
||||||
|
} else if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||||
|
return new AstLteD(fl, lhsp, rhsp);
|
||||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||||
return new AstLteS(fl, lhsp, rhsp);
|
return new AstLteS(fl, lhsp, rhsp);
|
||||||
} else {
|
} else {
|
||||||
return new AstLte(fl, lhsp, rhsp);
|
return new AstLte(fl, lhsp, rhsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
if (lhsp->isString() && rhsp->isString()) {
|
||||||
return new AstEqD(fl, lhsp, rhsp);
|
return new AstEqN(fl, lhsp, rhsp);
|
||||||
|
} else if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||||
|
return new AstEqD(fl, lhsp, rhsp);
|
||||||
} else {
|
} else {
|
||||||
return new AstEqWild(fl, lhsp, rhsp);
|
return new AstEqWild(fl, lhsp, rhsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,10 +181,12 @@ bool AstVar::isScBv() const {
|
|||||||
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
|
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
|
||||||
}
|
}
|
||||||
bool AstVar::isScUint() const {
|
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 {
|
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) {
|
void AstVar::combineType(AstVarType type) {
|
||||||
@@ -208,8 +223,8 @@ string AstVar::verilogKwd() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
||||||
if (forReturn) named=false;
|
UASSERT_OBJ(!forReturn, this,
|
||||||
if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument");
|
"Internal data is never passed as return, but as first argument");
|
||||||
string otype;
|
string otype;
|
||||||
AstBasicDType* bdtypep = basicp();
|
AstBasicDType* bdtypep = basicp();
|
||||||
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
|
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
|
||||||
@@ -279,21 +294,21 @@ string AstVar::vlEnumType() const {
|
|||||||
AstBasicDType* bdtypep = basicp();
|
AstBasicDType* bdtypep = basicp();
|
||||||
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
|
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
|
||||||
if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
|
if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
|
||||||
return "VLVT_PTR";
|
return "VLVT_PTR";
|
||||||
} else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
|
} else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
|
||||||
return "VLVT_PTR";
|
return "VLVT_PTR";
|
||||||
} else if (strtype) {
|
} else if (strtype) {
|
||||||
arg += "VLVT_STRING";
|
arg += "VLVT_STRING";
|
||||||
} else if (widthMin() <= 8) {
|
} else if (widthMin() <= 8) {
|
||||||
arg += "VLVT_UINT8";
|
arg += "VLVT_UINT8";
|
||||||
} else if (widthMin() <= 16) {
|
} else if (widthMin() <= 16) {
|
||||||
arg += "VLVT_UINT16";
|
arg += "VLVT_UINT16";
|
||||||
} else if (widthMin() <= VL_WORDSIZE) {
|
} else if (widthMin() <= VL_WORDSIZE) {
|
||||||
arg += "VLVT_UINT32";
|
arg += "VLVT_UINT32";
|
||||||
} else if (isQuad()) {
|
} else if (isQuad()) {
|
||||||
arg += "VLVT_UINT64";
|
arg += "VLVT_UINT64";
|
||||||
} else if (isWide()) {
|
} else if (isWide()) {
|
||||||
arg += "VLVT_WDATA";
|
arg += "VLVT_WDATA";
|
||||||
}
|
}
|
||||||
// else return "VLVT_UNKNOWN"
|
// else return "VLVT_UNKNOWN"
|
||||||
return arg;
|
return arg;
|
||||||
@@ -339,28 +354,28 @@ string AstVar::vlPropInit() const {
|
|||||||
out += ", "+cvtToStr(adtypep->declRange().left())+", "+cvtToStr(adtypep->declRange().right());
|
out += ", "+cvtToStr(adtypep->declRange().left())+", "+cvtToStr(adtypep->declRange().right());
|
||||||
dtp = adtypep->subDTypep();
|
dtp = adtypep->subDTypep();
|
||||||
}
|
}
|
||||||
else break; // AstBasicDType - nothing below
|
else break; // AstBasicDType - nothing below
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::cPubArgType(bool named, bool forReturn) const {
|
string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||||
if (forReturn) named=false;
|
if (forReturn) named = false;
|
||||||
string arg;
|
string arg;
|
||||||
if (isWide() && isReadOnly()) arg += "const ";
|
if (isWide() && isReadOnly()) arg += "const ";
|
||||||
if (widthMin() == 1) {
|
if (widthMin() == 1) {
|
||||||
arg += "bool";
|
arg += "bool";
|
||||||
} else if (widthMin() <= VL_WORDSIZE) {
|
} else if (widthMin() <= VL_WORDSIZE) {
|
||||||
arg += "uint32_t";
|
arg += "uint32_t";
|
||||||
} else if (isWide()) {
|
} else if (isWide()) {
|
||||||
arg += "uint32_t"; // []'s added later
|
arg += "uint32_t"; // []'s added later
|
||||||
} else {
|
} else {
|
||||||
arg += "vluint64_t";
|
arg += "vluint64_t";
|
||||||
}
|
}
|
||||||
if (isWide()) {
|
if (isWide()) {
|
||||||
if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead");
|
if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead");
|
||||||
arg += " (& "+name();
|
arg += " (& "+name();
|
||||||
arg += ")["+cvtToStr(widthWords())+"]";
|
arg += ")["+cvtToStr(widthWords())+"]";
|
||||||
} else {
|
} else {
|
||||||
if (!forReturn && (isWritable()
|
if (!forReturn && (isWritable()
|
||||||
|| direction().isRefOrConstRef())) arg += "&";
|
|| direction().isRefOrConstRef())) arg += "&";
|
||||||
@@ -370,25 +385,25 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::dpiArgType(bool named, bool forReturn) const {
|
string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||||
if (forReturn) named=false;
|
if (forReturn) named = false;
|
||||||
string arg;
|
string arg;
|
||||||
if (isDpiOpenArray()) {
|
if (isDpiOpenArray()) {
|
||||||
arg = "const svOpenArrayHandle";
|
arg = "const svOpenArrayHandle";
|
||||||
} else if (!basicp()) {
|
} else if (!basicp()) {
|
||||||
arg = "UNKNOWN";
|
arg = "UNKNOWN";
|
||||||
} else if (basicp()->keyword().isDpiBitVal()) {
|
} else if (basicp()->keyword().isDpiBitVal()) {
|
||||||
if (widthMin() == 1) {
|
if (widthMin() == 1) {
|
||||||
arg = "unsigned char";
|
arg = "unsigned char";
|
||||||
if (!forReturn && isWritable()) arg += "*";
|
if (!forReturn && isWritable()) arg += "*";
|
||||||
} else {
|
} else {
|
||||||
if (forReturn) {
|
if (forReturn) {
|
||||||
arg = "svBitVecVal";
|
arg = "svBitVecVal";
|
||||||
} else if (isReadOnly()) {
|
} else if (isReadOnly()) {
|
||||||
arg = "const svBitVecVal*";
|
arg = "const svBitVecVal*";
|
||||||
} else {
|
} else {
|
||||||
arg = "svBitVecVal*";
|
arg = "svBitVecVal*";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (basicp()->keyword().isDpiLogicVal()) {
|
} else if (basicp()->keyword().isDpiLogicVal()) {
|
||||||
if (widthMin() == 1) {
|
if (widthMin() == 1) {
|
||||||
arg = "unsigned char";
|
arg = "unsigned char";
|
||||||
@@ -403,9 +418,9 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arg = basicp()->keyword().dpiType();
|
arg = basicp()->keyword().dpiType();
|
||||||
if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) {
|
if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) {
|
||||||
arg = "unsigned "+arg;
|
arg = "unsigned "+arg;
|
||||||
}
|
}
|
||||||
if (!forReturn && isWritable()) arg += "*";
|
if (!forReturn && isWritable()) arg += "*";
|
||||||
}
|
}
|
||||||
@@ -415,23 +430,23 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
|||||||
|
|
||||||
string AstVar::scType() const {
|
string AstVar::scType() const {
|
||||||
if (isScBigUint()) {
|
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()) {
|
} 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()) {
|
} 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) {
|
} else if (widthMin() == 1) {
|
||||||
return "bool";
|
return "bool";
|
||||||
} else if (widthMin() <= VL_WORDSIZE) {
|
} else if (widthMin() <= VL_WORDSIZE) {
|
||||||
if (widthMin() <= 8 && v3Global.opt.pinsUint8()) {
|
if (widthMin() <= 8 && v3Global.opt.pinsUint8()) {
|
||||||
return "uint8_t";
|
return "uint8_t";
|
||||||
} else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) {
|
} else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) {
|
||||||
return "uint16_t";
|
return "uint16_t";
|
||||||
} else {
|
} else {
|
||||||
return "uint32_t";
|
return "uint32_t";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "vluint64_t";
|
return "vluint64_t";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,25 +455,25 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
|||||||
// Historically sc variables are identified by a variable
|
// Historically sc variables are identified by a variable
|
||||||
// attribute. TODO it would better be a data type attribute.
|
// attribute. TODO it would better be a data type attribute.
|
||||||
if (AstVar* anodep = VN_CAST(nodep, Var)) {
|
if (AstVar* anodep = VN_CAST(nodep, Var)) {
|
||||||
if (anodep->isSc()) return anodep;
|
if (anodep->isSc()) return anodep;
|
||||||
else return NULL;
|
else return NULL;
|
||||||
}
|
}
|
||||||
else if (VN_IS(nodep, VarRef)) {
|
else if (VN_IS(nodep, VarRef)) {
|
||||||
if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp();
|
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)) {
|
else if (VN_IS(nodep, ArraySel)) {
|
||||||
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) 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->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||||
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) 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->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::mtasksString() const {
|
string AstVar::mtasksString() const {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os<<" all: ";
|
os<<"all: ";
|
||||||
for (MTaskIdSet::const_iterator it = m_mtaskIds.begin();
|
for (MTaskIdSet::const_iterator it = m_mtaskIds.begin();
|
||||||
it != m_mtaskIds.end(); ++it) {
|
it != m_mtaskIds.end(); ++it) {
|
||||||
os<<*it<<" ";
|
os<<*it<<" ";
|
||||||
@@ -473,58 +488,58 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
|||||||
// ref order: a[1][2][3][4]
|
// ref order: a[1][2][3][4]
|
||||||
// Created as: reg [4] a [1][2][3];
|
// Created as: reg [4] a [1][2][3];
|
||||||
// *or* reg a [1][2][3][4];
|
// *or* reg a [1][2][3][4];
|
||||||
// // The bit select is optional; used only if "leftover" []'s
|
// // The bit select is optional; used only if "leftover" []'s
|
||||||
// SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a))))
|
// SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a))))
|
||||||
// DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3))))
|
// DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3))))
|
||||||
// *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT))))
|
// *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT))))
|
||||||
// SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0
|
// 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(),
|
// TODO this function should be removed in favor of recursing the dtype(),
|
||||||
// as that allows for more complicated data types.
|
// as that allows for more complicated data types.
|
||||||
int dim = 0;
|
int dim = 0;
|
||||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
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 (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||||
if ((dim++)==dimension) {
|
if ((dim++)==dimension) {
|
||||||
return dtypep;
|
return dtypep;
|
||||||
}
|
}
|
||||||
dtypep = adtypep->subDTypep();
|
dtypep = adtypep->subDTypep();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||||
// AstBasicDType - nothing below, return null
|
// AstBasicDType - nothing below, return null
|
||||||
if (adtypep->isRanged()) {
|
if (adtypep->isRanged()) {
|
||||||
if ((dim++) == dimension) {
|
if ((dim++) == dimension) {
|
||||||
return adtypep;
|
return adtypep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
|
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
|
||||||
if (adtypep->packed()) {
|
if (adtypep->packed()) {
|
||||||
if ((dim++) == dimension) {
|
if ((dim++) == dimension) {
|
||||||
return adtypep;
|
return adtypep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// Node no ->next in loop; use continue where necessary
|
// Node no ->next in loop; use continue where necessary
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AstNodeDType::arrayUnpackedElements() {
|
uint32_t AstNodeDType::arrayUnpackedElements() {
|
||||||
uint32_t entries=1;
|
uint32_t entries = 1;
|
||||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
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)) {
|
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
entries *= adtypep->elementsConst();
|
entries *= adtypep->elementsConst();
|
||||||
dtypep = adtypep->subDTypep();
|
dtypep = adtypep->subDTypep();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// AstBasicDType - nothing below, 1
|
// AstBasicDType - nothing below, 1
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
@@ -534,17 +549,17 @@ std::pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
|||||||
uint32_t packed = 0;
|
uint32_t packed = 0;
|
||||||
uint32_t unpacked = 0;
|
uint32_t unpacked = 0;
|
||||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
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 (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||||
if (VN_IS(adtypep, PackArrayDType)) packed++;
|
if (VN_IS(adtypep, PackArrayDType)) packed++;
|
||||||
else unpacked++;
|
else unpacked++;
|
||||||
dtypep = adtypep->subDTypep();
|
dtypep = adtypep->subDTypep();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||||
if (includeBasic && adtypep->isRanged()) packed++;
|
if (includeBasic && adtypep->isRanged()) packed++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return make_pair(packed, unpacked);
|
return make_pair(packed, unpacked);
|
||||||
}
|
}
|
||||||
@@ -553,7 +568,7 @@ int AstNodeDType::widthPow2() const {
|
|||||||
// I.e. width 30 returns 32, width 32 returns 32.
|
// I.e. width 30 returns 32, width 32 returns 32.
|
||||||
uint32_t width = this->width();
|
uint32_t width = this->width();
|
||||||
for (int p2=30; p2>=0; p2--) {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -572,9 +587,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variabl
|
|||||||
} else {
|
} else {
|
||||||
nodep = VN_CAST(nodep, NodePreSel)->lhsp();
|
nodep = VN_CAST(nodep, NodePreSel)->lhsp();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
return nodep;
|
return nodep;
|
||||||
}
|
}
|
||||||
@@ -599,7 +614,7 @@ string AstScope::nameDotless() const {
|
|||||||
string out = shortName();
|
string out = shortName();
|
||||||
string::size_type pos;
|
string::size_type pos;
|
||||||
while ((pos = out.find('.')) != string::npos) {
|
while ((pos = out.find('.')) != string::npos) {
|
||||||
out.replace(pos, 1, "__");
|
out.replace(pos, 1, "__");
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -607,58 +622,58 @@ string AstScope::nameDotless() const {
|
|||||||
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||||
string out;
|
string out;
|
||||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||||
out += textp->text();
|
out += textp->text();
|
||||||
}
|
}
|
||||||
// TOP will be replaced by top->name()
|
// TOP will be replaced by top->name()
|
||||||
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
|
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, 7) == "__DOT__") out.replace(0, 7, "");
|
||||||
if (out.substr(0,1) == ".") out.replace(0,1,"");
|
if (out.substr(0, 1) == ".") out.replace(0, 1, "");
|
||||||
return AstNode::prettyName(out);
|
return AstNode::prettyName(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||||
string out;
|
string out;
|
||||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
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, 10) == "__DOT__TOP") out.replace(0, 10, "");
|
||||||
if (out.substr(0,7) == "__DOT__") out.replace(0,7,"");
|
if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, "");
|
||||||
if (out.substr(0,1) == ".") out.replace(0,1,"");
|
if (out.substr(0, 1) == ".") out.replace(0, 1, "");
|
||||||
string::size_type pos;
|
string::size_type pos;
|
||||||
while ((pos = out.find('.')) != string::npos) {
|
while ((pos = out.find('.')) != string::npos) {
|
||||||
out.replace(pos, 1, "__");
|
out.replace(pos, 1, "__");
|
||||||
}
|
}
|
||||||
while ((pos=out.find("__DOT__")) != string::npos) {
|
while ((pos = out.find("__DOT__")) != string::npos) {
|
||||||
out.replace(pos, 7, "__");
|
out.replace(pos, 7, "__");
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AstSenTree::hasClocked() const {
|
bool AstSenTree::hasClocked() const {
|
||||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||||
if (senp->isClocked()) return true;
|
if (senp->isClocked()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool AstSenTree::hasSettle() const {
|
bool AstSenTree::hasSettle() const {
|
||||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||||
if (senp->isSettle()) return true;
|
if (senp->isSettle()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool AstSenTree::hasInitial() const {
|
bool AstSenTree::hasInitial() const {
|
||||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||||
if (senp->isInitial()) return true;
|
if (senp->isInitial()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool AstSenTree::hasCombo() const {
|
bool AstSenTree::hasCombo() const {
|
||||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||||
if (senp->isCombo()) return true;
|
if (senp->isCombo()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -667,20 +682,14 @@ void AstTypeTable::clearCache() {
|
|||||||
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
|
// 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
|
// 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) {
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m_detailedMap.clear();
|
m_detailedMap.clear();
|
||||||
// Clear generic()'s so dead detection will work
|
// Clear generic()'s so dead detection will work
|
||||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||||
bdtypep->generic(false);
|
bdtypep->generic(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,8 +698,8 @@ void AstTypeTable::repairCache() {
|
|||||||
clearCache();
|
clearCache();
|
||||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||||
(void)findInsertSameDType(bdtypep);
|
(void)findInsertSameDType(bdtypep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,30 +719,16 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
AstBasicDType* AstTypeTable::findLogicBitDType(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);
|
|
||||||
LogicMap& mapr = m_logicMap[idx][static_cast<int>(numeric)];
|
|
||||||
LogicMap::const_iterator it = mapr.find(widths);
|
|
||||||
if (it != mapr.end()) return it->second;
|
|
||||||
//
|
|
||||||
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
|
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
|
||||||
// Because the detailed map doesn't update this map,
|
|
||||||
// check the detailed map for this same node, and if found update this map
|
|
||||||
// Also adds this new node to the detailed map
|
|
||||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||||
if (newp != new1p) new1p->deleteTree();
|
if (newp != new1p) new1p->deleteTree();
|
||||||
else addTypesp(newp);
|
else addTypesp(newp);
|
||||||
//
|
|
||||||
mapr.insert(make_pair(widths,newp));
|
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
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* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
|
||||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||||
if (newp != new1p) new1p->deleteTree();
|
if (newp != new1p) new1p->deleteTree();
|
||||||
@@ -743,11 +738,11 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
|||||||
|
|
||||||
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
||||||
VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(),
|
VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(),
|
||||||
nodep->keyword(), nodep->nrange());
|
nodep->keyword(), nodep->nrange());
|
||||||
DetailedMap& mapr = m_detailedMap;
|
DetailedMap& mapr = m_detailedMap;
|
||||||
DetailedMap::const_iterator it = mapr.find(key);
|
DetailedMap::const_iterator it = mapr.find(key);
|
||||||
if (it != mapr.end()) return it->second;
|
if (it != mapr.end()) return it->second;
|
||||||
mapr.insert(make_pair(key,nodep));
|
mapr.insert(make_pair(key, nodep));
|
||||||
nodep->generic(true);
|
nodep->generic(true);
|
||||||
// No addTypesp; the upper function that called new() is responsible for adding
|
// No addTypesp; the upper function that called new() is responsible for adding
|
||||||
return nodep;
|
return nodep;
|
||||||
@@ -757,12 +752,12 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
|||||||
// Special walking tree inserters
|
// Special walking tree inserters
|
||||||
|
|
||||||
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
|
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
|
||||||
if (!backp()) newp->v3fatalSrc("Can't find current statement to addBeforeStmt");
|
UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt");
|
||||||
// Look up; virtual call will find where to put it
|
// Look up; virtual call will find where to put it
|
||||||
this->backp()->addBeforeStmt(newp, this);
|
this->backp()->addBeforeStmt(newp, this);
|
||||||
}
|
}
|
||||||
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
|
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
|
||||||
if (!backp()) newp->v3fatalSrc("Can't find current statement to addNextStmt");
|
UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt");
|
||||||
// Look up; virtual call will find where to put it
|
// Look up; virtual call will find where to put it
|
||||||
this->backp()->addNextStmt(newp, this);
|
this->backp()->addNextStmt(newp, this);
|
||||||
}
|
}
|
||||||
@@ -779,39 +774,41 @@ void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
|
|||||||
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
|
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
|
||||||
// Special, as statements need to be put in different places
|
// Special, as statements need to be put in different places
|
||||||
// Belowp is how we came to recurse up to this point
|
// 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()) {
|
if (belowp == precondsp()) {
|
||||||
// Must have been first statement in precondsp list, so newp is new first statement
|
// Must have been first statement in precondsp list, so newp is new first statement
|
||||||
belowp->addHereThisAsNext(newp);
|
belowp->addHereThisAsNext(newp);
|
||||||
} else if (belowp == condp()) {
|
} else if (belowp == condp()) {
|
||||||
// Goes before condition, IE in preconditions
|
// Goes before condition, IE in preconditions
|
||||||
addPrecondsp(newp);
|
addPrecondsp(newp);
|
||||||
} else if (belowp == bodysp()) {
|
} else if (belowp == bodysp()) {
|
||||||
// Was first statement in body, so new front
|
// Was first statement in body, so new front
|
||||||
belowp->addHereThisAsNext(newp);
|
belowp->addHereThisAsNext(newp);
|
||||||
} else {
|
} 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) {
|
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||||
// Special, as statements need to be put in different places
|
// Special, as statements need to be put in different places
|
||||||
// Belowp is how we came to recurse up to this point
|
// 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()) {
|
if (belowp == precondsp()) {
|
||||||
// Next in precond list
|
// Next in precond list
|
||||||
belowp->addNextHere(newp);
|
belowp->addNextHere(newp);
|
||||||
} else if (belowp == condp()) {
|
} else if (belowp == condp()) {
|
||||||
// Becomes first statement in body, body may have been empty
|
// Becomes first statement in body, body may have been empty
|
||||||
if (bodysp()) {
|
if (bodysp()) {
|
||||||
bodysp()->addHereThisAsNext(newp);
|
bodysp()->addHereThisAsNext(newp);
|
||||||
} else {
|
} else {
|
||||||
addBodysp(newp);
|
addBodysp(newp);
|
||||||
}
|
}
|
||||||
} else if (belowp == bodysp()) {
|
} else if (belowp == bodysp()) {
|
||||||
// Next statement in body
|
// Next statement in body
|
||||||
belowp->addNextHere(newp);
|
belowp->addNextHere(newp);
|
||||||
} else {
|
} 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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,7 +820,7 @@ void AstNode::dump(std::ostream& str) {
|
|||||||
//<<" "<<cvtToHex(this)->m_backp
|
//<<" "<<cvtToHex(this)->m_backp
|
||||||
<<" <e"<<std::dec<<editCount()
|
<<" <e"<<std::dec<<editCount()
|
||||||
<<((editCount()>=editCountLast())?"#>":">")
|
<<((editCount()>=editCountLast())?"#>":">")
|
||||||
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lineno()<<"}";
|
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lastLineno()<<"}";
|
||||||
if (user1p()) str<<" u1="<<cvtToHex(user1p());
|
if (user1p()) str<<" u1="<<cvtToHex(user1p());
|
||||||
if (user2p()) str<<" u2="<<cvtToHex(user2p());
|
if (user2p()) str<<" u2="<<cvtToHex(user2p());
|
||||||
if (user3p()) str<<" u3="<<cvtToHex(user3p());
|
if (user3p()) str<<" u3="<<cvtToHex(user3p());
|
||||||
@@ -831,17 +828,17 @@ void AstNode::dump(std::ostream& str) {
|
|||||||
if (user5p()) str<<" u5="<<cvtToHex(user5p());
|
if (user5p()) str<<" u5="<<cvtToHex(user5p());
|
||||||
if (hasDType()) {
|
if (hasDType()) {
|
||||||
// Final @ so less likely to by accident read it as a nodep
|
// 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())<<"@";
|
else str<<" @dt="<<cvtToHex(dtypep())<<"@";
|
||||||
if (AstNodeDType* dtp = dtypep()) {
|
if (AstNodeDType* dtp = dtypep()) {
|
||||||
dtp->dumpSmall(str);
|
dtp->dumpSmall(str);
|
||||||
}
|
}
|
||||||
} else { // V3Broken will throw an error
|
} else { // V3Broken will throw an error
|
||||||
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<cvtToHex(dtypep());
|
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<cvtToHex(dtypep());
|
||||||
}
|
}
|
||||||
if (name()!="") {
|
if (name()!="") {
|
||||||
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
|
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
|
||||||
else str<<" "<<V3Number::quoteNameControls(name());
|
else str<<" "<<V3OutFormatter::quoteNameControls(name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,9 +927,9 @@ void AstRange::dump(std::ostream& str) {
|
|||||||
if (littleEndian()) str<<" [LITTLE]";
|
if (littleEndian()) str<<" [LITTLE]";
|
||||||
}
|
}
|
||||||
void AstRefDType::dump(std::ostream& str) {
|
void AstRefDType::dump(std::ostream& str) {
|
||||||
static bool s_recursing = false;
|
|
||||||
this->AstNodeDType::dump(str);
|
this->AstNodeDType::dump(str);
|
||||||
if (defp()) {
|
if (defp()) {
|
||||||
|
static bool s_recursing = false;
|
||||||
if (!s_recursing) { // Prevent infinite dump if circular typedefs
|
if (!s_recursing) { // Prevent infinite dump if circular typedefs
|
||||||
s_recursing = true;
|
s_recursing = true;
|
||||||
str<<" -> "; defp()->dump(str);
|
str<<" -> "; defp()->dump(str);
|
||||||
@@ -951,7 +948,7 @@ void AstNodeDType::dump(std::ostream& str) {
|
|||||||
if (generic()) str<<" [GENERIC]";
|
if (generic()) str<<" [GENERIC]";
|
||||||
if (AstNodeDType* dtp = virtRefDTypep()) {
|
if (AstNodeDType* dtp = virtRefDTypep()) {
|
||||||
str<<" refdt="<<cvtToHex(dtp);
|
str<<" refdt="<<cvtToHex(dtp);
|
||||||
dtp->dumpSmall(str);
|
dtp->dumpSmall(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void AstNodeDType::dumpSmall(std::ostream& str) {
|
void AstNodeDType::dumpSmall(std::ostream& str) {
|
||||||
@@ -962,7 +959,7 @@ void AstNodeDType::dumpSmall(std::ostream& str) {
|
|||||||
<<(isDouble()?"d":"")
|
<<(isDouble()?"d":"")
|
||||||
<<(isString()?"str":"");
|
<<(isString()?"str":"");
|
||||||
if (!isDouble() && !isString()) {
|
if (!isDouble() && !isString()) {
|
||||||
str<<"w"<<(widthSized()?"":"u")<<width();
|
str<<"w"<<(widthSized()?"":"u")<<width();
|
||||||
}
|
}
|
||||||
if (!widthSized()) str<<"/"<<widthMin();
|
if (!widthSized()) str<<"/"<<widthMin();
|
||||||
str<<")";
|
str<<")";
|
||||||
@@ -996,8 +993,8 @@ void AstPackageImport::dump(std::ostream& str) {
|
|||||||
void AstSel::dump(std::ostream& str) {
|
void AstSel::dump(std::ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (declRange().ranged()) {
|
if (declRange().ranged()) {
|
||||||
str<<" decl"<<declRange()<<"]";
|
str<<" decl"<<declRange()<<"]";
|
||||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void AstSliceSel::dump(std::ostream& str) {
|
void AstSliceSel::dump(std::ostream& str) {
|
||||||
@@ -1014,37 +1011,21 @@ void AstMTaskBody::dump(std::ostream& str) {
|
|||||||
void AstTypeTable::dump(std::ostream& str) {
|
void AstTypeTable::dump(std::ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||||
if (AstBasicDType* subnodep=m_basicps[i]) {
|
if (AstBasicDType* subnodep = m_basicps[i]) {
|
||||||
str<<endl; // Newline from caller, so newline first
|
str<<endl; // Newline from caller, so newline first
|
||||||
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
|
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
|
||||||
str<<" -> ";
|
str<<" -> ";
|
||||||
subnodep->dump(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
|
|
||||||
std::stringstream nsstr;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
DetailedMap& mapr = m_detailedMap;
|
DetailedMap& mapr = m_detailedMap;
|
||||||
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||||
AstBasicDType* dtypep = it->second;
|
AstBasicDType* dtypep = it->second;
|
||||||
str<<endl; // Newline from caller, so newline first
|
str<<endl; // Newline from caller, so newline first
|
||||||
str<<"\t\tdetailed -> ";
|
str<<"\t\tdetailed -> ";
|
||||||
dtypep->dump(str);
|
dtypep->dump(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note get newline from caller too.
|
// Note get newline from caller too.
|
||||||
}
|
}
|
||||||
@@ -1153,8 +1134,8 @@ void AstBegin::dump(std::ostream& str) {
|
|||||||
void AstCoverDecl::dump(std::ostream& str) {
|
void AstCoverDecl::dump(std::ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (this->dataDeclNullp()) {
|
if (this->dataDeclNullp()) {
|
||||||
str<<" -> ";
|
str<<" -> ";
|
||||||
this->dataDeclNullp()->dump(str);
|
this->dataDeclNullp()->dump(str);
|
||||||
} else {
|
} else {
|
||||||
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
|
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
|
||||||
}
|
}
|
||||||
@@ -1176,12 +1157,16 @@ void AstNodeText::dump(std::ostream& str) {
|
|||||||
string out = text();
|
string out = text();
|
||||||
string::size_type pos;
|
string::size_type pos;
|
||||||
if ((pos = out.find('\n')) != string::npos) {
|
if ((pos = out.find('\n')) != string::npos) {
|
||||||
out.erase(pos,out.length()-pos);
|
out.erase(pos, out.length()-pos);
|
||||||
out += "...";
|
out += "...";
|
||||||
}
|
}
|
||||||
str<<" \""<<out<<"\"";
|
str<<" \""<<out<<"\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AstVFile::dump(std::ostream& str) {
|
||||||
|
this->AstNode::dump(str);
|
||||||
|
}
|
||||||
|
|
||||||
void AstCFile::dump(std::ostream& str) {
|
void AstCFile::dump(std::ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (source()) str<<" [SRC]";
|
if (source()) str<<" [SRC]";
|
||||||
@@ -1190,8 +1175,8 @@ void AstCFile::dump(std::ostream& str) {
|
|||||||
void AstCCall::dump(std::ostream& str) {
|
void AstCCall::dump(std::ostream& str) {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (funcp()) {
|
if (funcp()) {
|
||||||
str<<" "<<funcp()->name()<<" => ";
|
str<<" "<<funcp()->name()<<" => ";
|
||||||
funcp()->dump(str);
|
funcp()->dump(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void AstCFunc::dump(std::ostream& str) {
|
void AstCFunc::dump(std::ostream& str) {
|
||||||
|
|||||||
+2292
-1920
File diff suppressed because it is too large
Load Diff
+154
-152
@@ -20,15 +20,15 @@
|
|||||||
// V3Begin's Transformations:
|
// V3Begin's Transformations:
|
||||||
//
|
//
|
||||||
// Each module:
|
// Each module:
|
||||||
// Look for BEGINs
|
// Look for BEGINs
|
||||||
// BEGIN(VAR...) -> VAR ... {renamed}
|
// BEGIN(VAR...) -> VAR ... {renamed}
|
||||||
// FOR -> WHILEs
|
// FOR -> WHILEs
|
||||||
//
|
//
|
||||||
// There are two scopes; named BEGINs change %m and variable scopes.
|
// There are two scopes; named BEGINs change %m and variable scopes.
|
||||||
// Unnamed BEGINs change only variable, not $display("%m") scope.
|
// Unnamed BEGINs change only variable, not $display("%m") scope.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -47,17 +47,17 @@ class BeginState {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
//Entire netlist:
|
//Entire netlist:
|
||||||
// AstNodeFTask::user1 -> bool, 1=processed
|
// AstNodeFTask::user1 -> bool, 1=processed
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
bool m_anyFuncInBegin;
|
bool m_anyFuncInBegin;
|
||||||
public:
|
public:
|
||||||
BeginState() {
|
BeginState() {
|
||||||
m_anyFuncInBegin = false;
|
m_anyFuncInBegin = false;
|
||||||
}
|
}
|
||||||
~BeginState() {}
|
~BeginState() {}
|
||||||
void userMarkChanged(AstNode* nodep) {
|
void userMarkChanged(AstNode* nodep) {
|
||||||
nodep->user1(true);
|
nodep->user1(true);
|
||||||
m_anyFuncInBegin = true;
|
m_anyFuncInBegin = true;
|
||||||
}
|
}
|
||||||
bool anyFuncInBegin() const { return m_anyFuncInBegin; }
|
bool anyFuncInBegin() const { return m_anyFuncInBegin; }
|
||||||
};
|
};
|
||||||
@@ -67,171 +67,173 @@ public:
|
|||||||
class BeginVisitor : public AstNVisitor {
|
class BeginVisitor : public AstNVisitor {
|
||||||
private:
|
private:
|
||||||
// STATE
|
// STATE
|
||||||
BeginState* m_statep; // Current global state
|
BeginState* m_statep; // Current global state
|
||||||
AstNodeModule* m_modp; // Current module
|
AstNodeModule* m_modp; // Current module
|
||||||
AstNodeFTask* m_ftaskp; // Current function/task
|
AstNodeFTask* m_ftaskp; // Current function/task
|
||||||
string m_namedScope; // Name of begin blocks above us
|
string m_namedScope; // Name of begin blocks above us
|
||||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||||
int m_repeatNum; // Repeat counter
|
int m_repeatNum; // Repeat counter
|
||||||
int m_ifDepth; // Current if depth
|
int m_ifDepth; // Current if depth
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_repeatNum = 0;
|
m_repeatNum = 0;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeFTask* nodep) {
|
virtual void visit(AstNodeFTask* nodep) {
|
||||||
UINFO(8," "<<nodep<<endl);
|
UINFO(8," "<<nodep<<endl);
|
||||||
// Rename it
|
// Rename it
|
||||||
if (m_unnamedScope != "") {
|
if (m_unnamedScope != "") {
|
||||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||||
m_statep->userMarkChanged(nodep);
|
m_statep->userMarkChanged(nodep);
|
||||||
}
|
}
|
||||||
// BEGIN wrapping a function rename that function, but don't affect the inside function's variables
|
// BEGIN wrapping a function rename that function, but don't affect
|
||||||
// We then restart with empty naming; so that any begin's inside the function will rename inside the function
|
// the inside function's variables. We then restart with empty
|
||||||
// Process children
|
// naming; so that any begin's inside the function will rename
|
||||||
string oldScope = m_namedScope;
|
// inside the function.
|
||||||
string oldUnnamed = m_unnamedScope;
|
// Process children
|
||||||
{
|
string oldScope = m_namedScope;
|
||||||
m_namedScope = "";
|
string oldUnnamed = m_unnamedScope;
|
||||||
m_unnamedScope = "";
|
{
|
||||||
m_ftaskp = nodep;
|
m_namedScope = "";
|
||||||
|
m_unnamedScope = "";
|
||||||
|
m_ftaskp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_ftaskp = NULL;
|
m_ftaskp = NULL;
|
||||||
}
|
}
|
||||||
m_namedScope = oldScope;
|
m_namedScope = oldScope;
|
||||||
m_unnamedScope = oldUnnamed;
|
m_unnamedScope = oldUnnamed;
|
||||||
}
|
}
|
||||||
virtual void visit(AstBegin* nodep) {
|
virtual void visit(AstBegin* nodep) {
|
||||||
// Begin blocks were only useful in variable creation, change names and delete
|
// Begin blocks were only useful in variable creation, change names and delete
|
||||||
UINFO(8," "<<nodep<<endl);
|
UINFO(8," "<<nodep<<endl);
|
||||||
string oldScope = m_namedScope;
|
string oldScope = m_namedScope;
|
||||||
string oldUnnamed = m_unnamedScope;
|
string oldUnnamed = m_unnamedScope;
|
||||||
{
|
{
|
||||||
UINFO(8,"nname "<<m_namedScope<<endl);
|
UINFO(8,"nname "<<m_namedScope<<endl);
|
||||||
if (nodep->name() != "") { // Else unneeded unnamed block
|
if (nodep->name() != "") { // Else unneeded unnamed block
|
||||||
// Create data for dotted variable resolution
|
// Create data for dotted variable resolution
|
||||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||||
string::size_type pos;
|
string::size_type pos;
|
||||||
while ((pos=dottedname.find("__DOT__")) != string::npos) {
|
while ((pos=dottedname.find("__DOT__")) != string::npos) {
|
||||||
string ident = dottedname.substr(0,pos);
|
string ident = dottedname.substr(0, pos);
|
||||||
dottedname = dottedname.substr(pos+strlen("__DOT__"));
|
dottedname = dottedname.substr(pos+strlen("__DOT__"));
|
||||||
if (!nodep->unnamed()) {
|
if (!nodep->unnamed()) {
|
||||||
if (m_namedScope=="") m_namedScope = ident;
|
if (m_namedScope=="") m_namedScope = ident;
|
||||||
else m_namedScope = m_namedScope + "__DOT__"+ident;
|
else m_namedScope = m_namedScope + "__DOT__"+ident;
|
||||||
}
|
}
|
||||||
if (m_unnamedScope=="") m_unnamedScope = ident;
|
if (m_unnamedScope=="") m_unnamedScope = ident;
|
||||||
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
|
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
|
||||||
// Create CellInline for dotted var resolution
|
// Create CellInline for dotted var resolution
|
||||||
if (!m_ftaskp) {
|
if (!m_ftaskp) {
|
||||||
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
||||||
m_unnamedScope, "__BEGIN__");
|
m_unnamedScope, "__BEGIN__");
|
||||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
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());
|
iterateAndNextNull(nodep->stmtsp());
|
||||||
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
||||||
}
|
}
|
||||||
m_namedScope = oldScope;
|
m_namedScope = oldScope;
|
||||||
m_unnamedScope = oldUnnamed;
|
m_unnamedScope = oldUnnamed;
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
AstNode* addsp = NULL;
|
AstNode* addsp = NULL;
|
||||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||||
stmtsp->unlinkFrBackWithNext();
|
stmtsp->unlinkFrBackWithNext();
|
||||||
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
|
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
|
||||||
}
|
}
|
||||||
if (addsp) {
|
if (addsp) {
|
||||||
nodep->replaceWith(addsp);
|
nodep->replaceWith(addsp);
|
||||||
} else {
|
} else {
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
}
|
}
|
||||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstVar* nodep) {
|
virtual void visit(AstVar* nodep) {
|
||||||
if (m_unnamedScope != "") {
|
if (m_unnamedScope != "") {
|
||||||
// Rename it
|
// Rename it
|
||||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||||
m_statep->userMarkChanged(nodep);
|
m_statep->userMarkChanged(nodep);
|
||||||
// Move to module
|
// Move to module
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||||
else m_modp->addStmtp(nodep);
|
else m_modp->addStmtp(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstCell* nodep) {
|
virtual void visit(AstCell* nodep) {
|
||||||
UINFO(8," CELL "<<nodep<<endl);
|
UINFO(8," CELL "<<nodep<<endl);
|
||||||
if (m_namedScope != "") {
|
if (m_namedScope != "") {
|
||||||
m_statep->userMarkChanged(nodep);
|
m_statep->userMarkChanged(nodep);
|
||||||
// Rename it
|
// Rename it
|
||||||
nodep->name(m_namedScope+"__DOT__"+nodep->name());
|
nodep->name(m_namedScope+"__DOT__"+nodep->name());
|
||||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||||
// Move to module
|
// Move to module
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
m_modp->addStmtp(nodep);
|
m_modp->addStmtp(nodep);
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarXRef* nodep) {
|
virtual void visit(AstVarXRef* nodep) {
|
||||||
UINFO(9, " VARXREF "<<nodep<<endl);
|
UINFO(9, " VARXREF "<<nodep<<endl);
|
||||||
if (m_namedScope != "" && nodep->inlinedDots() == "") {
|
if (m_namedScope != "" && nodep->inlinedDots() == "") {
|
||||||
nodep->inlinedDots(m_namedScope);
|
nodep->inlinedDots(m_namedScope);
|
||||||
UINFO(9, " rescope to "<<nodep<<endl);
|
UINFO(9, " rescope to "<<nodep<<endl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstScopeName* nodep) {
|
virtual void visit(AstScopeName* nodep) {
|
||||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||||
// Similar code in V3Inline
|
// Similar code in V3Inline
|
||||||
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
||||||
if (m_namedScope != "") {
|
if (m_namedScope != "") {
|
||||||
// To keep correct visual order, must add before other Text's
|
// To keep correct visual order, must add before other Text's
|
||||||
AstNode* afterp = nodep->scopeAttrp();
|
AstNode* afterp = nodep->scopeAttrp();
|
||||||
if (afterp) afterp->unlinkFrBackWithNext();
|
if (afterp) afterp->unlinkFrBackWithNext();
|
||||||
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope));
|
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope));
|
||||||
if (afterp) nodep->scopeAttrp(afterp);
|
if (afterp) nodep->scopeAttrp(afterp);
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstCoverDecl* nodep) {
|
virtual void visit(AstCoverDecl* nodep) {
|
||||||
// Don't need to fix path in coverage statements, they're not under
|
// Don't need to fix path in coverage statements, they're not under
|
||||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
// VISITORS - LINT CHECK
|
// VISITORS - LINT CHECK
|
||||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
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
|
// Check IFDEPTH warning - could be in other transform files if desire
|
||||||
int prevIfDepth = m_ifDepth;
|
int prevIfDepth = m_ifDepth;
|
||||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
|
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
|
||||||
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
|
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
|
||||||
m_ifDepth = -1;
|
m_ifDepth = -1;
|
||||||
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
|
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
|
||||||
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
||||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
||||||
m_ifDepth = -1;
|
m_ifDepth = -1;
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_ifDepth = prevIfDepth;
|
m_ifDepth = prevIfDepth;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
BeginVisitor(AstNetlist* nodep, BeginState* statep) {
|
BeginVisitor(AstNetlist* nodep, BeginState* statep) {
|
||||||
m_statep = statep;
|
m_statep = statep;
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
m_ftaskp = NULL;
|
m_ftaskp = NULL;
|
||||||
m_repeatNum = 0;
|
m_repeatNum = 0;
|
||||||
m_ifDepth = 0;
|
m_ifDepth = 0;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~BeginVisitor() {}
|
virtual ~BeginVisitor() {}
|
||||||
@@ -244,29 +246,29 @@ class BeginRelinkVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Input:
|
// Input:
|
||||||
// AstNodeFTask::user1p // Node replaced, rename it
|
// AstNodeFTask::user1p // Node replaced, rename it
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||||
if (nodep->taskp()->user1()) { // It was converted
|
if (nodep->taskp()->user1()) { // It was converted
|
||||||
UINFO(9, " relinkFTask "<<nodep<<endl);
|
UINFO(9, " relinkFTask "<<nodep<<endl);
|
||||||
nodep->name(nodep->taskp()->name());
|
nodep->name(nodep->taskp()->name());
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarRef* nodep) {
|
virtual void visit(AstVarRef* nodep) {
|
||||||
if (nodep->varp()->user1()) { // It was converted
|
if (nodep->varp()->user1()) { // It was converted
|
||||||
UINFO(9, " relinVarRef "<<nodep<<endl);
|
UINFO(9, " relinVarRef "<<nodep<<endl);
|
||||||
nodep->name(nodep->varp()->name());
|
nodep->name(nodep->varp()->name());
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstIfaceRefDType* nodep) {
|
virtual void visit(AstIfaceRefDType* nodep) {
|
||||||
// May have changed cell names
|
// May have changed cell names
|
||||||
// TypeTable is always after all modules, so names are stable
|
// TypeTable is always after all modules, so names are stable
|
||||||
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
||||||
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
||||||
UINFO(8," rename to "<<nodep<<endl);
|
UINFO(8," rename to "<<nodep<<endl);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
//--------------------
|
//--------------------
|
||||||
@@ -274,7 +276,7 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) {
|
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) {
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3BEGIN_H_
|
#ifndef _V3BEGIN_H_
|
||||||
#define _V3BEGIN_H_ 1
|
#define _V3BEGIN_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void debeginAll(AstNetlist* nodep);
|
static void debeginAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+54
-54
@@ -18,15 +18,15 @@
|
|||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// BRANCH TRANSFORMATIONS:
|
// BRANCH TRANSFORMATIONS:
|
||||||
// At each IF/(IF else).
|
// At each IF/(IF else).
|
||||||
// Count underneath $display/$stop statements.
|
// Count underneath $display/$stop statements.
|
||||||
// If more on if than else, this branch is unlikely, or vice-versa.
|
// If more on if than else, this branch is unlikely, or vice-versa.
|
||||||
// At each FTASKREF,
|
// At each FTASKREF,
|
||||||
// Count calls into the function
|
// Count calls into the function
|
||||||
// Then, if FTASK is called only once, add inline attribute
|
// Then, if FTASK is called only once, add inline attribute
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -44,89 +44,89 @@ class BranchVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstFTask::user1() -> int. Number of references
|
// AstFTask::user1() -> int. Number of references
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
typedef std::vector<AstCFunc*> CFuncVec;
|
typedef std::vector<AstCFunc*> CFuncVec;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
int m_likely; // Excuses for branch likely taken
|
int m_likely; // Excuses for branch likely taken
|
||||||
int m_unlikely; // Excuses for branch likely not taken
|
int m_unlikely; // Excuses for branch likely not taken
|
||||||
CFuncVec m_cfuncsp; // List of all tasks
|
CFuncVec m_cfuncsp; // List of all tasks
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
m_likely = false;
|
m_likely = false;
|
||||||
m_unlikely = false;
|
m_unlikely = false;
|
||||||
}
|
}
|
||||||
void checkUnlikely(AstNode* nodep) {
|
void checkUnlikely(AstNode* nodep) {
|
||||||
if (nodep->isUnlikely()) {
|
if (nodep->isUnlikely()) {
|
||||||
UINFO(4," UNLIKELY: "<<nodep<<endl);
|
UINFO(4," UNLIKELY: "<<nodep<<endl);
|
||||||
m_unlikely++;
|
m_unlikely++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeIf* nodep) {
|
virtual void visit(AstNodeIf* nodep) {
|
||||||
UINFO(4," IF: "<<nodep<<endl);
|
UINFO(4," IF: "<<nodep<<endl);
|
||||||
int lastLikely = m_likely;
|
int lastLikely = m_likely;
|
||||||
int lastUnlikely = m_unlikely;
|
int lastUnlikely = m_unlikely;
|
||||||
{
|
{
|
||||||
// Do if
|
// Do if
|
||||||
reset();
|
reset();
|
||||||
iterateAndNextNull(nodep->ifsp());
|
iterateAndNextNull(nodep->ifsp());
|
||||||
int ifLikely = m_likely;
|
int ifLikely = m_likely;
|
||||||
int ifUnlikely = m_unlikely;
|
int ifUnlikely = m_unlikely;
|
||||||
// Do else
|
// Do else
|
||||||
reset();
|
reset();
|
||||||
iterateAndNextNull(nodep->elsesp());
|
iterateAndNextNull(nodep->elsesp());
|
||||||
int elseLikely = m_likely;
|
int elseLikely = m_likely;
|
||||||
int elseUnlikely = m_unlikely;
|
int elseUnlikely = m_unlikely;
|
||||||
// Compute
|
// Compute
|
||||||
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
|
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
|
||||||
if (likeness>0) {
|
if (likeness > 0) {
|
||||||
nodep->branchPred(AstBranchPred::BP_LIKELY);
|
nodep->branchPred(VBranchPred::BP_LIKELY);
|
||||||
} else if (likeness<0) {
|
} else if (likeness < 0) {
|
||||||
nodep->branchPred(AstBranchPred::BP_UNLIKELY);
|
nodep->branchPred(VBranchPred::BP_UNLIKELY);
|
||||||
} // else leave unknown
|
} // else leave unknown
|
||||||
}
|
}
|
||||||
m_likely = lastLikely;
|
m_likely = lastLikely;
|
||||||
m_unlikely = lastUnlikely;
|
m_unlikely = lastUnlikely;
|
||||||
}
|
}
|
||||||
virtual void visit(AstCCall* nodep) {
|
virtual void visit(AstCCall* nodep) {
|
||||||
checkUnlikely(nodep);
|
checkUnlikely(nodep);
|
||||||
nodep->funcp()->user1Inc();
|
nodep->funcp()->user1Inc();
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstCFunc* nodep) {
|
virtual void visit(AstCFunc* nodep) {
|
||||||
checkUnlikely(nodep);
|
checkUnlikely(nodep);
|
||||||
m_cfuncsp.push_back(nodep);
|
m_cfuncsp.push_back(nodep);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
checkUnlikely(nodep);
|
checkUnlikely(nodep);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void calc_tasks() {
|
void calc_tasks() {
|
||||||
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
|
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
|
||||||
AstCFunc* nodep = *it;
|
AstCFunc* nodep = *it;
|
||||||
if (!nodep->dontInline()) {
|
if (!nodep->dontInline()) {
|
||||||
nodep->isInline(true);
|
nodep->isInline(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit BranchVisitor(AstNetlist* nodep) {
|
explicit BranchVisitor(AstNetlist* nodep) {
|
||||||
reset();
|
reset();
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
calc_tasks();
|
calc_tasks();
|
||||||
}
|
}
|
||||||
virtual ~BranchVisitor() {}
|
virtual ~BranchVisitor() {}
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3BRANCH_H_
|
#ifndef _V3BRANCH_H_
|
||||||
#define _V3BRANCH_H_ 1
|
#define _V3BRANCH_H_ 1
|
||||||
|
|
||||||
@@ -35,4 +35,4 @@ public:
|
|||||||
static void branchAll(AstNetlist* nodep);
|
static void branchAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+145
-133
@@ -20,11 +20,11 @@
|
|||||||
// V3Broken's Transformations:
|
// V3Broken's Transformations:
|
||||||
//
|
//
|
||||||
// Entire netlist
|
// Entire netlist
|
||||||
// Mark all nodes
|
// Mark all nodes
|
||||||
// Check all links point to marked nodes
|
// Check all links point to marked nodes
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -47,128 +47,139 @@ private:
|
|||||||
// MEMBERS
|
// MEMBERS
|
||||||
// For each node, we keep if it exists or not.
|
// For each node, we keep if it exists or not.
|
||||||
typedef vl_unordered_map<const AstNode*,int> NodeMap; // Performance matters (when --debug)
|
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
|
// BITMASK
|
||||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||||
public:
|
public:
|
||||||
// METHODS
|
// METHODS
|
||||||
static void deleted(const AstNode* nodep) {
|
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;
|
if (debug()>=9) cout<<"-nodeDel: "<<cvtToHex(nodep)<<endl;
|
||||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
|
UASSERT_OBJ(!(iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
|
||||||
reinterpret_cast<const AstNode*>(nodep)
|
reinterpret_cast<const AstNode*>(nodep),
|
||||||
->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted");
|
"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
|
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
|
||||||
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
|
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
|
||||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||||
#endif
|
#endif
|
||||||
static void addNewed(const AstNode* nodep) {
|
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;
|
if (debug()>=9) cout<<"-nodeNew: "<<cvtToHex(nodep)<<endl;
|
||||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
if (iter!=s_nodes.end() || (iter->second & FLAG_ALLOCATED)) {
|
UASSERT_OBJ(!(iter !=s_nodes.end() && (iter->second & FLAG_ALLOCATED)),
|
||||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
nodep, "Newing AstNode object that is already allocated");
|
||||||
}
|
if (iter == s_nodes.end()) {
|
||||||
if (iter == s_nodes.end()) {
|
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
s_nodes.insert(make_pair(nodep, flags));
|
||||||
s_nodes.insert(make_pair(nodep,flags));
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
static void setUnder(const AstNode* nodep, bool flag) {
|
static void setUnder(const AstNode* nodep, bool flag) {
|
||||||
// Called by BrokenCheckVisitor when each node entered/exited
|
// Called by BrokenCheckVisitor when each node entered/exited
|
||||||
if (!okIfLinkedTo(nodep)) return;
|
if (!okIfLinkedTo(nodep)) return;
|
||||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
if (iter!=s_nodes.end()) {
|
if (iter!=s_nodes.end()) {
|
||||||
iter->second &= ~FLAG_UNDER_NOW;
|
iter->second &= ~FLAG_UNDER_NOW;
|
||||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void addInTree(AstNode* nodep, bool linkable) {
|
static void addInTree(AstNode* nodep, bool linkable) {
|
||||||
#ifndef VL_LEAK_CHECKS
|
#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
|
#endif
|
||||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
if (iter == s_nodes.end()) {
|
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
|
||||||
#ifdef VL_LEAK_CHECKS
|
#ifdef VL_LEAK_CHECKS
|
||||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
if (!(iter->second & FLAG_ALLOCATED)) {
|
|
||||||
#ifdef VL_LEAK_CHECKS
|
#ifdef VL_LEAK_CHECKS
|
||||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
|
||||||
|
"AstNode is in tree, but not allocated");
|
||||||
#endif
|
#endif
|
||||||
}
|
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
|
||||||
if (iter->second & FLAG_IN_TREE) {
|
"AstNode is already in tree at another location");
|
||||||
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()) {
|
||||||
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
|
s_nodes.insert(make_pair(nodep, or_flags));
|
||||||
if (iter == s_nodes.end()) {
|
} else {
|
||||||
s_nodes.insert(make_pair(nodep,or_flags));
|
iter->second |= 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) {
|
static bool okIfLinkedTo(const AstNode* nodep) {
|
||||||
// Someone has a pointer to this node. Is it kosher?
|
// Some node in tree has a pointer to this node. Is it kosher?
|
||||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
if (iter == s_nodes.end()) return false;
|
if (iter == s_nodes.end()) return false;
|
||||||
#ifdef VL_LEAK_CHECKS
|
#ifdef VL_LEAK_CHECKS
|
||||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||||
#endif
|
#endif
|
||||||
if (!(iter->second & FLAG_IN_TREE)) return false;
|
if (!(iter->second & FLAG_IN_TREE)) return false;
|
||||||
if (!(iter->second & FLAG_LINKABLE)) return false;
|
if (!(iter->second & FLAG_LINKABLE)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static bool okIfBelow(const AstNode* nodep) {
|
static bool okIfBelow(const AstNode* nodep) {
|
||||||
// Must be linked to and below current node
|
// Must be linked to and below current node
|
||||||
if (!okIfLinkedTo(nodep)) return false;
|
if (!okIfLinkedTo(nodep)) return false;
|
||||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||||
if (iter == s_nodes.end()) return false;
|
if (iter == s_nodes.end()) return false;
|
||||||
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static void prepForTree() {
|
static void prepForTree() {
|
||||||
#ifndef VL_LEAK_CHECKS
|
#ifndef VL_LEAK_CHECKS
|
||||||
s_nodes.clear();
|
s_nodes.clear();
|
||||||
#endif
|
#endif
|
||||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||||
it->second &= ~FLAG_IN_TREE;
|
it->second &= ~FLAG_IN_TREE;
|
||||||
it->second &= ~FLAG_LINKABLE;
|
it->second &= ~FLAG_LINKABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void doneWithTree() {
|
static void doneWithTree() {
|
||||||
for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without
|
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) {
|
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||||
if ((it->second & FLAG_ALLOCATED)
|
if ((it->second & FLAG_ALLOCATED)
|
||||||
&& !(it->second & FLAG_IN_TREE)
|
&& !(it->second & FLAG_IN_TREE)
|
||||||
&& !(it->second & FLAG_LEAKED)
|
&& !(it->second & FLAG_LEAKED)
|
||||||
&& (it->first->backp() ? backs==1 : backs==0)) {
|
&& (it->first->backp() ? backs==1 : backs==0)) {
|
||||||
// Use only AstNode::dump instead of the virtual one, as there
|
// Use only AstNode::dump instead of the virtual one, as there
|
||||||
// may be varp() and other cross links that are bad.
|
// may be varp() and other cross links that are bad.
|
||||||
if (v3Global.opt.debugCheck()) {
|
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: ":": ");
|
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||||
AstNode* rawp = const_cast<AstNode*>
|
AstNode* rawp = const_cast<AstNode*>
|
||||||
(static_cast<const AstNode*>(it->first));
|
(static_cast<const AstNode*>(it->first));
|
||||||
rawp->AstNode::dump(std::cerr);
|
rawp->AstNode::dump(std::cerr);
|
||||||
std::cerr<<endl;
|
std::cerr<<endl;
|
||||||
V3Error::incErrors();
|
V3Error::incErrors();
|
||||||
}
|
}
|
||||||
it->second |= FLAG_LEAKED;
|
it->second |= FLAG_LEAKED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
BrokenTable() {}
|
BrokenTable() {}
|
||||||
virtual ~BrokenTable() {}
|
virtual ~BrokenTable() {}
|
||||||
};
|
};
|
||||||
@@ -190,19 +201,19 @@ class BrokenMarkVisitor : public AstNVisitor {
|
|||||||
// Mark every node in the tree
|
// Mark every node in the tree
|
||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Nothing! // This may be called deep inside other routines
|
// Nothing! // This may be called deep inside other routines
|
||||||
// // so userp and friends may not be used
|
// // so userp and friends may not be used
|
||||||
// METHODS
|
// METHODS
|
||||||
void processAndIterate(AstNode* nodep) {
|
void processAndIterate(AstNode* nodep) {
|
||||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
}
|
}
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
processAndIterate(nodep);
|
processAndIterate(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit BrokenMarkVisitor(AstNetlist* nodep) {
|
explicit BrokenMarkVisitor(AstNetlist* nodep) {
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
@@ -215,52 +226,50 @@ public:
|
|||||||
class BrokenCheckVisitor : public AstNVisitor {
|
class BrokenCheckVisitor : public AstNVisitor {
|
||||||
private:
|
private:
|
||||||
void checkWidthMin(const AstNode* nodep) {
|
void checkWidthMin(const AstNode* nodep) {
|
||||||
if (nodep->width() != nodep->widthMin()
|
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||||
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
|
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||||
nodep->v3fatalSrc("Width != WidthMin");
|
nodep, "Width != WidthMin");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void processAndIterate(AstNode* nodep) {
|
void processAndIterate(AstNode* nodep) {
|
||||||
BrokenTable::setUnder(nodep,true);
|
BrokenTable::setUnder(nodep, true);
|
||||||
if (const char* whyp=nodep->broken()) {
|
const char* whyp = nodep->broken();
|
||||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<<whyp);
|
UASSERT_OBJ(!whyp, nodep,
|
||||||
}
|
"Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||||
if (nodep->dtypep()) {
|
if (nodep->dtypep()) {
|
||||||
if (!nodep->dtypep()->brokeExists()) {
|
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
|
||||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "
|
"Broken link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
|
||||||
<<cvtToHex(nodep->dtypep()));
|
UASSERT_OBJ(VN_IS(nodep->dtypep(), NodeDType), nodep,
|
||||||
} else if (!VN_IS(nodep->dtypep(), NodeDType)) {
|
"Non-dtype link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
|
||||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "
|
}
|
||||||
<<cvtToHex(nodep->dtypep()));
|
if (v3Global.assertDTypesResolved()) {
|
||||||
|
if (nodep->hasDType()) {
|
||||||
|
UASSERT_OBJ(nodep->dtypep(), nodep,
|
||||||
|
"No dtype on node with hasDType(): "<<nodep->prettyTypeName());
|
||||||
|
} else {
|
||||||
|
UASSERT_OBJ(!nodep->dtypep(), nodep,
|
||||||
|
"DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||||
}
|
}
|
||||||
}
|
UASSERT_OBJ(!nodep->getChildDTypep(), nodep,
|
||||||
if (v3Global.assertDTypesResolved()) {
|
"childDTypep() non-null on node after should have removed");
|
||||||
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);
|
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||||
}
|
}
|
||||||
checkWidthMin(nodep);
|
checkWidthMin(nodep);
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
BrokenTable::setUnder(nodep,false);
|
BrokenTable::setUnder(nodep, false);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep) {
|
virtual void visit(AstNodeAssign* nodep) {
|
||||||
processAndIterate(nodep);
|
processAndIterate(nodep);
|
||||||
if (v3Global.assertDTypesResolved()
|
UASSERT_OBJ(!(v3Global.assertDTypesResolved()
|
||||||
&& nodep->brokeLhsMustBeLvalue()
|
&& nodep->brokeLhsMustBeLvalue()
|
||||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
|
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()),
|
||||||
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
|
nodep, "Assignment LHS is not an lvalue");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
processAndIterate(nodep);
|
processAndIterate(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit BrokenCheckVisitor(AstNetlist* nodep) {
|
explicit BrokenCheckVisitor(AstNetlist* nodep) {
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
@@ -273,16 +282,16 @@ public:
|
|||||||
void V3Broken::brokenAll(AstNetlist* nodep) {
|
void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||||
//UINFO(9,__FUNCTION__<<": "<<endl);
|
//UINFO(9,__FUNCTION__<<": "<<endl);
|
||||||
static bool inBroken = false;
|
static bool inBroken = false;
|
||||||
if (inBroken) {
|
if (VL_UNCOVERABLE(inBroken)) {
|
||||||
// A error called by broken can recurse back into broken; avoid this
|
// A error called by broken can recurse back into broken; avoid this
|
||||||
UINFO(1,"Broken called under broken, skipping recursion.\n");
|
UINFO(1,"Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
|
||||||
} else {
|
} else {
|
||||||
inBroken = true;
|
inBroken = true;
|
||||||
BrokenTable::prepForTree();
|
BrokenTable::prepForTree();
|
||||||
BrokenMarkVisitor mvisitor (nodep);
|
BrokenMarkVisitor mvisitor (nodep);
|
||||||
BrokenCheckVisitor cvisitor (nodep);
|
BrokenCheckVisitor cvisitor (nodep);
|
||||||
BrokenTable::doneWithTree();
|
BrokenTable::doneWithTree();
|
||||||
inBroken = false;
|
inBroken = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,3 +301,6 @@ void V3Broken::addNewed(AstNode* nodep) {
|
|||||||
void V3Broken::deleted(AstNode* nodep) {
|
void V3Broken::deleted(AstNode* nodep) {
|
||||||
BrokenTable::deleted(nodep);
|
BrokenTable::deleted(nodep);
|
||||||
}
|
}
|
||||||
|
bool V3Broken::isAllocated(AstNode* nodep) {
|
||||||
|
return BrokenTable::isAllocated(nodep);
|
||||||
|
}
|
||||||
|
|||||||
+3
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3BROKEN_H_
|
#ifndef _V3BROKEN_H_
|
||||||
#define _V3BROKEN_H_ 1
|
#define _V3BROKEN_H_ 1
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ public:
|
|||||||
static void brokenAll(AstNetlist* nodep);
|
static void brokenAll(AstNetlist* nodep);
|
||||||
static void addNewed(AstNode* nodep);
|
static void addNewed(AstNode* nodep);
|
||||||
static void deleted(AstNode* nodep);
|
static void deleted(AstNode* nodep);
|
||||||
|
static bool isAllocated(AstNode* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+80
-75
@@ -18,14 +18,14 @@
|
|||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// V3CCtors's Transformations:
|
// V3CCtors's Transformations:
|
||||||
// Iterates over all modules and
|
// Iterates over all modules and
|
||||||
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
|
// 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 all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
|
||||||
// For each variable that needs reset, add a AstCReset node.
|
// 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"
|
#include "config_build.h"
|
||||||
@@ -43,59 +43,60 @@
|
|||||||
|
|
||||||
class V3CCtorsVisitor {
|
class V3CCtorsVisitor {
|
||||||
private:
|
private:
|
||||||
string m_basename;
|
string m_basename;
|
||||||
string m_argsp;
|
string m_argsp;
|
||||||
string m_callargsp;
|
string m_callargsp;
|
||||||
AstNodeModule* m_modp; // Current module
|
AstNodeModule* m_modp; // Current module
|
||||||
AstCFunc* m_tlFuncp; // Top level function being built
|
AstCFunc* m_tlFuncp; // Top level function being built
|
||||||
AstCFunc* m_funcp; // Current function
|
AstCFunc* m_funcp; // Current function
|
||||||
int m_numStmts; // Number of statements output
|
int m_numStmts; // Number of statements output
|
||||||
int m_funcNum; // Function number being built
|
int m_funcNum; // Function number being built
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void add(AstNode* nodep) {
|
void add(AstNode* nodep) {
|
||||||
if (v3Global.opt.outputSplitCFuncs()
|
if (v3Global.opt.outputSplitCFuncs()
|
||||||
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||||
m_funcp = NULL;
|
m_funcp = NULL;
|
||||||
}
|
}
|
||||||
if (!m_funcp) {
|
if (!m_funcp) {
|
||||||
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum), NULL, "void");
|
m_funcp = new AstCFunc(m_modp->fileline(),
|
||||||
m_funcp->isStatic(false);
|
m_basename+"_"+cvtToStr(++m_funcNum), NULL, "void");
|
||||||
m_funcp->declPrivate(true);
|
m_funcp->isStatic(false);
|
||||||
m_funcp->slow(true);
|
m_funcp->declPrivate(true);
|
||||||
m_funcp->argTypes(m_argsp);
|
m_funcp->slow(true);
|
||||||
m_modp->addStmtp(m_funcp);
|
m_funcp->argTypes(m_argsp);
|
||||||
|
m_modp->addStmtp(m_funcp);
|
||||||
|
|
||||||
// Add a top call to it
|
// Add a top call to it
|
||||||
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
|
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
|
||||||
callp->argTypes(m_callargsp);
|
callp->argTypes(m_callargsp);
|
||||||
|
|
||||||
m_tlFuncp->addStmtsp(callp);
|
m_tlFuncp->addStmtsp(callp);
|
||||||
m_numStmts = 0;
|
m_numStmts = 0;
|
||||||
}
|
}
|
||||||
m_funcp->addStmtsp(nodep);
|
m_funcp->addStmtsp(nodep);
|
||||||
m_numStmts += 1;
|
m_numStmts += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename,
|
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename,
|
||||||
const string& argsp="", const string& callargsp="",
|
const string& argsp="", const string& callargsp="",
|
||||||
const string& stmt="") {
|
const string& stmt="") {
|
||||||
m_basename = basename;
|
m_basename = basename;
|
||||||
m_argsp = argsp;
|
m_argsp = argsp;
|
||||||
m_callargsp = callargsp;
|
m_callargsp = callargsp;
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_numStmts = 0;
|
m_numStmts = 0;
|
||||||
m_funcNum = 0;
|
m_funcNum = 0;
|
||||||
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
|
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
|
||||||
m_tlFuncp->declPrivate(true);
|
m_tlFuncp->declPrivate(true);
|
||||||
m_tlFuncp->isStatic(false);
|
m_tlFuncp->isStatic(false);
|
||||||
m_tlFuncp->slow(true);
|
m_tlFuncp->slow(true);
|
||||||
m_tlFuncp->argTypes(m_argsp);
|
m_tlFuncp->argTypes(m_argsp);
|
||||||
if (stmt != "") {
|
if (stmt != "") {
|
||||||
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||||
}
|
}
|
||||||
m_funcp = m_tlFuncp;
|
m_funcp = m_tlFuncp;
|
||||||
m_modp->addStmtp(m_tlFuncp);
|
m_modp->addStmtp(m_tlFuncp);
|
||||||
}
|
}
|
||||||
~V3CCtorsVisitor() {}
|
~V3CCtorsVisitor() {}
|
||||||
private:
|
private:
|
||||||
@@ -105,7 +106,7 @@ private:
|
|||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
||||||
void V3CCtors::evalAsserts() {
|
void V3CCtors::evalAsserts() {
|
||||||
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module
|
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
||||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", NULL, "void");
|
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", NULL, "void");
|
||||||
funcp->declPrivate(true);
|
funcp->declPrivate(true);
|
||||||
funcp->isStatic(false);
|
funcp->isStatic(false);
|
||||||
@@ -116,36 +117,38 @@ void V3CCtors::evalAsserts() {
|
|||||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||||
if (varp->isPrimaryInish() && !varp->isSc()) {
|
if (varp->isPrimaryInish() && !varp->isSc()) {
|
||||||
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||||
int lastWordWidth = varp->width() % storedWidth;
|
int lastWordWidth = varp->width() % storedWidth;
|
||||||
if (lastWordWidth != 0) {
|
if (lastWordWidth != 0) {
|
||||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||||
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
|
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
|
||||||
if (varp->isWide()) {
|
if (varp->isWide()) {
|
||||||
newp = new AstWordSel(varp->fileline(), newp,
|
newp = new AstWordSel(
|
||||||
new AstConst(varp->fileline(), varp->widthWords()-1));
|
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);
|
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||||
newp = new AstAnd(varp->fileline(), newp,
|
newp = new AstAnd(varp->fileline(), newp,
|
||||||
new AstConst(varp->fileline(), num));
|
new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
||||||
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
|
storedWidth, value));
|
||||||
|
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
|
||||||
new AstCStmt(varp->fileline(),
|
new AstCStmt(varp->fileline(),
|
||||||
"Verilated::overWidthError(\""+varp->prettyName()+"\");"));
|
"Verilated::overWidthError(\""+varp->prettyName()+"\");"));
|
||||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||||
newp = ifp;
|
newp = ifp;
|
||||||
funcp->addStmtsp(newp);
|
funcp->addStmtsp(newp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void V3CCtors::cctorsAll() {
|
void V3CCtors::cctorsAll() {
|
||||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||||
evalAsserts();
|
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
|
// Process each module in turn
|
||||||
{
|
{
|
||||||
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
|
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
|
||||||
@@ -153,14 +156,16 @@ void V3CCtors::cctorsAll() {
|
|||||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||||
if (!varp->isIfaceParent() && !varp->isIfaceRef()
|
if (!varp->isIfaceParent() && !varp->isIfaceRef()
|
||||||
&& !varp->noReset()) {
|
&& !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()) {
|
if (v3Global.opt.coverage()) {
|
||||||
V3CCtorsVisitor configure_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");
|
"if (0 && vlSymsp && first) {} // Prevent unused\n");
|
||||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CCTORS_H_
|
#ifndef _V3CCTORS_H_
|
||||||
#define _V3CCTORS_H_ 1
|
#define _V3CCTORS_H_ 1
|
||||||
|
|
||||||
@@ -37,4 +37,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+345
-335
@@ -20,23 +20,24 @@
|
|||||||
// V3Case's Transformations:
|
// V3Case's Transformations:
|
||||||
//
|
//
|
||||||
// Each module:
|
// Each module:
|
||||||
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
|
// 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
|
// Need __en in changed list if a signal is on the LHS of a assign
|
||||||
// Cases:
|
// Cases:
|
||||||
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
|
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
|
||||||
// (AND item1 mask))
|
// (AND item1 mask))
|
||||||
// (other items))
|
// (other items))
|
||||||
// body
|
// body
|
||||||
// Or, converts to a if/else tree.
|
// Or, converts to a if/else tree.
|
||||||
// FUTURES:
|
// FUTURES:
|
||||||
// Large 16+ bit tables with constants and no masking (address muxes)
|
// 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.
|
// 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?)
|
// 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.)
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -48,68 +49,70 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
||||||
#define CASE_BARF 999999 // Magic width when non-constant
|
#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_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
||||||
class CaseLintVisitor : public AstNVisitor {
|
class CaseLintVisitor : public AstNVisitor {
|
||||||
private:
|
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
|
// METHODS
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
virtual void visit(AstNodeCase* nodep) {
|
virtual void visit(AstNodeCase* nodep) {
|
||||||
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
|
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
|
||||||
nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)");
|
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
|
||||||
}
|
}
|
||||||
// Detect multiple defaults
|
// Detect multiple defaults
|
||||||
bool hitDefault = false;
|
bool hitDefault = false;
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
for (AstCaseItem* itemp = nodep->itemsp();
|
||||||
if (itemp->isDefault()) {
|
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||||
if (hitDefault) {
|
if (itemp->isDefault()) {
|
||||||
nodep->v3error("Multiple default statements in case statement.");
|
if (hitDefault) {
|
||||||
}
|
itemp->v3error("Multiple default statements in case statement.");
|
||||||
hitDefault = true;
|
}
|
||||||
}
|
hitDefault = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for X/Z in non-casex statements
|
// Check for X/Z in non-casex statements
|
||||||
{
|
{
|
||||||
m_caseExprp = nodep;
|
m_caseExprp = nodep;
|
||||||
iterate(nodep->exprp());
|
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());
|
iterateAndNextNull(itemp->condsp());
|
||||||
}
|
}
|
||||||
m_caseExprp = NULL;
|
m_caseExprp = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstConst* nodep) {
|
virtual void visit(AstConst* nodep) {
|
||||||
// See also neverItem
|
// See also neverItem
|
||||||
if (m_caseExprp && nodep->num().isFourState()) {
|
if (m_caseExprp && nodep->num().isFourState()) {
|
||||||
if (VN_IS(m_caseExprp, GenCase)) {
|
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()) {
|
} 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()
|
} else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez()
|
||||||
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
||||||
if (nodep->num().isUnknown()) {
|
if (nodep->num().isUnknown()) {
|
||||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
|
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit CaseLintVisitor(AstNodeCase* nodep) {
|
explicit CaseLintVisitor(AstNodeCase* nodep) {
|
||||||
m_caseExprp = NULL;
|
m_caseExprp = NULL;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~CaseLintVisitor() {}
|
virtual ~CaseLintVisitor() {}
|
||||||
@@ -122,340 +125,347 @@ class CaseVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Cleared each Case
|
// Cleared each Case
|
||||||
// AstIf::user3() -> bool. Set true to indicate clone not needed
|
// AstIf::user3() -> bool. Set true to indicate clone not needed
|
||||||
AstUser3InUse m_inuser3;
|
AstUser3InUse m_inuser3;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
V3Double0 m_statCaseFast; // Statistic tracking
|
VDouble0 m_statCaseFast; // Statistic tracking
|
||||||
V3Double0 m_statCaseSlow; // Statistic tracking
|
VDouble0 m_statCaseSlow; // Statistic tracking
|
||||||
|
|
||||||
// Per-CASE
|
// Per-CASE
|
||||||
int m_caseWidth; // Width of valueItems
|
int m_caseWidth; // Width of valueItems
|
||||||
int m_caseItems; // Number of caseItem unique values
|
int m_caseItems; // Number of caseItem unique values
|
||||||
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
|
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
|
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
bool isCaseTreeFast(AstCase* nodep) {
|
bool isCaseTreeFast(AstCase* nodep) {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
bool opaque = false;
|
bool opaque = false;
|
||||||
m_caseItems = 0;
|
m_caseItems = 0;
|
||||||
m_caseNoOverlapsAllCovered = true;
|
m_caseNoOverlapsAllCovered = true;
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
for (AstCaseItem* itemp = nodep->itemsp();
|
||||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||||
if (icondp->width() > width) width = icondp->width();
|
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||||
if (icondp->isDouble()) opaque = true;
|
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
|
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
|
||||||
m_caseItems++;
|
m_caseItems++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_caseWidth = width;
|
m_caseWidth = width;
|
||||||
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
|
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
|
||||||
m_caseNoOverlapsAllCovered = false;
|
m_caseNoOverlapsAllCovered = false;
|
||||||
return false; // Too wide for analysis
|
return false; // Too wide for analysis
|
||||||
}
|
}
|
||||||
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
||||||
// Zero list of items for each value
|
// Zero list of items for each value
|
||||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) m_valueItem[i] = NULL;
|
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) m_valueItem[i] = NULL;
|
||||||
// Now pick up the values for each assignment
|
// Now pick up the values for each assignment
|
||||||
// We can cheat and use uint32_t's because we only support narrow case's
|
// We can cheat and use uint32_t's because we only support narrow case's
|
||||||
bool bitched = false;
|
bool bitched = false;
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
for (AstCaseItem* itemp = nodep->itemsp();
|
||||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||||
//if (debug()>=9) icondp->dumpTree(cout," caseitem: ");
|
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||||
|
//if (debug()>=9) icondp->dumpTree(cout, " caseitem: ");
|
||||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||||
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
|
UASSERT_OBJ(iconstp, nodep, "above 'can't parse' should have caught this");
|
||||||
if (neverItem(nodep, iconstp)) {
|
if (neverItem(nodep, iconstp)) {
|
||||||
// X in casez can't ever be executed
|
// X in casez can't ever be executed
|
||||||
} else {
|
} else {
|
||||||
V3Number nummask (itemp->fileline(), iconstp->width());
|
V3Number nummask (itemp, iconstp->width());
|
||||||
nummask.opBitsNonX(iconstp->num());
|
nummask.opBitsNonX(iconstp->num());
|
||||||
uint32_t mask = nummask.toUInt();
|
uint32_t mask = nummask.toUInt();
|
||||||
V3Number numval (itemp->fileline(), iconstp->width());
|
V3Number numval (itemp, iconstp->width());
|
||||||
numval.opBitsOne(iconstp->num());
|
numval.opBitsOne(iconstp->num());
|
||||||
uint32_t val = numval.toUInt();
|
uint32_t val = numval.toUInt();
|
||||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||||
if ((i & mask) == val) {
|
if ((i & mask) == val) {
|
||||||
if (!m_valueItem[i]) {
|
if (!m_valueItem[i]) {
|
||||||
m_valueItem[i] = itemp;
|
m_valueItem[i] = itemp;
|
||||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||||
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<std::hex<<i<<")");
|
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||||
bitched = true;
|
<<std::hex<<i<<")");
|
||||||
m_caseNoOverlapsAllCovered = false;
|
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
|
// 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) {
|
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) {
|
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||||
if (!m_valueItem[i]) {
|
if (!m_valueItem[i]) {
|
||||||
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<std::hex<<i<<")");
|
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered (example pattern 0x"
|
||||||
m_caseNoOverlapsAllCovered = false;
|
<<std::hex<<i<<")");
|
||||||
return false;
|
m_caseNoOverlapsAllCovered = false;
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
if (m_caseItems <= 3) return false; // Not worth simplifing
|
}
|
||||||
// Convert valueItem from AstCaseItem* to the expression
|
if (m_caseItems <= 3) return false; // Not worth simplifying
|
||||||
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
|
// 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) {
|
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||||
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
|
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) {
|
AstNode* replaceCaseFastRecurse(AstNode* cexprp, int msb, uint32_t upperValue) {
|
||||||
if (msb<0) {
|
if (msb<0) {
|
||||||
// There's no space for a IF. We know upperValue is thus down to a specific
|
// There's no space for a IF. We know upperValue is thus down to a specific
|
||||||
// exact value, so just return the tree value
|
// exact value, so just return the tree value
|
||||||
// Note can't clone here, as we're going to check for equivelence above
|
// Note can't clone here, as we're going to check for equivalence above
|
||||||
return m_valueItem[upperValue];
|
return m_valueItem[upperValue];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Make left and right subtrees
|
// Make left and right subtrees
|
||||||
// cexpr[msb:lsb] == 1
|
// cexpr[msb:lsb] == 1
|
||||||
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
|
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
|
||||||
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
|
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
|
||||||
|
|
||||||
if (tree0p == tree1p) {
|
if (tree0p == tree1p) {
|
||||||
// Same logic on both sides, so we can just return one of 'em
|
// Same logic on both sides, so we can just return one of 'em
|
||||||
return tree0p;
|
return tree0p;
|
||||||
}
|
}
|
||||||
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
|
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
|
||||||
bool same = true;
|
bool same = true;
|
||||||
for (uint32_t a=upperValue,
|
for (uint32_t a=upperValue,
|
||||||
b=(upperValue|(1UL<<msb));
|
b=(upperValue|(1UL<<msb));
|
||||||
a < (upperValue|(1UL<<msb));
|
a < (upperValue|(1UL<<msb));
|
||||||
a++, b++) {
|
a++, b++) {
|
||||||
if (m_valueItem[a] != m_valueItem[b]) { same=false; break; }
|
if (m_valueItem[a] != m_valueItem[b]) { same = false; break; }
|
||||||
}
|
}
|
||||||
if (same) {
|
if (same) {
|
||||||
tree1p->deleteTree(); VL_DANGLING(tree1p);
|
tree1p->deleteTree(); VL_DANGLING(tree1p);
|
||||||
return tree0p;
|
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
|
// Case expressions can't be linked twice, so clone them
|
||||||
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
|
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
|
||||||
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
|
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
|
||||||
|
|
||||||
// Alternate scheme if we ever do multiple bits at a time:
|
// Alternate scheme if we ever do multiple bits at a time:
|
||||||
//V3Number nummask (cexprp->fileline(), cexprp->width(), (1UL<<msb));
|
//V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
|
||||||
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||||
// new AstConst(cexprp->fileline(), nummask));
|
// new AstConst(cexprp->fileline(), nummask));
|
||||||
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
|
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
|
||||||
msb, 1);
|
msb, 1);
|
||||||
AstNode* eqp = new AstNeq(cexprp->fileline(),
|
AstNode* eqp = new AstNeq(cexprp->fileline(),
|
||||||
new AstConst(cexprp->fileline(), 0),
|
new AstConst(cexprp->fileline(), 0),
|
||||||
and1p);
|
and1p);
|
||||||
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
||||||
ifp->user3(1); // So we don't bother to clone it
|
ifp->user3(1); // So we don't bother to clone it
|
||||||
return ifp;
|
return ifp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void replaceCaseFast(AstCase* nodep) {
|
void replaceCaseFast(AstCase* nodep) {
|
||||||
// CASEx(cexpr,....
|
// CASEx(cexpr,....
|
||||||
// -> tree of IF(msb, IF(msb-1, 11, 10)
|
// -> tree of IF(msb, IF(msb-1, 11, 10)
|
||||||
// IF(msb-1, 01, 00))
|
// IF(msb-1, 01, 00))
|
||||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||||
|
|
||||||
if (debug()>=9) {
|
if (debug()>=9) {
|
||||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
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);
|
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle any assertions
|
// Handle any assertions
|
||||||
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
|
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
|
||||||
|
|
||||||
AstNode::user3ClearTree();
|
AstNode::user3ClearTree();
|
||||||
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
|
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
|
||||||
// Case expressions can't be linked twice, so clone them
|
// Case expressions can't be linked twice, so clone them
|
||||||
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
|
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
|
||||||
|
|
||||||
if (ifrootp) nodep->replaceWith(ifrootp);
|
if (ifrootp) nodep->replaceWith(ifrootp);
|
||||||
else nodep->unlinkFrBack();
|
else nodep->unlinkFrBack();
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
||||||
if (debug()>=9) ifrootp->dumpTree(cout," _simp: ");
|
if (debug()>=9) ifrootp->dumpTree(cout, " _simp: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
void replaceCaseComplicated(AstCase* nodep) {
|
void replaceCaseComplicated(AstCase* nodep) {
|
||||||
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
|
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
|
||||||
// -> IF((cexpr==icond1),istmts1,
|
// -> IF((cexpr==icond1),istmts1,
|
||||||
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
|
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
|
||||||
// ,istmts2, istmts3
|
// ,istmts2, istmts3
|
||||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||||
// We'll do this in two stages. First stage, convert the conditions to
|
// We'll do this in two stages. First stage, convert the conditions to
|
||||||
// the appropriate IF AND terms.
|
// the appropriate IF AND terms.
|
||||||
if (debug()>=9) nodep->dumpTree(cout," _comp_IN: ");
|
if (debug()>=9) nodep->dumpTree(cout, " _comp_IN: ");
|
||||||
bool hadDefault = false;
|
bool hadDefault = false;
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||||
if (!itemp->condsp()) {
|
if (!itemp->condsp()) {
|
||||||
// Default clause. Just make true, we'll optimize it away later
|
// Default clause. Just make true, we'll optimize it away later
|
||||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
|
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
|
||||||
hadDefault = true;
|
hadDefault = true;
|
||||||
} else {
|
} else {
|
||||||
// Expressioned clause
|
// Expressioned clause
|
||||||
AstNode* icondNextp = NULL;
|
AstNode* icondNextp = NULL;
|
||||||
AstNode* ifexprp = NULL; // If expression to test
|
AstNode* ifexprp = NULL; // If expression to test
|
||||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
|
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
|
||||||
icondNextp = icondp->nextp();
|
icondNextp = icondp->nextp();
|
||||||
icondp->unlinkFrBack();
|
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);
|
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||||
if (iconstp && neverItem(nodep, iconstp)) {
|
if (iconstp && neverItem(nodep, iconstp)) {
|
||||||
// X in casez can't ever be executed
|
// X in casez can't ever be executed
|
||||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||||
// For simplicity, make expression that is not equal, and let later
|
// For simplicity, make expression that is not equal, and let later
|
||||||
// optimizations remove it
|
// optimizations remove it
|
||||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||||
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
|
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
|
||||||
// Similar logic in V3Width::visit(AstInside)
|
// Similar logic in V3Width::visit(AstInside)
|
||||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||||
cexprp->cloneTree(false),
|
cexprp->cloneTree(false),
|
||||||
irangep->lhsp()->unlinkFrBack());
|
irangep->lhsp()->unlinkFrBack());
|
||||||
AstNode* bp = AstLte::newTyped(itemp->fileline(),
|
AstNode* bp = AstLte::newTyped(itemp->fileline(),
|
||||||
cexprp->cloneTree(false),
|
cexprp->cloneTree(false),
|
||||||
irangep->rhsp()->unlinkFrBack());
|
irangep->rhsp()->unlinkFrBack());
|
||||||
condp = new AstAnd(itemp->fileline(), ap, bp);
|
condp = new AstAnd(itemp->fileline(), ap, bp);
|
||||||
} else if (iconstp && iconstp->num().isFourState()
|
} else if (iconstp && iconstp->num().isFourState()
|
||||||
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
||||||
V3Number nummask (itemp->fileline(), iconstp->width());
|
V3Number nummask (itemp, iconstp->width());
|
||||||
nummask.opBitsNonX(iconstp->num());
|
nummask.opBitsNonX(iconstp->num());
|
||||||
V3Number numval (itemp->fileline(), iconstp->width());
|
V3Number numval (itemp, iconstp->width());
|
||||||
numval.opBitsOne(iconstp->num());
|
numval.opBitsOne(iconstp->num());
|
||||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||||
new AstConst(itemp->fileline(), nummask));
|
new AstConst(itemp->fileline(), nummask));
|
||||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||||
new AstConst(itemp->fileline(), numval),
|
new AstConst(itemp->fileline(), numval),
|
||||||
new AstConst(itemp->fileline(), nummask));
|
new AstConst(itemp->fileline(), nummask));
|
||||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||||
} else {
|
} else {
|
||||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||||
AstNode* and1p = cexprp->cloneTree(false);
|
AstNode* and1p = cexprp->cloneTree(false);
|
||||||
AstNode* and2p = icondp;
|
AstNode* and2p = icondp;
|
||||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||||
}
|
}
|
||||||
if (!ifexprp) {
|
if (!ifexprp) {
|
||||||
ifexprp = condp;
|
ifexprp = condp;
|
||||||
} else {
|
} else {
|
||||||
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
|
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Replace expression in tree
|
// Replace expression in tree
|
||||||
itemp->condsp(ifexprp);
|
itemp->condsp(ifexprp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
cexprp->deleteTree(); VL_DANGLING(cexprp);
|
||||||
if (!hadDefault) {
|
if (!hadDefault) {
|
||||||
// If there was no default, add a empty one, this greatly simplifies below code
|
// If there was no default, add a empty one, this greatly simplifies below code
|
||||||
// and constant propagation will just eliminate it for us later.
|
// and constant propagation will just eliminate it for us later.
|
||||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
|
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
|
||||||
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
|
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
|
||||||
NULL));
|
NULL));
|
||||||
}
|
}
|
||||||
if (debug()>=9) nodep->dumpTree(cout," _comp_COND: ");
|
if (debug()>=9) nodep->dumpTree(cout, " _comp_COND: ");
|
||||||
// Now build the IF statement tree
|
// Now build the IF statement tree
|
||||||
// The tree can be quite huge. Pull ever group of 8 out, and make a OR 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.
|
// This reduces the depth for the bottom elements, at the cost of
|
||||||
// If we ever have profiling data, we should pull out the most common item from here and
|
// some of the top elements. If we ever have profiling data, we
|
||||||
// instead make it the first IF branch.
|
// should pull out the most common item from here and instead make
|
||||||
int depth = 0;
|
// it the first IF branch.
|
||||||
AstNode* grouprootp = NULL;
|
int depth = 0;
|
||||||
AstIf* groupnextp = NULL;
|
AstNode* grouprootp = NULL;
|
||||||
AstIf* itemnextp = NULL;
|
AstIf* groupnextp = NULL;
|
||||||
|
AstIf* itemnextp = NULL;
|
||||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||||
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||||
// Expressioned clause
|
// Expressioned clause
|
||||||
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
|
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
|
||||||
{ // Prepare for next group
|
{ // Prepare for next group
|
||||||
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
||||||
if (depth == 1) { // First group or starting new group
|
if (depth == 1) { // First group or starting new group
|
||||||
itemnextp = NULL;
|
itemnextp = NULL;
|
||||||
AstIf* newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true), NULL, NULL);
|
AstIf* newp = new AstIf(itemp->fileline(),
|
||||||
if (groupnextp) groupnextp->addElsesp(newp);
|
ifexprp->cloneTree(true), NULL, NULL);
|
||||||
else grouprootp = newp;
|
if (groupnextp) groupnextp->addElsesp(newp);
|
||||||
groupnextp = newp;
|
else grouprootp = newp;
|
||||||
} else { // Continue group, modify if condition to OR in this new condition
|
groupnextp = newp;
|
||||||
AstNode* condp = groupnextp->condp()->unlinkFrBack();
|
} else { // Continue group, modify if condition to OR in this new condition
|
||||||
groupnextp->condp(new AstOr(ifexprp->fileline(),
|
AstNode* condp = groupnextp->condp()->unlinkFrBack();
|
||||||
condp,
|
groupnextp->condp(new AstOr(ifexprp->fileline(),
|
||||||
ifexprp->cloneTree(true)));
|
condp,
|
||||||
}
|
ifexprp->cloneTree(true)));
|
||||||
}
|
}
|
||||||
{ // Make the new lower IF and attach in the tree
|
}
|
||||||
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
|
{ // Make the new lower IF and attach in the tree
|
||||||
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
|
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
|
||||||
itemexprp->deleteTree(); VL_DANGLING(itemexprp);
|
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
|
||||||
itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
|
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);
|
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
|
||||||
else groupnextp->addIfsp(newp); // First in a new group
|
if (itemnextp) itemnextp->addElsesp(newp);
|
||||||
itemnextp = newp;
|
else groupnextp->addIfsp(newp); // First in a new group
|
||||||
}
|
itemnextp = newp;
|
||||||
}
|
}
|
||||||
if (debug()>=9) nodep->dumpTree(cout," _comp_TREE: ");
|
}
|
||||||
// Handle any assertions
|
if (debug()>=9) nodep->dumpTree(cout, " _comp_TREE: ");
|
||||||
replaceCaseParallel(nodep, false);
|
// Handle any assertions
|
||||||
// Replace the CASE... with IF...
|
replaceCaseParallel(nodep, false);
|
||||||
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout," _new: ");
|
// Replace the CASE... with IF...
|
||||||
if (grouprootp) nodep->replaceWith(grouprootp);
|
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout, " _new: ");
|
||||||
else nodep->unlinkFrBack();
|
if (grouprootp) nodep->replaceWith(grouprootp);
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
else nodep->unlinkFrBack();
|
||||||
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void replaceCaseParallel(AstCase* nodep, bool noOverlapsAllCovered) {
|
void replaceCaseParallel(AstCase* nodep, bool noOverlapsAllCovered) {
|
||||||
// Take the notParallelp tree under the case statement created by V3Assert
|
// 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.
|
// If the statement was proven to have no overlaps and all cases
|
||||||
// Else, convert to a normal statement parallel with the case statement.
|
// covered, we're done with it.
|
||||||
if (nodep->notParallelp() && !noOverlapsAllCovered) {
|
// Else, convert to a normal statement parallel with the case statement.
|
||||||
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
|
if (nodep->notParallelp() && !noOverlapsAllCovered) {
|
||||||
nodep->addNextHere(parp);
|
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
|
||||||
}
|
nodep->addNextHere(parp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool neverItem(AstCase* casep, AstConst* itemp) {
|
bool neverItem(AstCase* casep, AstConst* itemp) {
|
||||||
// Xs in case or casez are impossible due to two state simulations
|
// Xs in case or casez are impossible due to two state simulations
|
||||||
if (casep->casex()) {
|
if (casep->casex()) {
|
||||||
} else if (casep->casez() || casep->caseInside()) {
|
} else if (casep->casez() || casep->caseInside()) {
|
||||||
if (itemp->num().isUnknown()) return true;
|
if (itemp->num().isUnknown()) return true;
|
||||||
} else {
|
} else {
|
||||||
if (itemp->num().isFourState()) return true;
|
if (itemp->num().isFourState()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstCase* nodep) {
|
virtual void visit(AstCase* nodep) {
|
||||||
V3Case::caseLint(nodep);
|
V3Case::caseLint(nodep);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
|
if (debug()>=9) nodep->dumpTree(cout, " case_old: ");
|
||||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||||
// It's a simple priority encoder or complete statement
|
// It's a simple priority encoder or complete statement
|
||||||
// we can make a tree of statements to avoid extra comparisons
|
// we can make a tree of statements to avoid extra comparisons
|
||||||
++m_statCaseFast;
|
++m_statCaseFast;
|
||||||
replaceCaseFast(nodep); VL_DANGLING(nodep);
|
replaceCaseFast(nodep); VL_DANGLING(nodep);
|
||||||
} else {
|
} else {
|
||||||
++m_statCaseSlow;
|
++m_statCaseSlow;
|
||||||
replaceCaseComplicated(nodep); VL_DANGLING(nodep);
|
replaceCaseComplicated(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//--------------------
|
//--------------------
|
||||||
// Default: Just iterate
|
// Default: Just iterate
|
||||||
@@ -464,19 +474,19 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit CaseVisitor(AstNetlist* nodep) {
|
explicit CaseVisitor(AstNetlist* nodep) {
|
||||||
m_caseWidth = 0;
|
m_caseWidth = 0;
|
||||||
m_caseItems = 0;
|
m_caseItems = 0;
|
||||||
m_caseNoOverlapsAllCovered = false;
|
m_caseNoOverlapsAllCovered = false;
|
||||||
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
|
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
|
||||||
m_valueItem[i] = NULL;
|
m_valueItem[i] = NULL;
|
||||||
}
|
}
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~CaseVisitor() {
|
virtual ~CaseVisitor() {
|
||||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||||
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CASE_H_
|
#ifndef _V3CASE_H_
|
||||||
#define _V3CASE_H_ 1
|
#define _V3CASE_H_ 1
|
||||||
|
|
||||||
@@ -35,4 +35,4 @@ public:
|
|||||||
static void caseLint(AstNodeCase* nodep);
|
static void caseLint(AstNodeCase* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+74
-74
@@ -20,27 +20,27 @@
|
|||||||
// V3Cast's Transformations:
|
// V3Cast's Transformations:
|
||||||
//
|
//
|
||||||
// Each module:
|
// Each module:
|
||||||
// For each math operator, if above operator requires 32 bits,
|
// For each math operator, if above operator requires 32 bits,
|
||||||
// and this isn't, cast to 32 bits.
|
// and this isn't, cast to 32 bits.
|
||||||
// Likewise for 64 bit operators.
|
// Likewise for 64 bit operators.
|
||||||
//
|
//
|
||||||
// C++ rules:
|
// C++ rules:
|
||||||
// Integral promotions allow conversion to larger int. Unsigned is only
|
// Integral promotions allow conversion to larger int. Unsigned is only
|
||||||
// used if a int would not fit the value.
|
// 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
|
// Unsignedness can be lost on results of the below operations, as they
|
||||||
// may need the sign bit for proper operation:
|
// may need the sign bit for proper operation:
|
||||||
// /, %, /=, %=, <, <=, >, or >=
|
// /, %, /=, %=, <, <=, >, or >=
|
||||||
//
|
//
|
||||||
// Signed values are always sign extended on promotion or right shift,
|
// Signed values are always sign extended on promotion or right shift,
|
||||||
// even if assigning to a unsigned.
|
// even if assigning to a unsigned.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -58,8 +58,8 @@ class CastVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstNode::user() // bool. Indicates node is of known size
|
// AstNode::user() // bool. Indicates node is of known size
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
|
|
||||||
@@ -67,97 +67,97 @@ private:
|
|||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||||
UINFO(4," NeedCast "<<nodep<<endl);
|
UINFO(4," NeedCast "<<nodep<<endl);
|
||||||
AstNRelinker relinkHandle;
|
AstNRelinker relinkHandle;
|
||||||
nodep->unlinkFrBack(&relinkHandle);
|
nodep->unlinkFrBack(&relinkHandle);
|
||||||
//
|
//
|
||||||
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||||
relinkHandle.relink(castp);
|
relinkHandle.relink(castp);
|
||||||
//if (debug()>8) castp->dumpTree(cout,"-castins: ");
|
//if (debug()>8) castp->dumpTree(cout, "-castins: ");
|
||||||
//
|
//
|
||||||
insureLower32Cast(castp);
|
insureLower32Cast(castp);
|
||||||
nodep->user1(1); // Now must be of known size
|
nodep->user1(1); // Now must be of known size
|
||||||
}
|
}
|
||||||
int castSize(AstNode* nodep) {
|
int castSize(AstNode* nodep) {
|
||||||
if (nodep->isQuad()) return VL_QUADSIZE;
|
if (nodep->isQuad()) return VL_QUADSIZE;
|
||||||
else if (nodep->width()<=8) return 8;
|
else if (nodep->width()<=8) return 8;
|
||||||
else if (nodep->width()<=16) return 16;
|
else if (nodep->width()<=16) return 16;
|
||||||
else return VL_WORDSIZE;
|
else return VL_WORDSIZE;
|
||||||
}
|
}
|
||||||
void insureCast(AstNode* nodep) {
|
void insureCast(AstNode* nodep) {
|
||||||
if (castSize(nodep->backp()) != castSize(nodep)
|
if (castSize(nodep->backp()) != castSize(nodep)
|
||||||
|| !nodep->user1()) {
|
|| !nodep->user1()) {
|
||||||
insertCast(nodep, castSize(nodep->backp()));
|
insertCast(nodep, castSize(nodep->backp()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void insureLower32Cast(AstCCast* nodep) {
|
void insureLower32Cast(AstCCast* nodep) {
|
||||||
// If we have uint64 = CAST(uint64(x)) then the upcasting
|
// If we have uint64 = CAST(uint64(x)) then the upcasting
|
||||||
// really needs to be CAST(uint64(CAST(uint32(x))).
|
// really needs to be CAST(uint64(CAST(uint32(x))).
|
||||||
// Otherwise a (uint64)(a>b) would return wrong value, as
|
// Otherwise a (uint64)(a>b) would return wrong value, as
|
||||||
// less than has undeterministic signedness.
|
// less than has nondeterministic signedness.
|
||||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
||||||
&& !VN_IS(nodep->lhsp(), CCast)) {
|
&& !VN_IS(nodep->lhsp(), CCast)) {
|
||||||
insertCast(nodep->lhsp(), VL_WORDSIZE);
|
insertCast(nodep->lhsp(), VL_WORDSIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeUniop* nodep) {
|
virtual void visit(AstNodeUniop* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->user1(nodep->lhsp()->user1());
|
nodep->user1(nodep->lhsp()->user1());
|
||||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeBiop* nodep) {
|
virtual void visit(AstNodeBiop* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->user1(nodep->lhsp()->user1()
|
nodep->user1(nodep->lhsp()->user1()
|
||||||
| nodep->rhsp()->user1());
|
| nodep->rhsp()->user1());
|
||||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeTriop* nodep) {
|
virtual void visit(AstNodeTriop* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->user1(nodep->lhsp()->user1()
|
nodep->user1(nodep->lhsp()->user1()
|
||||||
| nodep->rhsp()->user1()
|
| nodep->rhsp()->user1()
|
||||||
| nodep->thsp()->user1());
|
| nodep->thsp()->user1());
|
||||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||||
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
|
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstCCast* nodep) {
|
virtual void visit(AstCCast* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
insureLower32Cast(nodep);
|
insureLower32Cast(nodep);
|
||||||
nodep->user1(1);
|
nodep->user1(1);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNegate* nodep) {
|
virtual void visit(AstNegate* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->user1(nodep->lhsp()->user1());
|
nodep->user1(nodep->lhsp()->user1());
|
||||||
if (nodep->lhsp()->widthMin()==1) {
|
if (nodep->lhsp()->widthMin()==1) {
|
||||||
// We want to avoid a GCC "converting of negative value" warning
|
// We want to avoid a GCC "converting of negative value" warning
|
||||||
// from our expansion of
|
// from our expansion of
|
||||||
// out = {32{a<b}} => out = - (a<b)
|
// out = {32{a<b}} => out = - (a<b)
|
||||||
insertCast(nodep->lhsp(), castSize(nodep));
|
insertCast(nodep->lhsp(), castSize(nodep));
|
||||||
} else {
|
} else {
|
||||||
insureCast(nodep->lhsp());
|
insureCast(nodep->lhsp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarRef* nodep) {
|
virtual void visit(AstVarRef* nodep) {
|
||||||
if (!nodep->lvalue()
|
if (!nodep->lvalue()
|
||||||
&& !VN_IS(nodep->backp(), CCast)
|
&& !VN_IS(nodep->backp(), CCast)
|
||||||
&& VN_IS(nodep->backp(), NodeMath)
|
&& VN_IS(nodep->backp(), NodeMath)
|
||||||
&& !VN_IS(nodep->backp(), ArraySel)
|
&& !VN_IS(nodep->backp(), ArraySel)
|
||||||
&& nodep->backp()->width()
|
&& nodep->backp()->width()
|
||||||
&& castSize(nodep) != castSize(nodep->varp())) {
|
&& castSize(nodep) != castSize(nodep->varp())) {
|
||||||
// Cast vars to IData first, else below has upper bits wrongly set
|
// Cast vars to IData first, else below has upper bits wrongly set
|
||||||
// CData x=3; out = (QData)(x<<30);
|
// CData x=3; out = (QData)(x<<30);
|
||||||
insertCast(nodep, castSize(nodep));
|
insertCast(nodep, castSize(nodep));
|
||||||
}
|
}
|
||||||
nodep->user1(1);
|
nodep->user1(1);
|
||||||
}
|
}
|
||||||
virtual void visit(AstConst* nodep) {
|
virtual void visit(AstConst* nodep) {
|
||||||
// Constants are of unknown size if smaller than 33 bits, becase
|
// Constants are of unknown size if smaller than 33 bits, because
|
||||||
// we're too lazy to wrap every constant in the universe in
|
// we're too lazy to wrap every constant in the universe in
|
||||||
// ((IData)#).
|
// ((IData)#).
|
||||||
nodep->user1(nodep->isQuad() || nodep->isWide());
|
nodep->user1(nodep->isQuad() || nodep->isWide());
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOPs
|
// NOPs
|
||||||
@@ -170,7 +170,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit CastVisitor(AstNetlist* nodep) {
|
explicit CastVisitor(AstNetlist* nodep) {
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CAST_H_
|
#ifndef _V3CAST_H_
|
||||||
#define _V3CAST_H_ 1
|
#define _V3CAST_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void castAll(AstNetlist* nodep);
|
static void castAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+431
-422
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CDC_H_
|
#ifndef _V3CDC_H_
|
||||||
#define _V3CDC_H_ 1
|
#define _V3CDC_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void cdcAll(AstNetlist* nodep);
|
static void cdcAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+161
-151
@@ -20,16 +20,16 @@
|
|||||||
// V3Changed's Transformations:
|
// V3Changed's Transformations:
|
||||||
//
|
//
|
||||||
// Each module:
|
// Each module:
|
||||||
// Each combo block
|
// Each combo block
|
||||||
// For each variable that comes from combo block and is generated AFTER a usage
|
// 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?)
|
// Add __Vlast_{var} to local section, init to current value (just use array?)
|
||||||
// Change = if any var != last.
|
// Change = if any var != last.
|
||||||
// If a signal is used as a clock in this module or any
|
// If a signal is used as a clock in this module or any
|
||||||
// module *below*, and it isn't a input to this module,
|
// module *below*, and it isn't a input to this module,
|
||||||
// we need to indicate a new clock has been created.
|
// we need to indicate a new clock has been created.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -47,56 +47,60 @@
|
|||||||
class ChangedState {
|
class ChangedState {
|
||||||
public:
|
public:
|
||||||
// STATE
|
// STATE
|
||||||
AstNodeModule* m_topModp; // Top module
|
AstNodeModule* m_topModp; // Top module
|
||||||
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
||||||
AstCFunc* m_chgFuncp; // Change function we're building
|
AstCFunc* m_chgFuncp; // Change function we're building
|
||||||
AstCFunc* m_tlChgFuncp; // Top level 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_numStmts; // Number of statements added to m_chgFuncp
|
||||||
int m_funcNum; // Number of change functions emitted
|
int m_funcNum; // Number of change functions emitted
|
||||||
|
|
||||||
ChangedState() {
|
ChangedState() {
|
||||||
m_topModp = NULL;
|
m_topModp = NULL;
|
||||||
m_chgFuncp = NULL;
|
m_chgFuncp = NULL;
|
||||||
m_scopetopp = NULL;
|
m_scopetopp = NULL;
|
||||||
m_tlChgFuncp = NULL;
|
m_tlChgFuncp = NULL;
|
||||||
m_numStmts = 0;
|
m_numStmts = 0;
|
||||||
m_funcNum = 0;
|
m_funcNum = 0;
|
||||||
}
|
}
|
||||||
~ChangedState() {}
|
~ChangedState() {}
|
||||||
|
|
||||||
void maybeCreateChgFuncp() {
|
void maybeCreateChgFuncp() {
|
||||||
// Don't create an extra function call if splitting is disabled
|
// Don't create an extra function call if splitting is disabled
|
||||||
if (!v3Global.opt.outputSplitCFuncs()) {
|
if (!v3Global.opt.outputSplitCFuncs()) {
|
||||||
m_chgFuncp = m_tlChgFuncp;
|
m_chgFuncp = m_tlChgFuncp;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
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 = new AstCFunc(m_scopetopp->fileline(),
|
||||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
"_change_request_" + cvtToStr(++m_funcNum),
|
||||||
m_chgFuncp->symProlog(true);
|
m_scopetopp, "QData");
|
||||||
m_chgFuncp->declPrivate(true);
|
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||||
m_scopetopp->addActivep(m_chgFuncp);
|
m_chgFuncp->symProlog(true);
|
||||||
|
m_chgFuncp->declPrivate(true);
|
||||||
|
m_scopetopp->addActivep(m_chgFuncp);
|
||||||
|
|
||||||
// Add a top call to it
|
// Add a top call to it
|
||||||
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
||||||
callp->argTypes("vlSymsp");
|
callp->argTypes("vlSymsp");
|
||||||
|
|
||||||
if (!m_tlChgFuncp->stmtsp()) {
|
if (!m_tlChgFuncp->stmtsp()) {
|
||||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||||
} else {
|
} else {
|
||||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||||
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
|
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
|
||||||
// This is currently using AstLogOr which will shortcut the evaluation if
|
// This is currently using AstLogOr which will shortcut the
|
||||||
// any function returns true. This is likely what we want and is similar to the logic already in use
|
// evaluation if any function returns true. This is likely what
|
||||||
// inside V3EmitC, however, it also means that verbose logging may miss to print change detect variables.
|
// we want and is similar to the logic already in use inside
|
||||||
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
|
// V3EmitC, however, it also means that verbose logging may
|
||||||
new AstLogOr(m_scopetopp->fileline(), callp,
|
// miss to print change detect variables.
|
||||||
returnp->lhsp()->unlinkFrBack()));
|
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
|
||||||
returnp->replaceWith(newp);
|
new AstLogOr(m_scopetopp->fileline(), callp,
|
||||||
returnp->deleteTree(); VL_DANGLING(returnp);
|
returnp->lhsp()->unlinkFrBack()));
|
||||||
}
|
returnp->replaceWith(newp);
|
||||||
m_numStmts = 0;
|
returnp->deleteTree(); VL_DANGLING(returnp);
|
||||||
}
|
}
|
||||||
|
m_numStmts = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,112 +110,116 @@ public:
|
|||||||
class ChangedInsertVisitor : public AstNVisitor {
|
class ChangedInsertVisitor : public AstNVisitor {
|
||||||
private:
|
private:
|
||||||
// STATE
|
// STATE
|
||||||
ChangedState* m_statep; // Shared state across visitors
|
ChangedState* m_statep; // Shared state across visitors
|
||||||
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
||||||
AstVarScope* m_newvscp; // New (change detect) 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_varEqnp; // Original var's equation to get var value
|
||||||
AstNode* m_newLvEqnp; // New var's equation to read value
|
AstNode* m_newLvEqnp; // New var's equation to read value
|
||||||
AstNode* m_newRvEqnp; // New var's equation to set value
|
AstNode* m_newRvEqnp; // New var's equation to set value
|
||||||
uint32_t m_detects; // # detects created
|
uint32_t m_detects; // # detects created
|
||||||
|
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
enum MiscConsts {
|
enum MiscConsts {
|
||||||
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
||||||
// Ok to increase this, but may result in much slower model
|
// Ok to increase this, but may result in much slower model
|
||||||
};
|
};
|
||||||
|
|
||||||
void newChangeDet() {
|
void newChangeDet() {
|
||||||
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
||||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<<cvtToStr(DETECTARRAY_MAX_INDEXES)
|
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "
|
||||||
<<" array indexes (probably with UNOPTFLAT warning suppressed): "<<m_vscp->prettyName()<<endl
|
<<cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||||
<<m_vscp->warnMore()
|
<<" array indexes (probably with UNOPTFLAT warning suppressed): "
|
||||||
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
|
<<m_vscp->prettyName()<<endl
|
||||||
return;
|
<<m_vscp->warnMore()
|
||||||
}
|
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
|
||||||
m_statep->maybeCreateChgFuncp();
|
return;
|
||||||
|
}
|
||||||
|
m_statep->maybeCreateChgFuncp();
|
||||||
|
|
||||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(),
|
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(),
|
||||||
m_varEqnp->cloneTree(true),
|
m_varEqnp->cloneTree(true),
|
||||||
m_newRvEqnp->cloneTree(true), false);
|
m_newRvEqnp->cloneTree(true), false);
|
||||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||||
AstAssign* initp = new AstAssign(m_vscp->fileline(),
|
AstAssign* initp = new AstAssign(m_vscp->fileline(),
|
||||||
m_newLvEqnp->cloneTree(true),
|
m_newLvEqnp->cloneTree(true),
|
||||||
m_varEqnp->cloneTree(true));
|
m_varEqnp->cloneTree(true));
|
||||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||||
EmitCBaseCounterVisitor visitor(initp);
|
EmitCBaseCounterVisitor visitor(initp);
|
||||||
m_statep->m_numStmts += visitor.count();
|
m_statep->m_numStmts += visitor.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstBasicDType* nodep) {
|
virtual void visit(AstBasicDType* nodep) {
|
||||||
newChangeDet();
|
newChangeDet();
|
||||||
}
|
}
|
||||||
virtual void visit(AstPackArrayDType* nodep) {
|
virtual void visit(AstPackArrayDType* nodep) {
|
||||||
newChangeDet();
|
newChangeDet();
|
||||||
}
|
}
|
||||||
virtual void visit(AstUnpackArrayDType* nodep) {
|
virtual void visit(AstUnpackArrayDType* nodep) {
|
||||||
for (int index=0; index < nodep->elementsConst(); ++index) {
|
for (int index=0; index < nodep->elementsConst(); ++index) {
|
||||||
AstNode* origVEp = m_varEqnp;
|
AstNode* origVEp = m_varEqnp;
|
||||||
AstNode* origNLEp = m_newLvEqnp;
|
AstNode* origNLEp = m_newLvEqnp;
|
||||||
AstNode* origNREp = m_newRvEqnp;
|
AstNode* origNREp = m_newRvEqnp;
|
||||||
|
|
||||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->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_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||||
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||||
|
|
||||||
iterate(nodep->subDTypep()->skipRefp());
|
iterate(nodep->subDTypep()->skipRefp());
|
||||||
|
|
||||||
m_varEqnp->deleteTree();
|
m_varEqnp->deleteTree();
|
||||||
m_newLvEqnp->deleteTree();
|
m_newLvEqnp->deleteTree();
|
||||||
m_newRvEqnp->deleteTree();
|
m_newRvEqnp->deleteTree();
|
||||||
|
|
||||||
m_varEqnp = origVEp;
|
m_varEqnp = origVEp;
|
||||||
m_newLvEqnp = origNLEp;
|
m_newLvEqnp = origNLEp;
|
||||||
m_newRvEqnp = origNREp;
|
m_newRvEqnp = origNREp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeClassDType* nodep) {
|
virtual void visit(AstNodeClassDType* nodep) {
|
||||||
if (nodep->packedUnsup()) {
|
if (nodep->packedUnsup()) {
|
||||||
newChangeDet();
|
newChangeDet();
|
||||||
} else {
|
} else {
|
||||||
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-class-");
|
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
|
||||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||||
" (probably with UNOPTFLAT warning suppressed): "
|
" (probably with UNOPTFLAT warning suppressed): "
|
||||||
<<m_vscp->varp()->prettyName());
|
<<m_vscp->varp()->prettyNameQ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
iterateChildren(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"
|
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||||
" (probably with UNOPTFLAT warning suppressed): "
|
" (probably with UNOPTFLAT warning suppressed): "
|
||||||
<<m_vscp->varp()->prettyName());
|
<<m_vscp->varp()->prettyNameQ());
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
||||||
m_statep = statep;
|
m_statep = statep;
|
||||||
m_vscp = vscp;
|
m_vscp = vscp;
|
||||||
m_detects = 0;
|
m_detects = 0;
|
||||||
{
|
{
|
||||||
AstVar* varp = m_vscp->varp();
|
AstVar* varp = m_vscp->varp();
|
||||||
string newvarname = "__Vchglast__"+m_vscp->scopep()->nameDotless()+"__"+varp->shortName();
|
string newvarname = ("__Vchglast__"+m_vscp->scopep()->nameDotless()
|
||||||
// Create: VARREF(_last)
|
+"__"+varp->shortName());
|
||||||
// ASSIGN(VARREF(_last), VARREF(var))
|
// Create: VARREF(_last)
|
||||||
// ...
|
// ASSIGN(VARREF(_last), VARREF(var))
|
||||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
// ...
|
||||||
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||||
m_statep->m_topModp->addStmtp(newvarp);
|
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP,
|
||||||
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
newvarname, varp);
|
||||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
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_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
|
||||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
||||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
||||||
}
|
}
|
||||||
iterate(vscp->dtypep()->skipRefp());
|
iterate(vscp->dtypep()->skipRefp());
|
||||||
m_varEqnp->deleteTree();
|
m_varEqnp->deleteTree();
|
||||||
m_newLvEqnp->deleteTree();
|
m_newLvEqnp->deleteTree();
|
||||||
m_newRvEqnp->deleteTree();
|
m_newRvEqnp->deleteTree();
|
||||||
}
|
}
|
||||||
virtual ~ChangedInsertVisitor() {}
|
virtual ~ChangedInsertVisitor() {}
|
||||||
};
|
};
|
||||||
@@ -223,61 +231,63 @@ class ChangedVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstVarScope::user1() -> bool. True indicates processed
|
// AstVarScope::user1() -> bool. True indicates processed
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
ChangedState* m_statep; // Shared state across visitors
|
ChangedState* m_statep; // Shared state across visitors
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
VL_DEBUG_FUNC; // Declare debug()
|
||||||
|
|
||||||
void genChangeDet(AstVarScope* vscp) {
|
void genChangeDet(AstVarScope* vscp) {
|
||||||
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
|
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<<vscp->prettyNameQ());
|
||||||
ChangedInsertVisitor visitor (vscp, m_statep);
|
ChangedInsertVisitor visitor (vscp, m_statep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
UINFO(4," MOD "<<nodep<<endl);
|
UINFO(4," MOD "<<nodep<<endl);
|
||||||
if (nodep->isTop()) {
|
if (nodep->isTop()) {
|
||||||
m_statep->m_topModp = nodep;
|
m_statep->m_topModp = nodep;
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstTopScope* nodep) {
|
virtual void visit(AstTopScope* nodep) {
|
||||||
UINFO(4," TS "<<nodep<<endl);
|
UINFO(4," TS "<<nodep<<endl);
|
||||||
// Clearing
|
// Clearing
|
||||||
AstNode::user1ClearTree();
|
AstNode::user1ClearTree();
|
||||||
// Create the change detection function
|
// Create the change detection function
|
||||||
AstScope* scopep = nodep->scopep();
|
AstScope* scopep = nodep->scopep();
|
||||||
if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
|
UASSERT_OBJ(scopep, nodep,
|
||||||
m_statep->m_scopetopp = scopep;
|
"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
|
// 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 = new AstCFunc(nodep->fileline(),
|
||||||
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
"_change_request", scopep, "QData");
|
||||||
m_statep->m_tlChgFuncp->symProlog(true);
|
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||||
m_statep->m_tlChgFuncp->declPrivate(true);
|
m_statep->m_tlChgFuncp->symProlog(true);
|
||||||
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
m_statep->m_tlChgFuncp->declPrivate(true);
|
||||||
// Each change detection function needs at least one AstChangeDet
|
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
||||||
// to ensure that V3EmitC outputs the necessary code.
|
// Each change detection function needs at least one AstChangeDet
|
||||||
m_statep->maybeCreateChgFuncp();
|
// to ensure that V3EmitC outputs the necessary code.
|
||||||
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
m_statep->maybeCreateChgFuncp();
|
||||||
|
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
||||||
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarScope* nodep) {
|
virtual void visit(AstVarScope* nodep) {
|
||||||
if (nodep->isCircular()) {
|
if (nodep->isCircular()) {
|
||||||
UINFO(8," CIRC "<<nodep<<endl);
|
UINFO(8," CIRC "<<nodep<<endl);
|
||||||
if (!nodep->user1SetOnce()) {
|
if (!nodep->user1SetOnce()) {
|
||||||
genChangeDet(nodep);
|
genChangeDet(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeMath* nodep) {
|
virtual void visit(AstNodeMath* nodep) {
|
||||||
// Short-circuit
|
// Short-circuit
|
||||||
}
|
}
|
||||||
//--------------------
|
//--------------------
|
||||||
// Default: Just iterate
|
// Default: Just iterate
|
||||||
@@ -286,9 +296,9 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
|
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
|
||||||
m_statep = statep;
|
m_statep = statep;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~ChangedVisitor() {}
|
virtual ~ChangedVisitor() {}
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CHANGED_H_
|
#ifndef _V3CHANGED_H_
|
||||||
#define _V3CHANGED_H_ 1
|
#define _V3CHANGED_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void changedAll(AstNetlist* nodep);
|
static void changedAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+107
-106
@@ -20,13 +20,13 @@
|
|||||||
// V3Clean's Transformations:
|
// V3Clean's Transformations:
|
||||||
//
|
//
|
||||||
// Each module:
|
// Each module:
|
||||||
// For each math operator, if it requires a clean operand,
|
// For each math operator, if it requires a clean operand,
|
||||||
// and the operand is dirty, insert a CLEAN node.
|
// and the operand is dirty, insert a CLEAN node.
|
||||||
// Resize operands to C++ 32/64/wide types.
|
// Resize operands to C++ 32/64/wide types.
|
||||||
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
|
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -44,12 +44,12 @@ class CleanVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
||||||
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
||||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
AstUser2InUse m_inuser2;
|
AstUser2InUse m_inuser2;
|
||||||
AstUser3InUse m_inuser3;
|
AstUser3InUse m_inuser3;
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
|
enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
|
||||||
@@ -62,153 +62,154 @@ private:
|
|||||||
|
|
||||||
// Width resetting
|
// Width resetting
|
||||||
int cppWidth(AstNode* nodep) {
|
int cppWidth(AstNode* nodep) {
|
||||||
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
|
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
|
||||||
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
|
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
|
||||||
else return nodep->widthWords()*VL_WORDSIZE;
|
else return nodep->widthWords()*VL_WORDSIZE;
|
||||||
}
|
}
|
||||||
void setCppWidth(AstNode* nodep) {
|
void setCppWidth(AstNode* nodep) {
|
||||||
nodep->user2(true); // Don't resize it again
|
nodep->user2(true); // Don't resize it again
|
||||||
AstNodeDType* old_dtypep = nodep->dtypep();
|
AstNodeDType* old_dtypep = nodep->dtypep();
|
||||||
int width=cppWidth(nodep); // widthMin is unchanged
|
int width = cppWidth(nodep); // widthMin is unchanged
|
||||||
if (old_dtypep->width() != width) {
|
if (old_dtypep->width() != width) {
|
||||||
// Since any given dtype's cppWidth() is the same, we can just
|
// Since any given dtype's cppWidth() is the same, we can just
|
||||||
// remember one convertion for each, and reuse it
|
// remember one conversion for each, and reuse it
|
||||||
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
||||||
nodep->dtypep(new_dtypep);
|
nodep->dtypep(new_dtypep);
|
||||||
} else {
|
} else {
|
||||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||||
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
||||||
if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed");
|
UASSERT_OBJ(new_dtypep2 != old_dtypep, nodep,
|
||||||
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
"Dtype didn't change when width changed");
|
||||||
}
|
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void computeCppWidth(AstNode* nodep) {
|
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!
|
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
||||||
} else {
|
} else {
|
||||||
setCppWidth(nodep);
|
setCppWidth(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the clean state in the userp on each node
|
// Store the clean state in the userp on each node
|
||||||
void setCleanState(AstNode* nodep, CleanState clean) {
|
void setCleanState(AstNode* nodep, CleanState clean) {
|
||||||
nodep->user1(clean);
|
nodep->user1(clean);
|
||||||
}
|
}
|
||||||
CleanState getCleanState(AstNode* nodep) {
|
CleanState getCleanState(AstNode* nodep) {
|
||||||
return static_cast<CleanState>(nodep->user1());
|
return static_cast<CleanState>(nodep->user1());
|
||||||
}
|
}
|
||||||
bool isClean(AstNode* nodep) {
|
bool isClean(AstNode* nodep) {
|
||||||
CleanState clstate = getCleanState(nodep);
|
CleanState clstate = getCleanState(nodep);
|
||||||
if (clstate==CS_CLEAN) return true;
|
if (clstate==CS_CLEAN) return true;
|
||||||
if (clstate==CS_DIRTY) return false;
|
if (clstate==CS_DIRTY) return false;
|
||||||
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
|
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void setClean(AstNode* nodep, bool isClean) {
|
void setClean(AstNode* nodep, bool isClean) {
|
||||||
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
||||||
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); //32,64,...
|
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); // 32,64,...
|
||||||
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
|
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operate on nodes
|
// Operate on nodes
|
||||||
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
|
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
|
||||||
UINFO(4," NeedClean "<<nodep<<endl);
|
UINFO(4," NeedClean "<<nodep<<endl);
|
||||||
AstNRelinker relinkHandle;
|
AstNRelinker relinkHandle;
|
||||||
nodep->unlinkFrBack(&relinkHandle);
|
nodep->unlinkFrBack(&relinkHandle);
|
||||||
//
|
//
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
V3Number mask (nodep->fileline(), cppWidth(nodep));
|
V3Number mask (nodep, cppWidth(nodep));
|
||||||
mask.setMask(nodep->widthMin());
|
mask.setMask(nodep->widthMin());
|
||||||
AstNode* cleanp = new AstAnd(nodep->fileline(),
|
AstNode* cleanp = new AstAnd(nodep->fileline(),
|
||||||
new AstConst(nodep->fileline(), mask),
|
new AstConst(nodep->fileline(), mask),
|
||||||
nodep);
|
nodep);
|
||||||
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
||||||
relinkHandle.relink(cleanp);
|
relinkHandle.relink(cleanp);
|
||||||
}
|
}
|
||||||
void insureClean(AstNode* nodep) {
|
void insureClean(AstNode* nodep) {
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
if (!isClean(nodep)) insertClean(nodep);
|
if (!isClean(nodep)) insertClean(nodep);
|
||||||
}
|
}
|
||||||
void insureCleanAndNext(AstNode* nodep) {
|
void insureCleanAndNext(AstNode* nodep) {
|
||||||
// Editing list, careful looping!
|
// Editing list, careful looping!
|
||||||
for (AstNode* exprp = nodep; exprp; ) {
|
for (AstNode* exprp = nodep; exprp; ) {
|
||||||
AstNode* nextp = exprp->nextp();
|
AstNode* nextp = exprp->nextp();
|
||||||
insureClean(exprp);
|
insureClean(exprp);
|
||||||
exprp = nextp;
|
exprp = nextp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base type handling methods
|
// Base type handling methods
|
||||||
void operandBiop(AstNodeBiop* nodep) {
|
void operandBiop(AstNodeBiop* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
if (nodep->cleanLhs()) {
|
if (nodep->cleanLhs()) {
|
||||||
insureClean(nodep->lhsp());
|
insureClean(nodep->lhsp());
|
||||||
}
|
}
|
||||||
if (nodep->cleanRhs()) {
|
if (nodep->cleanRhs()) {
|
||||||
insureClean(nodep->rhsp());
|
insureClean(nodep->rhsp());
|
||||||
}
|
}
|
||||||
//no setClean.. must do it in each user routine.
|
//no setClean.. must do it in each user routine.
|
||||||
}
|
}
|
||||||
void operandTriop(AstNodeTriop* nodep) {
|
void operandTriop(AstNodeTriop* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
if (nodep->cleanLhs()) {
|
if (nodep->cleanLhs()) {
|
||||||
insureClean(nodep->lhsp());
|
insureClean(nodep->lhsp());
|
||||||
}
|
}
|
||||||
if (nodep->cleanRhs()) {
|
if (nodep->cleanRhs()) {
|
||||||
insureClean(nodep->rhsp());
|
insureClean(nodep->rhsp());
|
||||||
}
|
}
|
||||||
if (nodep->cleanThs()) {
|
if (nodep->cleanThs()) {
|
||||||
insureClean(nodep->thsp());
|
insureClean(nodep->thsp());
|
||||||
}
|
}
|
||||||
//no setClean.. must do it in each user routine.
|
//no setClean.. must do it in each user routine.
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeUniop* nodep) {
|
virtual void visit(AstNodeUniop* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
if (nodep->cleanLhs()) {
|
if (nodep->cleanLhs()) {
|
||||||
insureClean(nodep->lhsp());
|
insureClean(nodep->lhsp());
|
||||||
}
|
}
|
||||||
setClean(nodep, nodep->cleanOut());
|
setClean(nodep, nodep->cleanOut());
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeBiop* nodep) {
|
virtual void visit(AstNodeBiop* nodep) {
|
||||||
operandBiop(nodep);
|
operandBiop(nodep);
|
||||||
setClean(nodep, nodep->cleanOut());
|
setClean(nodep, nodep->cleanOut());
|
||||||
}
|
}
|
||||||
virtual void visit(AstAnd* nodep) {
|
virtual void visit(AstAnd* nodep) {
|
||||||
operandBiop(nodep);
|
operandBiop(nodep);
|
||||||
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
||||||
}
|
}
|
||||||
virtual void visit(AstXor* nodep) {
|
virtual void visit(AstXor* nodep) {
|
||||||
operandBiop(nodep);
|
operandBiop(nodep);
|
||||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||||
}
|
}
|
||||||
virtual void visit(AstOr* nodep) {
|
virtual void visit(AstOr* nodep) {
|
||||||
operandBiop(nodep);
|
operandBiop(nodep);
|
||||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeMath* nodep) {
|
virtual void visit(AstNodeMath* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
setClean(nodep, nodep->cleanOut());
|
setClean(nodep, nodep->cleanOut());
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep) {
|
virtual void visit(AstNodeAssign* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
if (nodep->cleanRhs()) {
|
if (nodep->cleanRhs()) {
|
||||||
insureClean(nodep->rhsp());
|
insureClean(nodep->rhsp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstText* nodep) {
|
virtual void visit(AstText* nodep) {
|
||||||
setClean(nodep, true);
|
setClean(nodep, true);
|
||||||
@@ -217,17 +218,17 @@ private:
|
|||||||
setClean(nodep, true);
|
setClean(nodep, true);
|
||||||
}
|
}
|
||||||
virtual void visit(AstSel* nodep) {
|
virtual void visit(AstSel* nodep) {
|
||||||
operandTriop(nodep);
|
operandTriop(nodep);
|
||||||
setClean(nodep, nodep->cleanOut());
|
setClean(nodep, nodep->cleanOut());
|
||||||
}
|
}
|
||||||
virtual void visit(AstUCFunc* nodep) {
|
virtual void visit(AstUCFunc* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
setClean(nodep, false);
|
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)) {
|
if (!VN_IS(nodep->backp(), And)) {
|
||||||
insertClean(nodep);
|
insertClean(nodep);
|
||||||
}
|
}
|
||||||
insureCleanAndNext(nodep->bodysp());
|
insureCleanAndNext(nodep->bodysp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstTraceDecl* nodep) {
|
virtual void visit(AstTraceDecl* nodep) {
|
||||||
@@ -239,32 +240,32 @@ private:
|
|||||||
insureCleanAndNext(nodep->valuep());
|
insureCleanAndNext(nodep->valuep());
|
||||||
}
|
}
|
||||||
virtual void visit(AstTypedef* nodep) {
|
virtual void visit(AstTypedef* nodep) {
|
||||||
// No cleaning, or would loose pointer to enum
|
// No cleaning, or would loose pointer to enum
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstParamTypeDType* nodep) {
|
virtual void visit(AstParamTypeDType* nodep) {
|
||||||
// No cleaning, or would loose pointer to enum
|
// No cleaning, or would loose pointer to enum
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control flow operators
|
// Control flow operators
|
||||||
virtual void visit(AstNodeCond* nodep) {
|
virtual void visit(AstNodeCond* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
insureClean(nodep->condp());
|
insureClean(nodep->condp());
|
||||||
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
||||||
}
|
}
|
||||||
virtual void visit(AstWhile* nodep) {
|
virtual void visit(AstWhile* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
insureClean(nodep->condp());
|
insureClean(nodep->condp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeIf* nodep) {
|
virtual void visit(AstNodeIf* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
insureClean(nodep->condp());
|
insureClean(nodep->condp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstSFormatF* nodep) {
|
virtual void visit(AstSFormatF* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
insureCleanAndNext(nodep->exprsp());
|
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) {
|
virtual void visit(AstUCStmt* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
@@ -280,11 +281,11 @@ private:
|
|||||||
// Default: Just iterate
|
// Default: Just iterate
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
computeCppWidth(nodep);
|
computeCppWidth(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit CleanVisitor(AstNetlist* nodep) {
|
explicit CleanVisitor(AstNetlist* nodep) {
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CLEAN_H_
|
#ifndef _V3CLEAN_H_
|
||||||
#define _V3CLEAN_H_ 1
|
#define _V3CLEAN_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void cleanAll(AstNetlist* nodep);
|
static void cleanAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
@@ -1,921 +0,0 @@
|
|||||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
||||||
//*************************************************************************
|
|
||||||
// DESCRIPTION: Verilator: Break always into sensitivity active domains
|
|
||||||
//
|
|
||||||
// Code available from: http://www.veripool.org/verilator
|
|
||||||
//
|
|
||||||
//*************************************************************************
|
|
||||||
//
|
|
||||||
// Copyright 2008-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.
|
|
||||||
//
|
|
||||||
//*************************************************************************
|
|
||||||
// V3ClkGater's Transformations:
|
|
||||||
//
|
|
||||||
// Look for clock gaters
|
|
||||||
// Note there's overlap between this process and V3Split's
|
|
||||||
// ALWAYS: Build graph
|
|
||||||
// IF: Form vertex pointing to any IFs or ALWAYS above
|
|
||||||
// VARREF: Form vertex pointing to IFs it is under
|
|
||||||
// ASSIGN: If under normal assignment, disable optimization
|
|
||||||
// FUTURE OPTIMIZE: If signal is set to itself, consider that OK for gating.
|
|
||||||
// !splitable: Mark all VARREFs in this statement as comming from it
|
|
||||||
// Optimize graph so if signal is referenced under multiple IF branches it moves up
|
|
||||||
// Make ALWAYS for each new gating term, and move statements there
|
|
||||||
//*************************************************************************
|
|
||||||
|
|
||||||
#include "config_build.h"
|
|
||||||
#include "verilatedos.h"
|
|
||||||
|
|
||||||
#include "V3Global.h"
|
|
||||||
#include "V3Ast.h"
|
|
||||||
#include "V3Stats.h"
|
|
||||||
#include "V3Graph.h"
|
|
||||||
#include "V3ClkGater.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Base for debug
|
|
||||||
|
|
||||||
class GaterBaseVisitor : public AstNVisitor {
|
|
||||||
protected:
|
|
||||||
VL_DEBUG_FUNC; // Declare debug()
|
|
||||||
};
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Support classes
|
|
||||||
|
|
||||||
class GaterVarVertex;
|
|
||||||
|
|
||||||
class GaterVertex : public V3GraphVertex {
|
|
||||||
static uint32_t s_rankNum;
|
|
||||||
public:
|
|
||||||
explicit GaterVertex(V3Graph* graphp)
|
|
||||||
: V3GraphVertex(graphp) {
|
|
||||||
s_rankNum++; rank(s_rankNum);
|
|
||||||
}
|
|
||||||
virtual ~GaterVertex() {}
|
|
||||||
virtual int typeNum() const = 0;
|
|
||||||
static void clearRank() { s_rankNum=0; }
|
|
||||||
virtual int sortCmp(const V3GraphVertex* rhsp) const;
|
|
||||||
};
|
|
||||||
uint32_t GaterVertex::s_rankNum = 0;
|
|
||||||
|
|
||||||
class GaterHeadVertex : public GaterVertex {
|
|
||||||
public:
|
|
||||||
explicit GaterHeadVertex(V3Graph* graphp)
|
|
||||||
: GaterVertex(graphp) {}
|
|
||||||
virtual ~GaterHeadVertex() {}
|
|
||||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
|
||||||
virtual string name() const { return "*HEAD*"; }
|
|
||||||
virtual string dotColor() const { return "green"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class GaterPliVertex : public GaterVertex {
|
|
||||||
public:
|
|
||||||
explicit GaterPliVertex(V3Graph* graphp)
|
|
||||||
: GaterVertex(graphp) {}
|
|
||||||
virtual ~GaterPliVertex() {}
|
|
||||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
|
||||||
virtual string name() const { return "*PLI*"; }
|
|
||||||
virtual string dotColor() const { return "red"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class GaterIfVertex : public GaterVertex {
|
|
||||||
AstNodeIf* m_nodep;
|
|
||||||
public:
|
|
||||||
AstNodeIf* nodep() const { return m_nodep; }
|
|
||||||
GaterIfVertex(V3Graph* graphp, AstNodeIf* nodep)
|
|
||||||
: GaterVertex(graphp), m_nodep(nodep) { }
|
|
||||||
virtual ~GaterIfVertex() {}
|
|
||||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
|
||||||
virtual string name() const {
|
|
||||||
return cvtToHex(m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class GaterVarVertex : public GaterVertex {
|
|
||||||
AstVarScope* m_nodep;
|
|
||||||
public:
|
|
||||||
AstVarScope* nodep() const { return m_nodep; }
|
|
||||||
GaterVarVertex(V3Graph* graphp, AstVarScope* nodep)
|
|
||||||
: GaterVertex(graphp), m_nodep(nodep) { }
|
|
||||||
virtual ~GaterVarVertex() {}
|
|
||||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
|
||||||
virtual string name() const { return nodep()->name(); }
|
|
||||||
virtual string dotColor() const { return "skyblue"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Edge types
|
|
||||||
|
|
||||||
class GaterEdge : public V3GraphEdge {
|
|
||||||
uint32_t m_ifelse; // True branch of if
|
|
||||||
public:
|
|
||||||
// These are used as shift amounts into node's user()
|
|
||||||
#define VU_DEFINE enum VarUsage { VU_NONE=0, VU_IF=1, VU_ELSE=2, VU_PLI=4, VU_MADE=8}
|
|
||||||
VU_DEFINE;
|
|
||||||
|
|
||||||
uint32_t ifelse() const { return m_ifelse; }
|
|
||||||
bool ifelseTrue() const { return m_ifelse & VU_IF; }
|
|
||||||
bool ifelseFalse() const { return m_ifelse & VU_ELSE; }
|
|
||||||
bool ifelseBoth() const { return ifelseTrue() && ifelseFalse(); }
|
|
||||||
GaterEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, uint32_t ifelse)
|
|
||||||
: V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {}
|
|
||||||
virtual ~GaterEdge() {}
|
|
||||||
virtual string dotColor() const { return ((ifelse() & VU_PLI) ? "red"
|
|
||||||
: (ifelseBoth() ? "blue"
|
|
||||||
: (ifelseTrue() ? "green"
|
|
||||||
: "darkgreen"))); }
|
|
||||||
virtual int sortCmp(const V3GraphEdge* rEdgep) const {
|
|
||||||
// Our master sort sorts by edges first, so we don't want to
|
|
||||||
// consider the upstream vertex::sortCmp when sorting by edges.
|
|
||||||
// We do want to order by something though; rank works
|
|
||||||
if (fromp()->rank() < rEdgep->fromp()->rank()) return -1;
|
|
||||||
if (fromp()->rank() > rEdgep->fromp()->rank()) return 1;
|
|
||||||
// If same, resolve by ifelse
|
|
||||||
const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
|
|
||||||
if (m_ifelse < crEdgep->m_ifelse) return -1;
|
|
||||||
if (m_ifelse > crEdgep->m_ifelse) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const {
|
|
||||||
const GaterVertex* crhsp = static_cast<const GaterVertex*>(rhsp);
|
|
||||||
// We really only care about ordering Var's together, but...
|
|
||||||
// First put same type together
|
|
||||||
if (typeNum() < crhsp->typeNum()) return -1;
|
|
||||||
if (typeNum() > crhsp->typeNum()) return 1;
|
|
||||||
// If variable, group by same input fanin
|
|
||||||
// (know they're the same type based on above compare)
|
|
||||||
if (dynamic_cast<const GaterVarVertex*>(this)) {
|
|
||||||
// We've already sorted by edges, so just see if same tree
|
|
||||||
// If this gets too slow, we could compute a hash up front
|
|
||||||
V3GraphEdge* lEdgep = this->inBeginp();
|
|
||||||
V3GraphEdge* rEdgep = rhsp->inBeginp();
|
|
||||||
while (lEdgep && rEdgep) {
|
|
||||||
const GaterEdge* clEdgep = static_cast<const GaterEdge*>(lEdgep);
|
|
||||||
const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
|
|
||||||
if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1;
|
|
||||||
if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1;
|
|
||||||
if (clEdgep->ifelse() < crEdgep->ifelse()) return -1;
|
|
||||||
if (clEdgep->ifelse() > crEdgep->ifelse()) return 1;
|
|
||||||
lEdgep = lEdgep->inNextp();
|
|
||||||
rEdgep = rEdgep->inNextp();
|
|
||||||
}
|
|
||||||
if (!lEdgep && !rEdgep) return 0;
|
|
||||||
return lEdgep ? -1 : 1;
|
|
||||||
}
|
|
||||||
// Finally by rank of this vertex
|
|
||||||
if (rank() < rhsp->rank()) return -1;
|
|
||||||
if (rank() > rhsp->rank()) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Check for non-simple gating equations
|
|
||||||
|
|
||||||
class GaterCondVisitor : public GaterBaseVisitor {
|
|
||||||
private:
|
|
||||||
// RETURN STATE
|
|
||||||
bool m_isSimple; // Set false when we know it isn't simple
|
|
||||||
// METHODS
|
|
||||||
inline void okIterate(AstNode* nodep) {
|
|
||||||
if (m_isSimple) iterateChildren(nodep);
|
|
||||||
}
|
|
||||||
// VISITORS
|
|
||||||
virtual void visit(AstOr* nodep) { okIterate(nodep); }
|
|
||||||
virtual void visit(AstAnd* nodep) { okIterate(nodep); }
|
|
||||||
virtual void visit(AstNot* nodep) { okIterate(nodep); }
|
|
||||||
virtual void visit(AstLogOr* nodep) { okIterate(nodep); }
|
|
||||||
virtual void visit(AstLogAnd* nodep) { okIterate(nodep); }
|
|
||||||
virtual void visit(AstLogNot* nodep) { okIterate(nodep); }
|
|
||||||
virtual void visit(AstVarRef* nodep) { okIterate(nodep); }
|
|
||||||
|
|
||||||
// Other possibilities are equals, etc
|
|
||||||
// But, we don't want to get too complicated or it will take too much
|
|
||||||
// effort to calculate the gater
|
|
||||||
|
|
||||||
virtual void visit(AstNode* nodep) {
|
|
||||||
m_isSimple = false;
|
|
||||||
//iterateChildren(nodep);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
// CONSTUCTORS
|
|
||||||
explicit GaterCondVisitor(AstNode* nodep) {
|
|
||||||
m_isSimple = true;
|
|
||||||
iterate(nodep);
|
|
||||||
}
|
|
||||||
virtual ~GaterCondVisitor() {}
|
|
||||||
// PUBLIC METHODS
|
|
||||||
bool isSimple() const { return m_isSimple; }
|
|
||||||
};
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Check for non-simple gating equations
|
|
||||||
|
|
||||||
class GaterBodyVisitor : public GaterBaseVisitor {
|
|
||||||
// NODE STATE
|
|
||||||
// Input state
|
|
||||||
// AstVarScope::user2p() -> AstAlways* moving this variable to
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
// This is used as a bitmask
|
|
||||||
STATE_UNKNOWN=0,
|
|
||||||
STATE_KEEP=1, // Seen a variable we need, keep this statement
|
|
||||||
STATE_DELETE=2 // Seen a variable we need, delete this statement
|
|
||||||
// 3=keep & delete
|
|
||||||
};
|
|
||||||
|
|
||||||
bool m_original; // Deleting original statements, vs deleting new var statements
|
|
||||||
AstNode* m_exprp; // New gater expression we are building
|
|
||||||
bool m_cloning; // Clone this object 0=not sure yet, 1=do
|
|
||||||
uint32_t m_state; // Parsing state
|
|
||||||
|
|
||||||
// VISITORS
|
|
||||||
virtual void visit(AstVarRef* nodep) {
|
|
||||||
if (nodep->lvalue()) {
|
|
||||||
AstVarScope* vscp = nodep->varScopep();
|
|
||||||
if (vscp->user2p() == m_exprp) {
|
|
||||||
// This variable's block needs to move to the new always
|
|
||||||
if (m_original) {
|
|
||||||
UINFO(9," VARREF delete in old: "<<nodep<<endl);
|
|
||||||
m_state |= STATE_DELETE;
|
|
||||||
} else {
|
|
||||||
UINFO(9," VARREF stays in new: "<<nodep<<endl);
|
|
||||||
m_state |= STATE_KEEP;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (m_original) {
|
|
||||||
UINFO(9," VARREF other stays in old\n");
|
|
||||||
m_state |= STATE_KEEP;
|
|
||||||
} else {
|
|
||||||
UINFO(9," VARREF other delete in new\n");
|
|
||||||
m_state |= STATE_DELETE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//virtual void visit(AstNodeIf* nodep) { ... }
|
|
||||||
// Not needed, it's the same handling as any other statement. Cool, huh?
|
|
||||||
// (We may get empty IFs but the constant propagater will rip them up for us)
|
|
||||||
|
|
||||||
virtual void visit(AstNodeStmt* nodep) {
|
|
||||||
if (!nodep->isStatement()) {
|
|
||||||
iterateChildren(nodep);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t oldstate = m_state;
|
|
||||||
// Find if children want to delete this or not.
|
|
||||||
// Note children may bicker, and want to both keep and delete (branches on a if)
|
|
||||||
uint32_t childstate;
|
|
||||||
{
|
|
||||||
m_state = STATE_UNKNOWN;
|
|
||||||
iterateChildren(nodep);
|
|
||||||
childstate = m_state;
|
|
||||||
}
|
|
||||||
m_state = oldstate;
|
|
||||||
UINFO(9," Did state="<<childstate<<" "<<nodep<<endl);
|
|
||||||
|
|
||||||
// Indeterminate (statement with no lvalue), so keep only in original
|
|
||||||
if (childstate == STATE_UNKNOWN) {
|
|
||||||
if (m_original) childstate |= STATE_KEEP;
|
|
||||||
else childstate |= STATE_DELETE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((childstate & STATE_DELETE) && !(childstate & STATE_KEEP)) {
|
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
|
||||||
// Pass upwards we did delete
|
|
||||||
m_state |= STATE_DELETE;
|
|
||||||
} else {
|
|
||||||
// Pass upwards we must keep
|
|
||||||
m_state |= STATE_KEEP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void visit(AstNode* nodep) {
|
|
||||||
iterateChildren(nodep);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
// CONSTUCTORS
|
|
||||||
GaterBodyVisitor(AstAlways* nodep, AstNode* exprp, bool original) {
|
|
||||||
m_exprp = exprp;
|
|
||||||
m_original = original;
|
|
||||||
m_state = STATE_UNKNOWN;
|
|
||||||
m_cloning = false;
|
|
||||||
if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: ");
|
|
||||||
iterateAndNextNull(nodep->bodysp());
|
|
||||||
if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: ");
|
|
||||||
// If there's no statements we shouldn't have had a resulting graph
|
|
||||||
// vertex asking for this creation
|
|
||||||
}
|
|
||||||
virtual ~GaterBodyVisitor() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Create clock gaters
|
|
||||||
|
|
||||||
class GaterVisitor : public GaterBaseVisitor {
|
|
||||||
// NODE STATE
|
|
||||||
// Cleared on Always
|
|
||||||
// AstVarScope::user1p() -> GaterVarVertex*
|
|
||||||
// AstAlways::user4() -> bool. True indicates processed
|
|
||||||
// Cleared on each new Always
|
|
||||||
// AstVarScope::user2p() -> AstAlways* moving this variable to
|
|
||||||
AstUser1InUse m_inuser1;
|
|
||||||
AstUser2InUse m_inuser2;
|
|
||||||
AstUser4InUse m_inuser4;
|
|
||||||
|
|
||||||
// GRAPH STATE
|
|
||||||
// Cleared on simplify
|
|
||||||
// Vertex::user() -> VarUsage: Mark of which if/else edges are hit
|
|
||||||
|
|
||||||
// TYPES
|
|
||||||
VU_DEFINE;
|
|
||||||
|
|
||||||
enum MiscConsts {
|
|
||||||
IF_DEPTH_MAX = 4, // IFs deep we bother to analyze
|
|
||||||
DOMAINS_MAX = 32 // Clock domains before avoiding O(N^2) blowup
|
|
||||||
};
|
|
||||||
|
|
||||||
// MEMBERS
|
|
||||||
string m_nonopt; // Reason block is not optimizable
|
|
||||||
V3Double0 m_statGaters; // Statistic tracking
|
|
||||||
V3Double0 m_statBits; // Statistic tracking
|
|
||||||
bool m_directlyUnderAlw; // Immediately under Always or If
|
|
||||||
int m_ifDepth; // Depth of IF statements
|
|
||||||
int m_numIfs; // Number of IF statements
|
|
||||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
|
||||||
GaterPliVertex* m_pliVertexp; // Element specifying PLI ordering
|
|
||||||
GaterHeadVertex* m_headVertexp; // Top vertex
|
|
||||||
V3GraphVertex* m_aboveVertexp; // Vertex above this point in tree
|
|
||||||
uint32_t m_aboveTrue; // Vertex above this point is true branch
|
|
||||||
AstVarScope* m_stmtVscp; // Current statement had variable assigned
|
|
||||||
bool m_stmtInPli; // Current statement has PLI
|
|
||||||
|
|
||||||
// METHODS
|
|
||||||
void nonOptimizable(AstNode* nodep, const char* reasonp) {
|
|
||||||
if (m_nonopt=="") {
|
|
||||||
UINFO(9," Nonopt: "<<reasonp<<": "<<nodep<<endl);
|
|
||||||
m_nonopt = reasonp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void scoreboardPli(AstNode* nodep) {
|
|
||||||
// Order all PLI statements with other PLI statements
|
|
||||||
// This ensures $display's and such remain in proper order
|
|
||||||
// We don't prevent splitting out other non-pli statements, however,
|
|
||||||
// because it is common to have $uasserts sprinkled about.
|
|
||||||
if (!m_pliVertexp) {
|
|
||||||
m_pliVertexp = new GaterPliVertex(&m_graph);
|
|
||||||
}
|
|
||||||
if (m_stmtVscp) { // Already saw a variable, be sure to mark it!
|
|
||||||
GaterVarVertex* varVtxp = reinterpret_cast<GaterVarVertex*>(m_stmtVscp->user1p());
|
|
||||||
new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI);
|
|
||||||
}
|
|
||||||
m_stmtInPli = true; // Mark all followon variables too
|
|
||||||
}
|
|
||||||
|
|
||||||
// METHODS -- GRAPH stuff
|
|
||||||
void simplifyGraph() {
|
|
||||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false);
|
|
||||||
|
|
||||||
// We now have all PLI variables with edges FROM the pli vertex
|
|
||||||
simplifyPli();
|
|
||||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false);
|
|
||||||
|
|
||||||
// Any vertex that points along both true & false path to variable
|
|
||||||
// can be simplfied so parent points to that vertex. Any vertex
|
|
||||||
// that points to a (great...) grandparent of a variable can just
|
|
||||||
// point to the edge.
|
|
||||||
m_graph.userClearVertices(); // user() will contain VarUsage
|
|
||||||
simplifyIfElseRecurse(m_headVertexp);
|
|
||||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false);
|
|
||||||
|
|
||||||
m_graph.userClearVertices(); // user() will contain VarUsage
|
|
||||||
simplifyGrandRecurse(m_headVertexp, 1);
|
|
||||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false);
|
|
||||||
|
|
||||||
simplifyRemoveUngated(m_headVertexp);
|
|
||||||
|
|
||||||
// Give all signals with same gating term same color
|
|
||||||
graphColorSameFeeder();
|
|
||||||
|
|
||||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void simplifyPli() {
|
|
||||||
// For now, we'll not gate any logic with PLI lvariables in it. In
|
|
||||||
// the future we may move PLI statements. One way to do so is to
|
|
||||||
// all below pli VARs to go IfVertex -> PliVertex -> VarVertex then
|
|
||||||
// they all must get colored the same. There may be a lot of
|
|
||||||
// duplicated edges to the PLI; they'll need cleanup. All of the
|
|
||||||
// later optimizations need to deal, and the GaterBodyVisitor needs
|
|
||||||
// to know how to move them.
|
|
||||||
if (m_pliVertexp) {
|
|
||||||
// Follow PLI out edges to find all relevant variables
|
|
||||||
for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) {
|
|
||||||
nextp = edgep->outNextp(); // We may edit the list
|
|
||||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
|
|
||||||
vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep);
|
|
||||||
} else {
|
|
||||||
m_graph.dump();
|
|
||||||
v3fatalSrc("PLI vertex points to non-signal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simplifyIfElseRecurse(V3GraphVertex* vertexp) {
|
|
||||||
// From bottom-up, propagate duplicate IF/ELSE branches to grandparent
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
||||||
simplifyIfElseRecurse(edgep->top());
|
|
||||||
}
|
|
||||||
//UINFO(9,"IERecurse "<<vertexp<<endl);
|
|
||||||
if (dynamic_cast<GaterIfVertex*>(vertexp)) {
|
|
||||||
// Clear indications
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
||||||
edgep->top()->user(VU_NONE);
|
|
||||||
}
|
|
||||||
// Mark nodes on to/from side
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
||||||
V3GraphVertex* toVxp = edgep->top();
|
|
||||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
|
||||||
// We may mark this twice in one pass - if so it's duplicated; no worries
|
|
||||||
if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF);
|
|
||||||
if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE);
|
|
||||||
//UINFO(9," mark "<<edgep<<" "<<toVxp<<endl);
|
|
||||||
}
|
|
||||||
// Any with both IF & ELSE mark get removal mark, and new parent edge
|
|
||||||
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
|
||||||
nextp = edgep->outNextp(); // We may edit the list
|
|
||||||
V3GraphVertex* toVxp = edgep->top();
|
|
||||||
//UINFO(9," to "<<toVxp->user()<<" "<<toVxp<<endl);
|
|
||||||
if ((toVxp->user() & VU_IF) && (toVxp->user() & VU_ELSE)) {
|
|
||||||
edgep->unlinkDelete(); VL_DANGLING(edgep);
|
|
||||||
if (!(toVxp->user() & VU_MADE)) { // Make an edge only once
|
|
||||||
toVxp->user(toVxp->user() | VU_MADE);
|
|
||||||
GaterEdge* inedgep = static_cast<GaterEdge*>(vertexp->inBeginp());
|
|
||||||
V3GraphVertex* grandparent = inedgep->fromp();
|
|
||||||
new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) {
|
|
||||||
// From top-down delete any vars that grandparents source
|
|
||||||
// IE A -> B -> C -> VAR
|
|
||||||
// \----------^
|
|
||||||
//UINFO(9,"GRecurse "<<depth<<" "<<vertexp<<endl);
|
|
||||||
// Mark all variables to be swept
|
|
||||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
|
|
||||||
nextp = edgep->outNextp(); // We may edit the list
|
|
||||||
if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
|
|
||||||
if (toVxp->user() && toVxp->user() < depth) {
|
|
||||||
// A recursion "above" us marked it,
|
|
||||||
// Remove this edge, it's redundant with the upper edge
|
|
||||||
edgep->unlinkDelete(); VL_DANGLING(edgep);
|
|
||||||
} else {
|
|
||||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
|
||||||
if (cedgep->ifelseBoth()) {
|
|
||||||
toVxp->user(depth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Recurse
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
||||||
simplifyGrandRecurse(edgep->top(), depth+1);
|
|
||||||
}
|
|
||||||
// Clean our marks
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
||||||
V3GraphVertex* toVxp = edgep->top();
|
|
||||||
if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it
|
|
||||||
} else {
|
|
||||||
toVxp->user(0); // We marked it originally, so unmark now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Delete any If nodes with no children
|
|
||||||
// Last, as want bottom-up cleanup
|
|
||||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
|
|
||||||
nextp = edgep->outNextp(); // We may edit the list
|
|
||||||
if (GaterIfVertex* toVxp = dynamic_cast<GaterIfVertex*>(edgep->top())) {
|
|
||||||
if (!toVxp->outBeginp()) {
|
|
||||||
if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop
|
|
||||||
toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simplifyRemoveUngated(V3GraphVertex* vertexp) {
|
|
||||||
// Remove variables that are ungated
|
|
||||||
// At this point, any variable under the head is ungated
|
|
||||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
|
|
||||||
nextp = edgep->outNextp(); // We may edit the list
|
|
||||||
if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
|
|
||||||
if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop
|
|
||||||
toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void graphColorSameFeeder() {
|
|
||||||
// Assign same color to all destination vertices that have same
|
|
||||||
// subgraph feeding into them
|
|
||||||
// (I.E. all "from" nodes are common within each color)
|
|
||||||
// We could hash, but instead it's faster to sort edges, so we know
|
|
||||||
// the same edge list is always adjacent. The result is a
|
|
||||||
// O(vertices*edges) loop, but we'd need that to hash too.
|
|
||||||
if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); }
|
|
||||||
|
|
||||||
m_graph.sortEdges();
|
|
||||||
|
|
||||||
// Now sort vertices, so same inbound edge set ends up adjacent
|
|
||||||
m_graph.sortVertices();
|
|
||||||
|
|
||||||
// Now walk and assign colors; same color is adjacent
|
|
||||||
m_graph.clearColors();
|
|
||||||
m_graph.userClearEdges(); // Used by newExprFromGraph
|
|
||||||
uint32_t color = 1;
|
|
||||||
GaterVarVertex* lastVxp = NULL;
|
|
||||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
||||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
|
|
||||||
if (!vVxp->inBeginp()) {
|
|
||||||
// At this point, any variable not linked is an error
|
|
||||||
// (It should have at least landed under the Head node)
|
|
||||||
vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection");
|
|
||||||
}
|
|
||||||
if (!lastVxp || vVxp->sortCmp(lastVxp)) {
|
|
||||||
// Different sources for this new node
|
|
||||||
color++;
|
|
||||||
}
|
|
||||||
vVxp->color(color);
|
|
||||||
lastVxp = vVxp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
AstNode* newExprFromGraph(GaterVarVertex* vertexp) {
|
|
||||||
// Recurse backwards, then form equation on return path
|
|
||||||
// We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation
|
|
||||||
// We get equations like "a | (!a & b)" which obviously could be reduced here,
|
|
||||||
// instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up
|
|
||||||
static uint32_t s_generation = 0;
|
|
||||||
++s_generation;
|
|
||||||
nafgMarkRecurse(vertexp, s_generation);
|
|
||||||
AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation);
|
|
||||||
if (debug()>=9) nodep->dumpTree(cout," GateExpr: ");
|
|
||||||
return nodep;
|
|
||||||
}
|
|
||||||
void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) {
|
|
||||||
// Backwards mark user() on the path we recurse
|
|
||||||
//UINFO(9," nafgMark: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
|
||||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
||||||
//UINFO(9," nafgMark: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
|
|
||||||
edgep->user(generation);
|
|
||||||
nafgMarkRecurse(edgep->fromp(), generation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) {
|
|
||||||
// Forewards follow user() marked previously and build tree
|
|
||||||
AstNode* nodep = NULL;
|
|
||||||
// OR across all edges found at this level
|
|
||||||
//UINFO(9," nafgEnter: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
||||||
if (edgep->user() == generation) {
|
|
||||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
|
||||||
AstNode* eqnp = NULL;
|
|
||||||
//UINFO(9," nafgFollow: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
|
|
||||||
if (dynamic_cast<GaterHeadVertex*>(edgep->fromp())) {
|
|
||||||
// Just OR in all lower terms
|
|
||||||
eqnp = nafgCreateRecurse(edgep->top(), generation);
|
|
||||||
} else if (GaterIfVertex* cVxp = dynamic_cast<GaterIfVertex*>(edgep->fromp())) {
|
|
||||||
// Edges from IFs represent a real IF branch in the equation tree
|
|
||||||
//UINFO(9," ifver "<<cvtToHex(edgep)<<" cc"<<edgep->dotColor()<<endl);
|
|
||||||
eqnp = cVxp->nodep()->condp()->cloneTree(true);
|
|
||||||
if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition");
|
|
||||||
if (cedgep->ifelseFalse()) {
|
|
||||||
eqnp = new AstNot(eqnp->fileline(),eqnp);
|
|
||||||
}
|
|
||||||
// We need to AND this term onto whatever was found below it
|
|
||||||
AstNode* belowp = nafgCreateRecurse(edgep->top(), generation);
|
|
||||||
if (belowp) eqnp = new AstAnd(eqnp->fileline(),eqnp,belowp);
|
|
||||||
}
|
|
||||||
// Top level we could choose to make multiple gaters, or ORs under the gater
|
|
||||||
// Right now we'll put OR lower down and let other optimizations deal
|
|
||||||
if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp);
|
|
||||||
else nodep = eqnp;
|
|
||||||
//if (debug()>=9) nodep->dumpTree(cout," followExpr: ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//UINFO(9," nafgExit: "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
|
||||||
return nodep;
|
|
||||||
}
|
|
||||||
|
|
||||||
void newAlwaysTrees(AstAlways* nodep) {
|
|
||||||
// Across all variables we're moving
|
|
||||||
uint32_t lastColor = 0;
|
|
||||||
AstNode* lastExprp = NULL;
|
|
||||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
||||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
|
|
||||||
if (!lastExprp || lastColor != vVxp->color()) {
|
|
||||||
lastColor = vVxp->color();
|
|
||||||
// Create the block we've just finished
|
|
||||||
if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below
|
|
||||||
// New expression for this color
|
|
||||||
lastExprp = newExprFromGraph(vVxp);
|
|
||||||
}
|
|
||||||
// Mark variable to move
|
|
||||||
if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters");
|
|
||||||
vVxp->nodep()->user2p(lastExprp);
|
|
||||||
m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more!
|
|
||||||
// There shouldn't be two possibilities we want to
|
|
||||||
// move to, IE {A,B} <= Z because we marked such
|
|
||||||
// things as unoptimizable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Create the final block we've just finished
|
|
||||||
if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above
|
|
||||||
}
|
|
||||||
|
|
||||||
void newAlwaysTree(AstAlways* nodep, AstNode* exprp) {
|
|
||||||
// Create new always with specified gating expression
|
|
||||||
// Relevant vars are already marked with user2p
|
|
||||||
|
|
||||||
// Should we put the gater under the always itself,
|
|
||||||
// or make a new signal?
|
|
||||||
// New signal: May cause replicated logic until we can combine
|
|
||||||
// Need way to toggle signal on complicated pos/negedge
|
|
||||||
// Under Always: More complicated, may not propagate logic with gateer
|
|
||||||
// Untill we get better gate optimization, we'll go this route.
|
|
||||||
//#define GATER_NOP
|
|
||||||
#ifdef GATER_NOP
|
|
||||||
// For testing only, don't clock gate but still make new always
|
|
||||||
AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
|
|
||||||
#else
|
|
||||||
// Make a SenGate
|
|
||||||
AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
|
|
||||||
if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");
|
|
||||||
|
|
||||||
AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
|
|
||||||
new AstSenGate(nodep->fileline(),
|
|
||||||
oldsenitemsp->cloneTree(true),
|
|
||||||
exprp));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Make new body; note clone will preserve user2p() indicating moving vars
|
|
||||||
AstNode* bodyp = nodep->bodysp()->cloneTree(true);
|
|
||||||
|
|
||||||
AstAlways* alwp = new AstAlways(nodep->fileline(),
|
|
||||||
nodep->keyword(),
|
|
||||||
sensesp,
|
|
||||||
bodyp);
|
|
||||||
|
|
||||||
alwp->user4(1); // No need to process the new always again!
|
|
||||||
nodep->addNextHere(alwp);
|
|
||||||
|
|
||||||
// Blow moved statements from old body
|
|
||||||
{ GaterBodyVisitor vis(nodep,exprp,true); }
|
|
||||||
// Blow old statements from new body
|
|
||||||
{ GaterBodyVisitor vis(alwp,exprp,false); }
|
|
||||||
|
|
||||||
++m_statGaters;
|
|
||||||
if (debug()>=9) alwp->dumpTree(cout," new: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// VISITORS
|
|
||||||
virtual void visit(AstAlways* nodep) {
|
|
||||||
if (debug()>=9) cout<<endl<<endl<<endl;
|
|
||||||
UINFO(5, "Gater: ALWAYS: "<<nodep<<endl);
|
|
||||||
if (nodep->user4SetOnce()) return;
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
if (debug()>=9) nodep->dumpTree(cout," Alwin: ");
|
|
||||||
|
|
||||||
// Look for constructs we can't optimize
|
|
||||||
// Form graph with Vertices at each IF, and each Variable
|
|
||||||
m_aboveTrue = VU_IF | VU_ELSE;
|
|
||||||
iterateChildrenAlw(nodep, true);
|
|
||||||
|
|
||||||
// Other reasons to not optimize
|
|
||||||
if (!m_numIfs) nonOptimizable(nodep, "No if statements");
|
|
||||||
|
|
||||||
// Something to optimize, oh my!
|
|
||||||
if (m_nonopt!="") {
|
|
||||||
UINFO(5, " Gater non-opt: "<<m_nonopt<<endl);
|
|
||||||
} else {
|
|
||||||
// Process it
|
|
||||||
simplifyGraph();
|
|
||||||
|
|
||||||
// See how much moves
|
|
||||||
uint32_t lastColor = 0;
|
|
||||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
||||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
|
|
||||||
if (lastColor < vVxp->color()) {
|
|
||||||
lastColor = vVxp->color();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lastColor == 0) { // Nothing we moved!
|
|
||||||
nonOptimizable(nodep, "Nothing moved");
|
|
||||||
}
|
|
||||||
else if (lastColor > DOMAINS_MAX) {
|
|
||||||
// Our move algorithm is fairly slow and if we're splitting
|
|
||||||
// up too much it'll get really nasty. It's probably a bad
|
|
||||||
// move for performance to split too much, anyhow, as the
|
|
||||||
// number of gaters will result in calling many small c functions.
|
|
||||||
nonOptimizable(nodep, "Too much moved");
|
|
||||||
}
|
|
||||||
if (m_nonopt=="") {
|
|
||||||
newAlwaysTrees(nodep);
|
|
||||||
if (debug()>=9) nodep->dumpTree(cout," Gaterout: ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UINFO(5, " Gater done"<<endl);
|
|
||||||
}
|
|
||||||
virtual void visit(AstVarRef* nodep) {
|
|
||||||
if (nodep->lvalue()) {
|
|
||||||
AstVarScope* vscp = nodep->varScopep();
|
|
||||||
if (nodep->varp()->isSigPublic()) {
|
|
||||||
// Public signals shouldn't be changed, pli code might be messing with them
|
|
||||||
scoreboardPli(nodep);
|
|
||||||
}
|
|
||||||
// If another lvalue in this node, give up optimizing.
|
|
||||||
// We could just not optimize this variable, but we've already marked the
|
|
||||||
// other variable as optimizable, so we can instead pretend it's a PLI node.
|
|
||||||
if (m_stmtVscp) {
|
|
||||||
UINFO(5, " Multiple lvalues in one statement: "<<nodep<<endl);
|
|
||||||
scoreboardPli(nodep); // This will set m_stmtInPli
|
|
||||||
}
|
|
||||||
m_stmtVscp = vscp;
|
|
||||||
// Find, or make new Vertex
|
|
||||||
GaterVarVertex* vertexp = reinterpret_cast<GaterVarVertex*>(vscp->user1p());
|
|
||||||
if (!vertexp) {
|
|
||||||
vertexp = new GaterVarVertex(&m_graph, vscp);
|
|
||||||
vscp->user1p(vertexp);
|
|
||||||
}
|
|
||||||
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
|
|
||||||
if (m_stmtInPli) {
|
|
||||||
new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void visit(AstNodeIf* nodep) {
|
|
||||||
m_ifDepth++;
|
|
||||||
bool allowGater = m_directlyUnderAlw && m_ifDepth <= IF_DEPTH_MAX;
|
|
||||||
if (allowGater) {
|
|
||||||
GaterCondVisitor condVisitor(nodep->condp());
|
|
||||||
if (!condVisitor.isSimple()) {
|
|
||||||
// Don't clear optimization, simply drop this IF as part of the gating
|
|
||||||
UINFO(5," IFnon-simple-condition: "<<nodep<<endl);
|
|
||||||
allowGater = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!allowGater) {
|
|
||||||
// IF burried under something complicated
|
|
||||||
iterateChildrenAlw(nodep, true);
|
|
||||||
} else {
|
|
||||||
UINFO(5, " IF: "<<nodep<<endl);
|
|
||||||
m_numIfs++;
|
|
||||||
// Recurse to next level of if.
|
|
||||||
V3GraphVertex* lastabovep = m_aboveVertexp;
|
|
||||||
uint32_t lasttrue = m_aboveTrue;
|
|
||||||
GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
|
|
||||||
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
|
|
||||||
{
|
|
||||||
iterateAndNextNull(nodep->condp()); // directlyUnder stays as-is
|
|
||||||
}
|
|
||||||
{
|
|
||||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
|
||||||
m_aboveTrue = VU_IF;
|
|
||||||
iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
|
||||||
m_aboveTrue = VU_ELSE;
|
|
||||||
iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true)
|
|
||||||
}
|
|
||||||
m_aboveVertexp = lastabovep;
|
|
||||||
m_aboveTrue = lasttrue;
|
|
||||||
}
|
|
||||||
m_ifDepth--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void visit(AstAssignDly* nodep) {
|
|
||||||
// iterateChildrenAlw will detect this is a statement for us
|
|
||||||
iterateChildrenAlw(nodep, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void visit(AstNodeAssign* nodep) {
|
|
||||||
// Note NOT AssignDly; handled above, We'll just mark this block as
|
|
||||||
// not optimizable.
|
|
||||||
//
|
|
||||||
// A future alternative is to look for any lvalues that are also
|
|
||||||
// variables in the sensitivity list, or rvalues in this block. If
|
|
||||||
// any hit, disable optimization. Unlikely to be useful (For loops
|
|
||||||
// being an exception, but they're already unrolled.)
|
|
||||||
nonOptimizable(nodep, "Non-delayed assignment");
|
|
||||||
// No reason to iterate.
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void visit(AstSenItem* nodep) {
|
|
||||||
if (!nodep->isClocked()) {
|
|
||||||
nonOptimizable(nodep, "Non-clocked sensitivity");
|
|
||||||
}
|
|
||||||
iterateChildrenAlw(nodep, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------
|
|
||||||
virtual void visit(AstNode* nodep) {
|
|
||||||
if (m_nonopt=="") { // Else accelerate
|
|
||||||
iterateChildrenAlw(nodep, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inline void iterateChildrenAlw(AstNode* nodep, bool under) {
|
|
||||||
// **** USE THIS INSTEAD OF iterateChildren!
|
|
||||||
// Note If visitor doesn't call here; does it its own way
|
|
||||||
bool lastdua = m_directlyUnderAlw;
|
|
||||||
AstVarScope* lastvscp = m_stmtVscp;
|
|
||||||
bool lastpli = m_stmtInPli;
|
|
||||||
m_directlyUnderAlw = under;
|
|
||||||
if (VN_IS(nodep, NodeStmt)) { // Restored below
|
|
||||||
UINFO(9," Stmt: "<<nodep<<endl);
|
|
||||||
m_stmtVscp = NULL;
|
|
||||||
m_stmtInPli = false;
|
|
||||||
}
|
|
||||||
if (!nodep->isPure() || nodep->isBrancher()) {
|
|
||||||
// May also be a new statement (above if); if so we mark it immediately
|
|
||||||
UINFO(9," NotPure "<<nodep<<endl);
|
|
||||||
scoreboardPli(nodep);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
iterateChildren(nodep);
|
|
||||||
}
|
|
||||||
m_directlyUnderAlw = lastdua;
|
|
||||||
if (VN_IS(nodep, NodeStmt)) { // Reset what set above; else propagate up to above statement
|
|
||||||
m_stmtVscp = lastvscp;
|
|
||||||
m_stmtInPli = lastpli;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// CONSTUCTORS
|
|
||||||
explicit GaterVisitor(AstNetlist* nodep) {
|
|
||||||
// AstAlways visitor does the real work, so most zeroing needs to be in clear()
|
|
||||||
clear();
|
|
||||||
iterate(nodep);
|
|
||||||
}
|
|
||||||
void clear() {
|
|
||||||
m_nonopt = "";
|
|
||||||
m_directlyUnderAlw = false;
|
|
||||||
m_ifDepth = 0;
|
|
||||||
m_numIfs = 0;
|
|
||||||
AstNode::user1ClearTree();
|
|
||||||
AstNode::user2ClearTree();
|
|
||||||
// Prepare graph
|
|
||||||
m_graph.clear();
|
|
||||||
GaterVertex::clearRank();
|
|
||||||
m_pliVertexp = NULL;
|
|
||||||
m_headVertexp = new GaterHeadVertex(&m_graph);
|
|
||||||
m_aboveVertexp = m_headVertexp;
|
|
||||||
m_aboveTrue = false;
|
|
||||||
m_stmtVscp = NULL;
|
|
||||||
m_stmtInPli = false;
|
|
||||||
}
|
|
||||||
virtual ~GaterVisitor() {
|
|
||||||
V3Stats::addStat("Optimizations, Gaters inserted", m_statGaters);
|
|
||||||
V3Stats::addStat("Optimizations, Gaters impacted bits", m_statBits);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Active class functions
|
|
||||||
|
|
||||||
void V3ClkGater::clkGaterAll(AstNetlist* nodep) {
|
|
||||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
||||||
{
|
|
||||||
// While the gater does well at some modules, it seems to slow down many others
|
|
||||||
UINFO(5,"ClkGater is disabled due to performance issues\n");
|
|
||||||
} // Destruct before checking
|
|
||||||
//GaterVisitor visitor (nodep);
|
|
||||||
V3Global::dumpCheckGlobalTree("clkgater", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
||||||
//*************************************************************************
|
|
||||||
// DESCRIPTION: Verilator: Break always into clock gated blocks
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
//*************************************************************************
|
|
||||||
|
|
||||||
#ifndef _V3CLKGATER_H_
|
|
||||||
#define _V3CLKGATER_H_ 1
|
|
||||||
|
|
||||||
#include "config_build.h"
|
|
||||||
#include "verilatedos.h"
|
|
||||||
|
|
||||||
#include "V3Error.h"
|
|
||||||
#include "V3Ast.h"
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
class V3ClkGater {
|
|
||||||
public:
|
|
||||||
static void clkGaterAll(AstNetlist* nodep);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // Guard
|
|
||||||
+277
-269
@@ -22,15 +22,15 @@
|
|||||||
// Top Scope:
|
// Top Scope:
|
||||||
// Check created ACTIVEs
|
// Check created ACTIVEs
|
||||||
// Compress adjacent ACTIVEs with same sensitivity list
|
// Compress adjacent ACTIVEs with same sensitivity list
|
||||||
// Form master _eval function
|
// Form master _eval function
|
||||||
// Add around the SENTREE a (IF POSEDGE(..))
|
// Add around the SENTREE a (IF POSEDGE(..))
|
||||||
// Add a __Vlast_{clock} for the comparison
|
// Add a __Vlast_{clock} for the comparison
|
||||||
// Set the __Vlast_{clock} at the end of the block
|
// Set the __Vlast_{clock} at the end of the block
|
||||||
// Replace UNTILSTABLEs with loops until specified signals become const.
|
// Replace UNTILSTABLEs with loops until specified signals become const.
|
||||||
// Create global calling function for any per-scope functions. (For FINALs).
|
// Create global calling function for any per-scope functions. (For FINALs).
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -49,22 +49,22 @@ class ClockVisitor : public AstNVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Cleared each Module:
|
// Cleared each Module:
|
||||||
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
|
|
||||||
// TYPES
|
// 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
|
// STATE
|
||||||
AstNodeModule* m_modp; // Current module
|
AstNodeModule* m_modp; // Current module
|
||||||
AstTopScope* m_topScopep; // Current top scope
|
AstTopScope* m_topScopep; // Current top scope
|
||||||
AstScope* m_scopep; // Current scope
|
AstScope* m_scopep; // Current scope
|
||||||
AstCFunc* m_evalFuncp; // Top eval function we are creating
|
AstCFunc* m_evalFuncp; // Top eval function we are creating
|
||||||
AstCFunc* m_initFuncp; // Top initial function we are creating
|
AstCFunc* m_initFuncp; // Top initial function we are creating
|
||||||
AstCFunc* m_finalFuncp; // Top final function we are creating
|
AstCFunc* m_finalFuncp; // Top final function we are creating
|
||||||
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
||||||
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
||||||
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
||||||
AstMTaskBody* m_mtaskBodyp; // Current mtask body
|
AstMTaskBody* m_mtaskBodyp; // Current mtask body
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
@@ -72,15 +72,18 @@ private:
|
|||||||
|
|
||||||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||||
AstVar* varp = vscp->varp();
|
AstVar* varp = vscp->varp();
|
||||||
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<<varp->prettyName());
|
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "
|
||||||
string newvarname = (string("__Vclklast__")+vscp->scopep()->nameDotless()+"__"+varp->name());
|
<<varp->prettyNameQ());
|
||||||
AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
|
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
|
newvarp->noReset(true); // Reset by below assign
|
||||||
m_modp->addStmtp(newvarp);
|
m_modp->addStmtp(newvarp);
|
||||||
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||||
vscp->user1p(newvscp);
|
vscp->user1p(newvscp);
|
||||||
m_scopep->addVarp(newvscp);
|
m_scopep->addVarp(newvscp);
|
||||||
// Add init
|
// Add init
|
||||||
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false);
|
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false);
|
||||||
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
||||||
@@ -88,243 +91,246 @@ private:
|
|||||||
new AstVarRef(newvarp->fileline(), newvscp, true),
|
new AstVarRef(newvarp->fileline(), newvscp, true),
|
||||||
fromp);
|
fromp);
|
||||||
addToInitial(newinitp);
|
addToInitial(newinitp);
|
||||||
// At bottom, assign them
|
// At bottom, assign them
|
||||||
AstAssign* finalp
|
AstAssign* finalp
|
||||||
= new AstAssign(vscp->fileline(),
|
= new AstAssign(vscp->fileline(),
|
||||||
new AstVarRef(vscp->fileline(), newvscp, true),
|
new AstVarRef(vscp->fileline(), newvscp, true),
|
||||||
new AstVarRef(vscp->fileline(), vscp, false));
|
new AstVarRef(vscp->fileline(), vscp, false));
|
||||||
m_evalFuncp->addFinalsp(finalp);
|
m_evalFuncp->addFinalsp(finalp);
|
||||||
//
|
//
|
||||||
UINFO(4,"New Last: "<<newvscp<<endl);
|
UINFO(4,"New Last: "<<newvscp<<endl);
|
||||||
return newvscp;
|
return newvscp;
|
||||||
}
|
}
|
||||||
AstNode* createSenItemEquation(AstSenItem* nodep) {
|
AstNode* createSenItemEquation(AstSenItem* nodep) {
|
||||||
// We know the var is clean, and one bit, so we use binary ops
|
// We know the var is clean, and one bit, so we use binary ops
|
||||||
// for speed instead of logical ops.
|
// for speed instead of logical ops.
|
||||||
// POSEDGE: var & ~var_last
|
// POSEDGE: var & ~var_last
|
||||||
// NEGEDGE: ~var & var_last
|
// NEGEDGE: ~var & var_last
|
||||||
// BOTHEDGE: var ^ var_last
|
// BOTHEDGE: var ^ var_last
|
||||||
// HIGHEDGE: var
|
// HIGHEDGE: var
|
||||||
// LOWEDGE: ~var
|
// LOWEDGE: ~var
|
||||||
AstNode* newp = NULL;
|
AstNode* newp = NULL;
|
||||||
if (nodep->edgeType()==AstEdgeType::ET_ILLEGAL) {
|
if (nodep->edgeType()==VEdgeType::ET_ILLEGAL) {
|
||||||
if (!v3Global.opt.bboxUnsup()) {
|
if (!v3Global.opt.bboxUnsup()) {
|
||||||
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
|
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
|
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
|
||||||
if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) {
|
if (nodep->edgeType() == VEdgeType::ET_POSEDGE) {
|
||||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||||
newp = new AstAnd(nodep->fileline(),
|
newp = new AstAnd(nodep->fileline(),
|
||||||
new AstVarRef(nodep->fileline(),
|
new AstVarRef(nodep->fileline(),
|
||||||
nodep->varrefp()->varScopep(), false),
|
nodep->varrefp()->varScopep(), false),
|
||||||
new AstNot(nodep->fileline(),
|
new AstNot(nodep->fileline(),
|
||||||
new AstVarRef(nodep->fileline(),
|
new AstVarRef(nodep->fileline(),
|
||||||
lastVscp, false)));
|
lastVscp, false)));
|
||||||
} else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) {
|
} else if (nodep->edgeType() == VEdgeType::ET_NEGEDGE) {
|
||||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||||
newp = new AstAnd(nodep->fileline(),
|
newp = new AstAnd(nodep->fileline(),
|
||||||
new AstNot(nodep->fileline(),
|
new AstNot(nodep->fileline(),
|
||||||
new AstVarRef(nodep->fileline(),
|
new AstVarRef(nodep->fileline(),
|
||||||
nodep->varrefp()->varScopep(), false)),
|
nodep->varrefp()->varScopep(), false)),
|
||||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||||
} else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) {
|
} else if (nodep->edgeType() == VEdgeType::ET_BOTHEDGE) {
|
||||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||||
newp = new AstXor(nodep->fileline(),
|
newp = new AstXor(nodep->fileline(),
|
||||||
new AstVarRef(nodep->fileline(),
|
new AstVarRef(nodep->fileline(),
|
||||||
nodep->varrefp()->varScopep(), false),
|
nodep->varrefp()->varScopep(), false),
|
||||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||||
} else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) {
|
} else if (nodep->edgeType() == VEdgeType::ET_HIGHEDGE) {
|
||||||
newp = new AstVarRef(nodep->fileline(),
|
newp = new AstVarRef(nodep->fileline(),
|
||||||
clkvscp, false);
|
clkvscp, false);
|
||||||
} else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) {
|
} else if (nodep->edgeType() == VEdgeType::ET_LOWEDGE) {
|
||||||
newp = new AstNot(nodep->fileline(),
|
newp = new AstNot(nodep->fileline(),
|
||||||
new AstVarRef(nodep->fileline(),
|
new AstVarRef(nodep->fileline(),
|
||||||
clkvscp, false));
|
clkvscp, false));
|
||||||
} else {
|
} else {
|
||||||
nodep->v3fatalSrc("Bad edge type");
|
nodep->v3fatalSrc("Bad edge type");
|
||||||
}
|
}
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
AstNode* createSenGateEquation(AstSenGate* nodep) {
|
AstNode* createSenGateEquation(AstSenGate* nodep) {
|
||||||
AstNode* newp = new AstAnd(nodep->fileline(),
|
AstNode* newp = new AstAnd(nodep->fileline(),
|
||||||
createSenseEquation(nodep->sensesp()),
|
createSenseEquation(nodep->sensesp()),
|
||||||
nodep->rhsp()->cloneTree(true));
|
nodep->rhsp()->cloneTree(true));
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
|
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
|
||||||
// Nodep may be a list of elements; we need to walk it
|
// Nodep may be a list of elements; we need to walk it
|
||||||
AstNode* senEqnp = NULL;
|
AstNode* senEqnp = NULL;
|
||||||
for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
for (AstNodeSenItem* senp = nodesp; senp; senp = VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||||
AstNode* senOnep = NULL;
|
AstNode* senOnep = NULL;
|
||||||
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
|
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
|
||||||
senOnep = createSenItemEquation(itemp);
|
senOnep = createSenItemEquation(itemp);
|
||||||
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
|
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
|
||||||
senOnep = createSenGateEquation(itemp);
|
senOnep = createSenGateEquation(itemp);
|
||||||
} else {
|
} else {
|
||||||
senp->v3fatalSrc("Strange node under sentree");
|
senp->v3fatalSrc("Strange node under sentree");
|
||||||
}
|
}
|
||||||
if (senEqnp) {
|
if (senEqnp) {
|
||||||
// Add new OR to the sensitivity list equation
|
// Add new OR to the sensitivity list equation
|
||||||
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
|
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
|
||||||
} else {
|
} else {
|
||||||
senEqnp = senOnep;
|
senEqnp = senOnep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return senEqnp;
|
return senEqnp;
|
||||||
}
|
}
|
||||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||||
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
||||||
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
|
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
|
||||||
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL);
|
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL);
|
||||||
return (newifp);
|
return (newifp);
|
||||||
}
|
}
|
||||||
void clearLastSen() {
|
void clearLastSen() {
|
||||||
m_lastSenp = NULL;
|
m_lastSenp = NULL;
|
||||||
m_lastIfp = NULL;
|
m_lastIfp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstTopScope* nodep) {
|
virtual void visit(AstTopScope* nodep) {
|
||||||
UINFO(4," TOPSCOPE "<<nodep<<endl);
|
UINFO(4," TOPSCOPE "<<nodep<<endl);
|
||||||
m_topScopep=nodep;
|
m_topScopep = nodep;
|
||||||
m_scopep = nodep->scopep();
|
m_scopep = nodep->scopep();
|
||||||
if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
|
UASSERT_OBJ(m_scopep, nodep,
|
||||||
//VV***** We reset all user1p()
|
"No scope found on top level, perhaps you have no statements?");
|
||||||
AstNode::user1ClearTree();
|
//VV***** We reset all user1p()
|
||||||
// Make top functions
|
AstNode::user1ClearTree();
|
||||||
{
|
// Make top functions
|
||||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
|
{
|
||||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
|
||||||
funcp->dontCombine(true);
|
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||||
funcp->symProlog(true);
|
funcp->dontCombine(true);
|
||||||
funcp->isStatic(true);
|
funcp->symProlog(true);
|
||||||
funcp->entryPoint(true);
|
funcp->isStatic(true);
|
||||||
m_scopep->addActivep(funcp);
|
funcp->entryPoint(true);
|
||||||
m_evalFuncp = funcp;
|
m_scopep->addActivep(funcp);
|
||||||
}
|
m_evalFuncp = funcp;
|
||||||
{
|
}
|
||||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
|
{
|
||||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
|
||||||
funcp->dontCombine(true);
|
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||||
funcp->slow(true);
|
funcp->dontCombine(true);
|
||||||
funcp->symProlog(true);
|
funcp->slow(true);
|
||||||
funcp->isStatic(true);
|
funcp->symProlog(true);
|
||||||
funcp->entryPoint(true);
|
funcp->isStatic(true);
|
||||||
m_scopep->addActivep(funcp);
|
funcp->entryPoint(true);
|
||||||
m_initFuncp = funcp;
|
m_scopep->addActivep(funcp);
|
||||||
}
|
m_initFuncp = funcp;
|
||||||
{
|
}
|
||||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep);
|
{
|
||||||
funcp->skipDecl(true);
|
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep);
|
||||||
funcp->dontCombine(true);
|
funcp->skipDecl(true);
|
||||||
funcp->slow(true);
|
funcp->dontCombine(true);
|
||||||
funcp->isStatic(false);
|
funcp->slow(true);
|
||||||
funcp->entryPoint(true);
|
funcp->isStatic(false);
|
||||||
funcp->addInitsp(new AstCStmt(nodep->fileline(),
|
funcp->entryPoint(true);
|
||||||
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
funcp->addInitsp(new AstCStmt
|
||||||
funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
(nodep->fileline(),
|
||||||
m_scopep->addActivep(funcp);
|
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||||
m_finalFuncp = funcp;
|
funcp->addInitsp(new AstCStmt(nodep->fileline(),
|
||||||
}
|
EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||||
{
|
m_scopep->addActivep(funcp);
|
||||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
|
m_finalFuncp = funcp;
|
||||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
}
|
||||||
funcp->dontCombine(true);
|
{
|
||||||
funcp->slow(true);
|
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
|
||||||
funcp->isStatic(true);
|
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||||
funcp->symProlog(true);
|
funcp->dontCombine(true);
|
||||||
funcp->entryPoint(true);
|
funcp->slow(true);
|
||||||
m_scopep->addActivep(funcp);
|
funcp->isStatic(true);
|
||||||
m_settleFuncp = funcp;
|
funcp->symProlog(true);
|
||||||
}
|
funcp->entryPoint(true);
|
||||||
// Process the activates
|
m_scopep->addActivep(funcp);
|
||||||
|
m_settleFuncp = funcp;
|
||||||
|
}
|
||||||
|
// Process the activates
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
// Done, clear so we can detect errors
|
// Done, clear so we can detect errors
|
||||||
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
||||||
clearLastSen();
|
clearLastSen();
|
||||||
m_topScopep=NULL;
|
m_topScopep = NULL;
|
||||||
m_scopep = NULL;
|
m_scopep = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
//UINFO(4," MOD "<<nodep<<endl);
|
//UINFO(4," MOD "<<nodep<<endl);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_modp= NULL;
|
m_modp= NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstScope* nodep) {
|
virtual void visit(AstScope* nodep) {
|
||||||
//UINFO(4," SCOPE "<<nodep<<endl);
|
//UINFO(4," SCOPE "<<nodep<<endl);
|
||||||
m_scopep = nodep;
|
m_scopep = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (AstNode* movep = nodep->finalClksp()) {
|
if (AstNode* movep = nodep->finalClksp()) {
|
||||||
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
|
UASSERT_OBJ(m_topScopep, nodep, "Final clocks under non-top scope");
|
||||||
movep->unlinkFrBackWithNext();
|
movep->unlinkFrBackWithNext();
|
||||||
m_evalFuncp->addFinalsp(movep);
|
m_evalFuncp->addFinalsp(movep);
|
||||||
}
|
}
|
||||||
m_scopep = NULL;
|
m_scopep = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlways* nodep) {
|
virtual void visit(AstAlways* nodep) {
|
||||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName(), true);
|
||||||
nodep->replaceWith(cmtp);
|
nodep->replaceWith(cmtp);
|
||||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||||
stmtsp->unlinkFrBackWithNext();
|
stmtsp->unlinkFrBackWithNext();
|
||||||
cmtp->addNextHere(stmtsp);
|
cmtp->addNextHere(stmtsp);
|
||||||
}
|
}
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlwaysPost* nodep) {
|
virtual void visit(AstAlwaysPost* nodep) {
|
||||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName(), true);
|
||||||
nodep->replaceWith(cmtp);
|
nodep->replaceWith(cmtp);
|
||||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||||
stmtsp->unlinkFrBackWithNext();
|
stmtsp->unlinkFrBackWithNext();
|
||||||
cmtp->addNextHere(stmtsp);
|
cmtp->addNextHere(stmtsp);
|
||||||
}
|
}
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstCoverToggle* nodep) {
|
virtual void visit(AstCoverToggle* nodep) {
|
||||||
//nodep->dumpTree(cout,"ct:");
|
//nodep->dumpTree(cout, "ct:");
|
||||||
//COVERTOGGLE(INC, ORIG, CHANGE) ->
|
//COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||||
AstNode* incp = nodep->incp()->unlinkFrBack();
|
AstNode* incp = nodep->incp()->unlinkFrBack();
|
||||||
AstNode* origp = nodep->origp()->unlinkFrBack();
|
AstNode* origp = nodep->origp()->unlinkFrBack();
|
||||||
AstNode* changep = nodep->changep()->unlinkFrBack();
|
AstNode* changep = nodep->changep()->unlinkFrBack();
|
||||||
AstIf* newp = new AstIf(nodep->fileline(),
|
AstIf* newp = new AstIf(nodep->fileline(),
|
||||||
new AstXor(nodep->fileline(),
|
new AstXor(nodep->fileline(),
|
||||||
origp,
|
origp,
|
||||||
changep),
|
changep),
|
||||||
incp, NULL);
|
incp, NULL);
|
||||||
// We could add another IF to detect posedges, and only increment if so.
|
// We could add another IF to detect posedges, and only increment if so.
|
||||||
// It's another whole branch though verus a potential memory miss.
|
// It's another whole branch though versus a potential memory miss.
|
||||||
// We'll go with the miss.
|
// We'll go with the miss.
|
||||||
newp->addIfsp(new AstAssign(nodep->fileline(),
|
newp->addIfsp(new AstAssign(nodep->fileline(),
|
||||||
changep->cloneTree(false),
|
changep->cloneTree(false),
|
||||||
origp->cloneTree(false)));
|
origp->cloneTree(false)));
|
||||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstInitial* nodep) {
|
virtual void visit(AstInitial* nodep) {
|
||||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
|
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName(), true);
|
||||||
nodep->replaceWith(cmtp);
|
nodep->replaceWith(cmtp);
|
||||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||||
stmtsp->unlinkFrBackWithNext();
|
stmtsp->unlinkFrBackWithNext();
|
||||||
cmtp->addNextHere(stmtsp);
|
cmtp->addNextHere(stmtsp);
|
||||||
}
|
}
|
||||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstCFunc* nodep) {
|
virtual void visit(AstCFunc* nodep) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
// Link to global function
|
// Link to global function
|
||||||
if (nodep->formCallTree()) {
|
if (nodep->formCallTree()) {
|
||||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||||
callp->argTypes("vlSymsp");
|
callp->argTypes("vlSymsp");
|
||||||
m_finalFuncp->addStmtsp(callp);
|
m_finalFuncp->addStmtsp(callp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstSenTree* nodep) {
|
virtual void visit(AstSenTree* nodep) {
|
||||||
// Delete it later; Actives still pointing to it
|
// Delete it later; Actives still pointing to it
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
pushDeletep(nodep);
|
pushDeletep(nodep);
|
||||||
}
|
}
|
||||||
void addToEvalLoop(AstNode* stmtsp) {
|
void addToEvalLoop(AstNode* stmtsp) {
|
||||||
m_evalFuncp->addStmtsp(stmtsp); // add to top level function
|
m_evalFuncp->addStmtsp(stmtsp); // add to top level function
|
||||||
@@ -336,18 +342,19 @@ private:
|
|||||||
m_initFuncp->addStmtsp(stmtsp); // add to top level function
|
m_initFuncp->addStmtsp(stmtsp); // add to top level function
|
||||||
}
|
}
|
||||||
virtual void visit(AstActive* nodep) {
|
virtual void visit(AstActive* nodep) {
|
||||||
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
||||||
// Need to save and restore any member state in AstUntilStable block
|
// Need to save and restore any member state in AstUntilStable block
|
||||||
if (!m_topScopep || !nodep->stmtsp()) {
|
if (!m_topScopep || !nodep->stmtsp()) {
|
||||||
// Not at the top or empty block...
|
// Not at the top or empty block...
|
||||||
// Only empty blocks should be leftover on the non-top. Killem.
|
// Only empty blocks should be leftover on the non-top. Killem.
|
||||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
UASSERT_OBJ(!nodep->stmtsp(), nodep, "Non-empty lower active");
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
} else if (m_mtaskBodyp) {
|
} else if (m_mtaskBodyp) {
|
||||||
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
||||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||||
if (nodep->hasClocked()) {
|
if (nodep->hasClocked()) {
|
||||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||||
|
"Initial block should not have clock sensitivity");
|
||||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||||
UINFO(4," sameSenseTree\n");
|
UINFO(4," sameSenseTree\n");
|
||||||
} else {
|
} else {
|
||||||
@@ -367,39 +374,40 @@ private:
|
|||||||
m_mtaskBodyp->addStmtsp(stmtsp);
|
m_mtaskBodyp->addStmtsp(stmtsp);
|
||||||
}
|
}
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
} else {
|
} else {
|
||||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||||
if (nodep->hasClocked()) {
|
if (nodep->hasClocked()) {
|
||||||
// Remember the latest sensitivity so we can compare it next time
|
// Remember the latest sensitivity so we can compare it next time
|
||||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
"Initial block should not have clock sensitivity");
|
||||||
UINFO(4," sameSenseTree\n");
|
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||||
} else {
|
UINFO(4," sameSenseTree\n");
|
||||||
clearLastSen();
|
} else {
|
||||||
m_lastSenp = nodep->sensesp();
|
clearLastSen();
|
||||||
// Make a new if statement
|
m_lastSenp = nodep->sensesp();
|
||||||
m_lastIfp = makeActiveIf(m_lastSenp);
|
// Make a new if statement
|
||||||
addToEvalLoop(m_lastIfp);
|
m_lastIfp = makeActiveIf(m_lastSenp);
|
||||||
}
|
addToEvalLoop(m_lastIfp);
|
||||||
// Move statements to if
|
}
|
||||||
m_lastIfp->addIfsp(stmtsp);
|
// Move statements to if
|
||||||
} else if (nodep->hasInitial()) {
|
m_lastIfp->addIfsp(stmtsp);
|
||||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
} else if (nodep->hasInitial()) {
|
||||||
// Move statements to function
|
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||||
addToInitial(stmtsp);
|
// Move statements to function
|
||||||
} else if (nodep->hasSettle()) {
|
addToInitial(stmtsp);
|
||||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
} else if (nodep->hasSettle()) {
|
||||||
// Move statements to function
|
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||||
addToSettleLoop(stmtsp);
|
// Move statements to function
|
||||||
} else {
|
addToSettleLoop(stmtsp);
|
||||||
// Combo
|
} else {
|
||||||
clearLastSen();
|
// Combo
|
||||||
// Move statements to function
|
clearLastSen();
|
||||||
addToEvalLoop(stmtsp);
|
// Move statements to function
|
||||||
}
|
addToEvalLoop(stmtsp);
|
||||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
}
|
||||||
}
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstExecGraph* nodep) {
|
virtual void visit(AstExecGraph* nodep) {
|
||||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
|
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
|
||||||
@@ -423,19 +431,19 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit ClockVisitor(AstNetlist* nodep) {
|
explicit ClockVisitor(AstNetlist* nodep) {
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
m_evalFuncp = NULL;
|
m_evalFuncp = NULL;
|
||||||
m_initFuncp = NULL;
|
m_initFuncp = NULL;
|
||||||
m_finalFuncp = NULL;
|
m_finalFuncp = NULL;
|
||||||
m_settleFuncp = NULL;
|
m_settleFuncp = NULL;
|
||||||
m_topScopep = NULL;
|
m_topScopep = NULL;
|
||||||
m_lastSenp = NULL;
|
m_lastSenp = NULL;
|
||||||
m_lastIfp = NULL;
|
m_lastIfp = NULL;
|
||||||
m_scopep = NULL;
|
m_scopep = NULL;
|
||||||
m_mtaskBodyp = NULL;
|
m_mtaskBodyp = NULL;
|
||||||
//
|
//
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
// Allow downstream modules to find _eval()
|
// Allow downstream modules to find _eval()
|
||||||
// easily without iterating through the tree.
|
// easily without iterating through the tree.
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3CLOCK_H_
|
#ifndef _V3CLOCK_H_
|
||||||
#define _V3CLOCK_H_ 1
|
#define _V3CLOCK_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void clockAll(AstNetlist* nodep);
|
static void clockAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+278
-272
@@ -19,18 +19,18 @@
|
|||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// V3Combine's Transformations:
|
// V3Combine's Transformations:
|
||||||
//
|
//
|
||||||
// For every function that we spit out
|
// For every function that we spit out
|
||||||
// Examine code to find largest common blocks
|
// Examine code to find largest common blocks
|
||||||
// Hash each node depth first
|
// Hash each node depth first
|
||||||
// Hash includes varp name and operator type, and constants
|
// Hash includes varp name and operator type, and constants
|
||||||
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
||||||
// GO through table
|
// GO through table
|
||||||
// Lookup in hash, while next of each statement match, grow that common block
|
// Lookup in hash, while next of each statement match, grow that common block
|
||||||
// Foreach common block
|
// Foreach common block
|
||||||
// If common block large enough (> 20 statements) & used 2x or more
|
// If common block large enough (> 20 statements) & used 2x or more
|
||||||
// Make new function
|
// Make new function
|
||||||
// Move common block to function
|
// Move common block to function
|
||||||
// Replace each common block ref with funccall
|
// 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
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ protected:
|
|||||||
// Note this is disabled, it still needed work
|
// Note this is disabled, it still needed work
|
||||||
// Also repair it for DPI functions; when make __common need to insure proper
|
// Also repair it for DPI functions; when make __common need to insure proper
|
||||||
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
|
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
|
||||||
// get split between functions causing the text to have a danginling reference.
|
// get split between functions causing the text to have a dangling reference.
|
||||||
bool statementCombine() { return false; } // duplicateFunctionCombine();
|
bool statementCombine() { return false; } // duplicateFunctionCombine();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,57 +79,60 @@ class CombCallVisitor : CombBaseVisitor {
|
|||||||
// Find all CCALLS of each CFUNC, so that we can later rename them
|
// Find all CCALLS of each CFUNC, so that we can later rename them
|
||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
bool m_find; // Find mode vs. delete mode
|
bool m_find; // Find mode vs. delete mode
|
||||||
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
|
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||||
// METHODS
|
// METHODS
|
||||||
public:
|
public:
|
||||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||||
if (oldfuncp==newfuncp) return;
|
if (oldfuncp==newfuncp) return;
|
||||||
if (newfuncp) {
|
if (newfuncp) {
|
||||||
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
||||||
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
||||||
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
|
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange
|
||||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
= m_callMmap.equal_range(oldfuncp);
|
||||||
CallMmap::iterator eqit = nextit++;
|
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||||
AstCCall* callp = eqit->second;
|
CallMmap::iterator eqit = nextit++;
|
||||||
if (!callp->user3()) { // !already done
|
AstCCall* callp = eqit->second;
|
||||||
UINFO(4, " Called "<<callp<<endl);
|
if (!callp->user3()) { // !already done
|
||||||
if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func");
|
UINFO(4, " Called "<<callp<<endl);
|
||||||
if (newfuncp) {
|
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
|
||||||
AstCCall* newp = new AstCCall(callp, newfuncp);
|
"Call list broken, points to call w/different func");
|
||||||
// Special new AstCCall form above transfers children of callp to newfuncp
|
if (newfuncp) {
|
||||||
callp->replaceWith(newp);
|
AstCCall* newp = new AstCCall(callp, newfuncp);
|
||||||
addCall(newp); // Fix the table
|
// Special new AstCCall form above transfers children of callp to newfuncp
|
||||||
} else { // Just deleting empty function
|
callp->replaceWith(newp);
|
||||||
callp->unlinkFrBack();
|
addCall(newp); // Fix the table
|
||||||
}
|
} else { // Just deleting empty function
|
||||||
callp->user3(true); // Dead now
|
callp->unlinkFrBack();
|
||||||
pushDeletep(callp); VL_DANGLING(callp);
|
}
|
||||||
m_callMmap.erase(eqit); // Fix the table
|
callp->user3(true); // Dead now
|
||||||
}
|
pushDeletep(callp); VL_DANGLING(callp);
|
||||||
}
|
m_callMmap.erase(eqit); // Fix the table
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// METHODS
|
// METHODS
|
||||||
void addCall(AstCCall* nodep) {
|
void addCall(AstCCall* nodep) {
|
||||||
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
||||||
}
|
}
|
||||||
void deleteCall(AstCCall* nodep) {
|
void deleteCall(AstCCall* nodep) {
|
||||||
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
|
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange
|
||||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
= m_callMmap.equal_range(nodep->funcp());
|
||||||
CallMmap::iterator eqit = nextit++;
|
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||||
AstCCall* callp = eqit->second;
|
CallMmap::iterator eqit = nextit++;
|
||||||
if (callp==nodep) {
|
AstCCall* callp = eqit->second;
|
||||||
m_callMmap.erase(eqit);
|
if (callp==nodep) {
|
||||||
return;
|
m_callMmap.erase(eqit);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
nodep->v3fatalSrc("deleteCall node not found in table");
|
}
|
||||||
|
nodep->v3fatalSrc("deleteCall node not found in table");
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstCCall* nodep) {
|
virtual void visit(AstCCall* nodep) {
|
||||||
addCall(nodep);
|
addCall(nodep);
|
||||||
}
|
}
|
||||||
// Speed things up
|
// Speed things up
|
||||||
virtual void visit(AstNodeAssign* nodep) {}
|
virtual void visit(AstNodeAssign* nodep) {}
|
||||||
@@ -140,7 +143,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
CombCallVisitor() {
|
CombCallVisitor() {
|
||||||
m_find = false;
|
m_find = false;
|
||||||
}
|
}
|
||||||
virtual ~CombCallVisitor() {}
|
virtual ~CombCallVisitor() {}
|
||||||
void main(AstNetlist* nodep) {
|
void main(AstNetlist* nodep) {
|
||||||
@@ -155,10 +158,10 @@ class CombMarkVisitor : CombBaseVisitor {
|
|||||||
// Mark all nodes under specified one.
|
// Mark all nodes under specified one.
|
||||||
private:
|
private:
|
||||||
// OUTPUT:
|
// OUTPUT:
|
||||||
// AstNode::user3() -> bool. True to indicate duplicated
|
// AstNode::user3() -> bool. True to indicate duplicated
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNode* nodep) {
|
virtual void visit(AstNode* nodep) {
|
||||||
nodep->user3(true);
|
nodep->user3(true);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@@ -176,273 +179,276 @@ class CombineVisitor : CombBaseVisitor {
|
|||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstNodeStmt::user() -> bool. True if iterated already
|
// AstNodeStmt::user() -> bool. True if iterated already
|
||||||
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
||||||
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
||||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
|
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||||
AstUser1InUse m_inuser1;
|
AstUser1InUse m_inuser1;
|
||||||
AstUser3InUse m_inuser3;
|
AstUser3InUse m_inuser3;
|
||||||
//AstUser4InUse part of V3Hashed
|
//AstUser4InUse part of V3Hashed
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState;
|
typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState;
|
||||||
V3Double0 m_statCombs; // Statistic tracking
|
VDouble0 m_statCombs; // Statistic tracking
|
||||||
CombineState m_state; // Major state
|
CombineState m_state; // Major state
|
||||||
AstNodeModule* m_modp; // Current module
|
AstNodeModule* m_modp; // Current module
|
||||||
AstCFunc* m_funcp; // Current function
|
AstCFunc* m_funcp; // Current function
|
||||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||||
CombCallVisitor m_call; // Tracking of function call users
|
CombCallVisitor m_call; // Tracking of function call users
|
||||||
int m_modNFuncs; // Number of functions made
|
int m_modNFuncs; // Number of functions made
|
||||||
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
||||||
AstNode* m_walkLast2p; // 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
|
V3Hashed m_hashed; // Hash for every node in module
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void hashStatement(AstNode* nodep) {
|
void hashStatement(AstNode* nodep) {
|
||||||
// Compute hash on entire tree of this statement
|
// Compute hash on entire tree of this statement
|
||||||
m_hashed.hashAndInsert(nodep);
|
m_hashed.hashAndInsert(nodep);
|
||||||
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
|
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
|
||||||
}
|
}
|
||||||
void hashFunctions(AstCFunc* nodep) {
|
void hashFunctions(AstCFunc* nodep) {
|
||||||
// Compute hash of all statement trees in the function
|
// Compute hash of all statement trees in the function
|
||||||
CombineState oldState = m_state;
|
CombineState oldState = m_state;
|
||||||
{
|
{
|
||||||
m_state = STATE_HASH;
|
m_state = STATE_HASH;
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
m_state = oldState;
|
m_state = oldState;
|
||||||
}
|
}
|
||||||
void walkEmptyFuncs() {
|
void walkEmptyFuncs() {
|
||||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||||
AstNode* node1p = it->second;
|
AstNode* node1p = it->second;
|
||||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||||
if (oldfuncp
|
if (oldfuncp
|
||||||
&& oldfuncp->emptyBody()
|
&& oldfuncp->emptyBody()
|
||||||
&& !oldfuncp->dontCombine()) {
|
&& !oldfuncp->dontCombine()) {
|
||||||
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())
|
||||||
// Mark user3p on entire old tree, so we don't process it more
|
<<" "<<oldfuncp<<endl);
|
||||||
CombMarkVisitor visitor(oldfuncp);
|
// Mark user3p on entire old tree, so we don't process it more
|
||||||
m_call.replaceFunc(oldfuncp, NULL);
|
CombMarkVisitor visitor(oldfuncp);
|
||||||
oldfuncp->unlinkFrBack();
|
m_call.replaceFunc(oldfuncp, NULL);
|
||||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
oldfuncp->unlinkFrBack();
|
||||||
}
|
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void walkDupFuncs() {
|
void walkDupFuncs() {
|
||||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||||
V3Hash hashval = it->first;
|
V3Hash hashval = it->first;
|
||||||
AstNode* node1p = it->second;
|
AstNode* node1p = it->second;
|
||||||
if (!VN_IS(node1p, CFunc)) continue;
|
if (!VN_IS(node1p, CFunc)) continue;
|
||||||
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
|
UASSERT_OBJ(!hashval.isIllegal(), node1p, "Illegal (unhashed) nodes");
|
||||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||||
AstNode* node2p = eqit->second;
|
AstNode* node2p = eqit->second;
|
||||||
if (!(eqit->first == hashval)) break;
|
if (!(eqit->first == hashval)) break;
|
||||||
if (node1p==node2p) continue; // Identical iterator
|
if (node1p==node2p) continue; // Identical iterator
|
||||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||||
// Replace AstCCall's that point here
|
// Replace AstCCall's that point here
|
||||||
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
|
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);
|
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||||
UINFO(5," DupFunc "<<std::hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
UINFO(5," DupFunc "<<std::hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
||||||
UINFO(5," and "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
UINFO(5," and "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||||
// Mark user3p on entire old tree, so we don't process it more
|
// Mark user3p on entire old tree, so we don't process it more
|
||||||
++m_statCombs;
|
++m_statCombs;
|
||||||
CombMarkVisitor visitor(oldfuncp);
|
CombMarkVisitor visitor(oldfuncp);
|
||||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||||
oldfuncp->unlinkFrBack();
|
oldfuncp->unlinkFrBack();
|
||||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||||
}
|
}
|
||||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||||
//oldfuncp->dumpTree(cout,"MAYDEL: ");
|
//oldfuncp->dumpTree(cout, "MAYDEL: ");
|
||||||
if (nodep->nextp()==NULL
|
if (nodep->nextp()==NULL
|
||||||
&& oldfuncp->initsp()==NULL
|
&& oldfuncp->initsp()==NULL
|
||||||
&& oldfuncp->stmtsp()==nodep
|
&& oldfuncp->stmtsp()==nodep
|
||||||
&& oldfuncp->finalsp()==NULL) {
|
&& oldfuncp->finalsp()==NULL) {
|
||||||
UINFO(9," Function only has call "<<oldfuncp<<endl);
|
UINFO(9," Function only has call "<<oldfuncp<<endl);
|
||||||
m_call.deleteCall(nodep);
|
m_call.deleteCall(nodep);
|
||||||
CombMarkVisitor visitor(oldfuncp);
|
CombMarkVisitor visitor(oldfuncp);
|
||||||
replaceFuncWFunc(oldfuncp, nodep->funcp()); VL_DANGLING(nodep);
|
replaceFuncWFunc(oldfuncp, nodep->funcp()); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void walkDupCodeStart(AstNode* node1p) {
|
void walkDupCodeStart(AstNode* node1p) {
|
||||||
V3Hash hashval(node1p->user4p());
|
V3Hash hashval(node1p->user4p());
|
||||||
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
|
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
|
||||||
//
|
//
|
||||||
int bestDepth = 0; // Best substitution found in the search
|
int bestDepth = 0; // Best substitution found in the search
|
||||||
AstNode* bestNode2p = NULL;
|
AstNode* bestNode2p = NULL;
|
||||||
AstNode* bestLast1p = NULL;
|
AstNode* bestLast1p = NULL;
|
||||||
AstNode* bestLast2p = NULL;
|
AstNode* bestLast2p = NULL;
|
||||||
//
|
//
|
||||||
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
|
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange
|
||||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
= m_hashed.mmap().equal_range(hashval);
|
||||||
AstNode* node2p = eqit->second;
|
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||||
if (node1p==node2p) continue;
|
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
|
// We need to mark iteration to prevent matching code inside
|
||||||
m_walkLast1p = NULL;
|
// code (abab matching in ababab)
|
||||||
m_walkLast2p = NULL;
|
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||||
int depth = walkDupCodeNext(node1p, node2p, 1);
|
m_walkLast1p = NULL;
|
||||||
if (depth>COMBINE_MIN_STATEMENTS
|
m_walkLast2p = NULL;
|
||||||
&& depth>bestDepth) {
|
int depth = walkDupCodeNext(node1p, node2p, 1);
|
||||||
bestDepth = depth;
|
if (depth>COMBINE_MIN_STATEMENTS
|
||||||
bestNode2p = node2p;
|
&& depth>bestDepth) {
|
||||||
bestLast1p = m_walkLast1p;
|
bestDepth = depth;
|
||||||
bestLast2p = m_walkLast2p;
|
bestNode2p = node2p;
|
||||||
}
|
bestLast1p = m_walkLast1p;
|
||||||
}
|
bestLast2p = m_walkLast2p;
|
||||||
if (bestDepth) {
|
}
|
||||||
// Found a replacement
|
}
|
||||||
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
|
if (bestDepth) {
|
||||||
UINFO(5," DupFunc "<<" "<<node1p<<endl);
|
// Found a replacement
|
||||||
UINFO(5," and "<<" "<<bestNode2p<<endl);
|
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
|
||||||
UINFO(5," Through "<<" "<<bestLast1p<<endl);
|
UINFO(5," DupFunc "<<" "<<node1p<<endl);
|
||||||
UINFO(5," and "<<" "<<bestLast2p<<endl);
|
UINFO(5," and "<<" "<<bestNode2p<<endl);
|
||||||
//
|
UINFO(5," Through "<<" "<<bestLast1p<<endl);
|
||||||
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
UINFO(5," and "<<" "<<bestLast2p<<endl);
|
||||||
}
|
//
|
||||||
|
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
|
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
|
||||||
// Find number of common statements between the two node1p_nextp's...
|
// Find number of common statements between the two node1p_nextp's...
|
||||||
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
||||||
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
||||||
if (!m_hashed.sameNodes(node1p,node2p)) return 0; // walk of tree has same comparison
|
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
|
||||||
V3Hash hashval(node1p->user4p());
|
V3Hash hashval(node1p->user4p());
|
||||||
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||||
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||||
m_walkLast1p = node1p;
|
m_walkLast1p = node1p;
|
||||||
m_walkLast2p = node2p;
|
m_walkLast2p = node2p;
|
||||||
node1p->user1(true);
|
node1p->user1(true);
|
||||||
node2p->user1(true);
|
node2p->user1(true);
|
||||||
if (node1p->nextp() && node2p->nextp()) {
|
if (node1p->nextp() && node2p->nextp()) {
|
||||||
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
|
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
|
||||||
}
|
}
|
||||||
return hashval.depth();
|
return hashval.depth();
|
||||||
}
|
}
|
||||||
|
|
||||||
void walkReplace(AstNode* node1p, AstNode* node2p,
|
void walkReplace(AstNode* node1p, AstNode* node2p,
|
||||||
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
|
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
|
||||||
// Make new function
|
// Make new function
|
||||||
string oldname = m_funcp->name();
|
string oldname = m_funcp->name();
|
||||||
string::size_type pos;
|
string::size_type pos;
|
||||||
if ((pos=oldname.find("_common")) != string::npos) {
|
if ((pos = oldname.find("_common")) != string::npos) {
|
||||||
oldname.erase(pos);
|
oldname.erase(pos);
|
||||||
}
|
}
|
||||||
if ((pos=oldname.find("__")) != string::npos) {
|
if ((pos = oldname.find("__")) != string::npos) {
|
||||||
oldname.erase(pos);
|
oldname.erase(pos);
|
||||||
}
|
}
|
||||||
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
||||||
oldname+"_common"+cvtToStr(++m_modNFuncs),
|
oldname+"_common"+cvtToStr(++m_modNFuncs),
|
||||||
NULL);
|
NULL);
|
||||||
m_modp->addStmtp(newfuncp);
|
m_modp->addStmtp(newfuncp);
|
||||||
// Create calls
|
// Create calls
|
||||||
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
||||||
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
||||||
// Grab statement bodies
|
// Grab statement bodies
|
||||||
AstNRelinker relink1Handle;
|
AstNRelinker relink1Handle;
|
||||||
AstNRelinker relink2Handle;
|
AstNRelinker relink2Handle;
|
||||||
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
|
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
|
||||||
nextp = walkp->nextp();
|
nextp = walkp->nextp();
|
||||||
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
|
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
|
||||||
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
|
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
|
||||||
if (walkp==last1p) break;
|
if (walkp==last1p) break;
|
||||||
}
|
}
|
||||||
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
|
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
|
||||||
nextp = walkp->nextp();
|
nextp = walkp->nextp();
|
||||||
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
|
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
|
||||||
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
|
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
|
||||||
if (walkp==last2p) break;
|
if (walkp==last2p) break;
|
||||||
}
|
}
|
||||||
// Move node1 statements to new function
|
// Move node1 statements to new function
|
||||||
newfuncp->addStmtsp(node1p);
|
newfuncp->addStmtsp(node1p);
|
||||||
//newfuncp->dumpTree(cout," newfunctree: ");
|
//newfuncp->dumpTree(cout, " newfunctree: ");
|
||||||
// Mark node2 statements as dead
|
// Mark node2 statements as dead
|
||||||
CombMarkVisitor visitor(node2p);
|
CombMarkVisitor visitor(node2p);
|
||||||
pushDeletep(node2p); // Delete later
|
pushDeletep(node2p); // Delete later
|
||||||
// Link in new function
|
// Link in new function
|
||||||
relink1Handle.relink(call1p);
|
relink1Handle.relink(call1p);
|
||||||
relink2Handle.relink(call2p);
|
relink2Handle.relink(call2p);
|
||||||
// Hash the new function
|
// Hash the new function
|
||||||
hashFunctions(newfuncp);
|
hashFunctions(newfuncp);
|
||||||
m_call.addCall(call1p);
|
m_call.addCall(call1p);
|
||||||
m_call.addCall(call2p);
|
m_call.addCall(call2p);
|
||||||
// If either new statement makes a func with only a single call, replace
|
// If either new statement makes a func with only a single call, replace
|
||||||
// the above callers to call it directly
|
// the above callers to call it directly
|
||||||
replaceOnlyCallFunc(call1p); VL_DANGLING(call1p);
|
replaceOnlyCallFunc(call1p); VL_DANGLING(call1p);
|
||||||
replaceOnlyCallFunc(call2p); VL_DANGLING(call2p);
|
replaceOnlyCallFunc(call2p); VL_DANGLING(call2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
virtual void visit(AstNetlist* nodep) {
|
virtual void visit(AstNetlist* nodep) {
|
||||||
// Track all callers of each function
|
// Track all callers of each function
|
||||||
m_call.main(nodep);
|
m_call.main(nodep);
|
||||||
//
|
//
|
||||||
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||||
// Iterate modules backwards, in bottom-up order.
|
// Iterate modules backwards, in bottom-up order.
|
||||||
// Required so that a module instantiating another can benefit from collapsing.
|
// Required so that a module instantiating another can benefit from collapsing.
|
||||||
iterateChildrenBackwards(nodep);
|
iterateChildrenBackwards(nodep);
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeModule* nodep) {
|
virtual void visit(AstNodeModule* nodep) {
|
||||||
UINFO(4," MOD "<<nodep<<endl);
|
UINFO(4," MOD "<<nodep<<endl);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_modNFuncs = 0;
|
m_modNFuncs = 0;
|
||||||
m_walkLast2p = NULL;
|
m_walkLast2p = NULL;
|
||||||
m_hashed.clear();
|
m_hashed.clear();
|
||||||
// Compute hash of all statement trees in the function
|
// Compute hash of all statement trees in the function
|
||||||
m_state = STATE_HASH;
|
m_state = STATE_HASH;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_state = STATE_IDLE;
|
m_state = STATE_IDLE;
|
||||||
if (debug()>=9) {
|
if (debug()>=9) {
|
||||||
m_hashed.dumpFilePrefixed("combine");
|
m_hashed.dumpFilePrefixed("combine");
|
||||||
}
|
}
|
||||||
// Walk the hashes removing empty functions
|
// Walk the hashes removing empty functions
|
||||||
if (emptyFunctionDeletion()) {
|
if (emptyFunctionDeletion()) {
|
||||||
walkEmptyFuncs();
|
walkEmptyFuncs();
|
||||||
}
|
}
|
||||||
// Walk the hashes looking for duplicate functions
|
// Walk the hashes looking for duplicate functions
|
||||||
if (duplicateFunctionCombine()) {
|
if (duplicateFunctionCombine()) {
|
||||||
walkDupFuncs();
|
walkDupFuncs();
|
||||||
}
|
}
|
||||||
// Walk the statements looking for large replicated code sections
|
// Walk the statements looking for large replicated code sections
|
||||||
if (statementCombine()) {
|
if (statementCombine()) {
|
||||||
m_state = STATE_DUP;
|
m_state = STATE_DUP;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_state = STATE_IDLE;
|
m_state = STATE_IDLE;
|
||||||
}
|
}
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstCFunc* nodep) {
|
virtual void visit(AstCFunc* nodep) {
|
||||||
m_funcp = nodep;
|
m_funcp = nodep;
|
||||||
if (!nodep->dontCombine()) {
|
if (!nodep->dontCombine()) {
|
||||||
if (m_state == STATE_HASH) {
|
if (m_state == STATE_HASH) {
|
||||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||||
} else if (m_state == STATE_DUP) {
|
} else if (m_state == STATE_DUP) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_funcp = NULL;
|
m_funcp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeStmt* nodep) {
|
virtual void visit(AstNodeStmt* nodep) {
|
||||||
if (!nodep->isStatement()) {
|
if (!nodep->isStatement()) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_state == STATE_HASH && m_funcp) {
|
if (m_state == STATE_HASH && m_funcp) {
|
||||||
hashStatement(nodep);
|
hashStatement(nodep);
|
||||||
}
|
}
|
||||||
else if (m_state == STATE_DUP && m_funcp) {
|
else if (m_state == STATE_DUP && m_funcp) {
|
||||||
walkDupCodeStart(nodep);
|
walkDupCodeStart(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------
|
//--------------------
|
||||||
@@ -455,9 +461,9 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTUCTORS
|
// CONSTRUCTORS
|
||||||
explicit CombineVisitor(AstNetlist* nodep) {
|
explicit CombineVisitor(AstNetlist* nodep) {
|
||||||
m_state = STATE_IDLE;
|
m_state = STATE_IDLE;
|
||||||
m_modp = NULL;
|
m_modp = NULL;
|
||||||
m_funcp = NULL;
|
m_funcp = NULL;
|
||||||
m_modNFuncs = 0;
|
m_modNFuncs = 0;
|
||||||
@@ -466,7 +472,7 @@ public:
|
|||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
}
|
}
|
||||||
virtual ~CombineVisitor() {
|
virtual ~CombineVisitor() {
|
||||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef _V3COMBINE_H_
|
#ifndef _V3COMBINE_H_
|
||||||
#define _V3COMBINE_H_ 1
|
#define _V3COMBINE_H_ 1
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ public:
|
|||||||
static void combineAll(AstNetlist* nodep);
|
static void combineAll(AstNetlist* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|||||||
+81
-78
@@ -17,7 +17,7 @@
|
|||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
||||||
@@ -33,110 +33,113 @@
|
|||||||
|
|
||||||
class V3ConfigLine {
|
class V3ConfigLine {
|
||||||
public:
|
public:
|
||||||
int m_lineno; // Line number to make change at
|
int m_lineno; // Line number to make change at
|
||||||
V3ErrorCode m_code; // Error code
|
V3ErrorCode m_code; // Error code
|
||||||
bool m_on; // True to enaable message
|
bool m_on; // True to enable message
|
||||||
V3ConfigLine(V3ErrorCode code, int lineno, bool on)
|
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() {}
|
~V3ConfigLine() {}
|
||||||
inline bool operator< (const V3ConfigLine& rh) const {
|
inline bool operator< (const V3ConfigLine& rh) const {
|
||||||
if (m_lineno<rh.m_lineno) return true;
|
if (m_lineno<rh.m_lineno) return true;
|
||||||
if (m_lineno>rh.m_lineno) return false;
|
if (m_lineno>rh.m_lineno) return false;
|
||||||
if (m_code<rh.m_code) return true;
|
if (m_code<rh.m_code) return true;
|
||||||
if (m_code>rh.m_code) return false;
|
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"
|
// Always turn "on" before "off" so that overlapping lines will end
|
||||||
return (m_on>rh.m_on);
|
// 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 {
|
class V3ConfigIgnores {
|
||||||
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
||||||
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
||||||
|
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
string m_lastFilename; // Last filename looked up
|
string m_lastFilename; // Last filename looked up
|
||||||
int m_lastLineno; // Last linenumber 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_lastIt; // Point with next linenumber > current line number
|
||||||
IgnLines::const_iterator m_lastEnd; // Point with end()
|
IgnLines::const_iterator m_lastEnd; // Point with end()
|
||||||
|
|
||||||
IgnFiles m_ignWilds; // Ignores for each wildcarded filename
|
IgnFiles m_ignWilds; // Ignores for each wildcarded filename
|
||||||
IgnFiles m_ignFiles; // Ignores for each non-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() { m_lastLineno = -1; }
|
||||||
~V3ConfigIgnores() {}
|
~V3ConfigIgnores() {}
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
inline IgnLines* findWilds(const string& wildname) {
|
inline IgnLines* findWilds(const string& wildname) {
|
||||||
IgnFiles::iterator it = m_ignWilds.find(wildname);
|
IgnFiles::iterator it = m_ignWilds.find(wildname);
|
||||||
if (it != m_ignWilds.end()) {
|
if (it != m_ignWilds.end()) {
|
||||||
return &(it->second);
|
return &(it->second);
|
||||||
} else {
|
} else {
|
||||||
m_ignWilds.insert(make_pair(wildname, IgnLines()));
|
m_ignWilds.insert(make_pair(wildname, IgnLines()));
|
||||||
it = m_ignWilds.find(wildname);
|
it = m_ignWilds.find(wildname);
|
||||||
return &(it->second);
|
return &(it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void absBuild(const string& filename) {
|
inline void absBuild(const string& filename) {
|
||||||
// Given a filename, find all wildcard matches against it and build
|
// Given a filename, find all wildcard matches against it and build
|
||||||
// hash with the specific filename. This avoids having to wildmatch
|
// hash with the specific filename. This avoids having to wildmatch
|
||||||
// more than once against any filename.
|
// more than once against any filename.
|
||||||
IgnFiles::iterator it = m_ignFiles.find(filename);
|
IgnFiles::iterator it = m_ignFiles.find(filename);
|
||||||
if (it == m_ignFiles.end()) {
|
if (it == m_ignFiles.end()) {
|
||||||
// Haven't seen this filename before
|
// Haven't seen this filename before
|
||||||
m_ignFiles.insert(make_pair(filename, IgnLines()));
|
m_ignFiles.insert(make_pair(filename, IgnLines()));
|
||||||
it = m_ignFiles.find(filename);
|
it = m_ignFiles.find(filename);
|
||||||
// Make new list for this file of all matches
|
// Make new list for this file of all matches
|
||||||
for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) {
|
for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) {
|
||||||
if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) {
|
if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) {
|
||||||
for (IgnLines::iterator lit = fnit->second.begin(); lit != fnit->second.end(); ++lit) {
|
for (IgnLines::iterator lit = fnit->second.begin();
|
||||||
it->second.insert(*lit);
|
lit != fnit->second.end(); ++lit) {
|
||||||
}
|
it->second.insert(*lit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_lastIt = it->second.begin();
|
}
|
||||||
m_lastEnd = it->second.end();
|
m_lastIt = it->second.begin();
|
||||||
|
m_lastEnd = it->second.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline static V3ConfigIgnores& singleton() { return s_singleton; }
|
inline static V3ConfigIgnores& singleton() { return s_singleton; }
|
||||||
|
|
||||||
void addIgnore(V3ErrorCode code, const string& wildname, int lineno, bool on) {
|
void addIgnore(V3ErrorCode code, const string& wildname, int lineno, bool on) {
|
||||||
// Insert
|
// Insert
|
||||||
IgnLines* linesp = findWilds(wildname);
|
IgnLines* linesp = findWilds(wildname);
|
||||||
UINFO(9,"config addIgnore "<<wildname<<":"<<lineno<<", "<<code<<", "<<on<<endl);
|
UINFO(9,"config addIgnore "<<wildname<<":"<<lineno<<", "<<code<<", "<<on<<endl);
|
||||||
linesp->insert(V3ConfigLine(code, lineno, on));
|
linesp->insert(V3ConfigLine(code, lineno, on));
|
||||||
// Flush the match cache, due to a change in the rules.
|
// Flush the match cache, due to a change in the rules.
|
||||||
m_ignFiles.clear();
|
m_ignFiles.clear();
|
||||||
m_lastFilename = " ";
|
m_lastFilename = " ";
|
||||||
}
|
}
|
||||||
inline void applyIgnores(FileLine* filelinep) {
|
inline void applyIgnores(FileLine* filelinep) {
|
||||||
// HOT routine, called each parsed token line
|
// HOT routine, called each parsed token line
|
||||||
if (m_lastLineno != filelinep->lineno()
|
if (m_lastLineno != filelinep->lastLineno()
|
||||||
|| m_lastFilename != filelinep->filename()) {
|
|| m_lastFilename != filelinep->filename()) {
|
||||||
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
|
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
|
||||||
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
|
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
|
||||||
absBuild(filelinep->filename());
|
absBuild(filelinep->filename());
|
||||||
m_lastFilename = filelinep->filename();
|
m_lastFilename = filelinep->filename();
|
||||||
}
|
}
|
||||||
// Process all on/offs for lines up to and including the current line
|
// Process all on/offs for lines up to and including the current line
|
||||||
int curlineno = filelinep->lineno();
|
int curlineno = filelinep->lastLineno();
|
||||||
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
|
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
|
||||||
if (m_lastIt->m_lineno > curlineno) break;
|
if (m_lastIt->m_lineno > curlineno) break;
|
||||||
//UINFO(9," Hit "<<*m_lastIt<<endl);
|
//UINFO(9," Hit "<<*m_lastIt<<endl);
|
||||||
filelinep->warnOn(m_lastIt->m_code, m_lastIt->m_on);
|
filelinep->warnOn(m_lastIt->m_code, m_lastIt->m_on);
|
||||||
}
|
}
|
||||||
if (0 && debug() >= 9) {
|
if (0 && debug() >= 9) {
|
||||||
for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) {
|
for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) {
|
||||||
UINFO(9," NXT "<<*it<<endl);
|
UINFO(9," NXT "<<*it<<endl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_lastLineno = filelinep->lineno();
|
m_lastLineno = filelinep->lastLineno();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -147,10 +150,10 @@ V3ConfigIgnores V3ConfigIgnores::s_singleton;
|
|||||||
|
|
||||||
void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
|
void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
|
||||||
if (filename=="*") {
|
if (filename=="*") {
|
||||||
FileLine::globalWarnOff(code,!on);
|
FileLine::globalWarnOff(code,!on);
|
||||||
} else {
|
} else {
|
||||||
V3ConfigIgnores::singleton().addIgnore(code, filename, min, on);
|
V3ConfigIgnores::singleton().addIgnore(code, filename, min, on);
|
||||||
if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on);
|
if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user