mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 16:44:11 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89a0632ecc | ||
|
|
dcc05db7f1 | ||
|
|
90fb2e5487 | ||
|
|
b5ce7d5982 | ||
|
|
f211616a4c | ||
|
|
a9cb9bac21 | ||
|
|
ae005434e4 | ||
|
|
ef87d057fc | ||
|
|
181b9a5795 | ||
|
|
9baf9c55c2 | ||
|
|
e29132377e | ||
|
|
999751c422 | ||
|
|
22656d6fdd | ||
|
|
ef0c5fd772 | ||
|
|
90c61c79d6 | ||
|
|
4ba3bff87f | ||
|
|
c3dd6f5344 | ||
|
|
321880f5a6 | ||
|
|
3737d209f6 | ||
|
|
29c4b0a141 | ||
|
|
956f64c6ba | ||
|
|
c2d18d8ae4 | ||
|
|
5b9806ae6d | ||
|
|
665fa140a8 | ||
|
|
32f843a214 | ||
|
|
72a3c756ad | ||
|
|
47069dfe52 | ||
|
|
43a84d7ad8 | ||
|
|
b6ca2a42f2 | ||
|
|
e52a4ac74f | ||
|
|
decfa6bd7a | ||
|
|
8931bd37e2 | ||
|
|
4b79d23d00 | ||
|
|
77fe7c426e | ||
|
|
331c2244fc | ||
|
|
5a3eab634a | ||
|
|
77e68acf54 | ||
|
|
7a355d448a | ||
|
|
3b7ad1820d | ||
|
|
d3b63b2653 | ||
|
|
046896e60a | ||
|
|
fb9119ff49 | ||
|
|
818aaa8b89 | ||
|
|
26bdfc3474 | ||
|
|
0e91d8a10e | ||
|
|
434c3c3ef3 | ||
|
|
21e05c43dd | ||
|
|
0e154b35ef |
@@ -8,6 +8,34 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 4.220 2022-03-12
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Removed the deprecated lint_off flag -msg; use -rule instead.
|
||||
* Removed the deprecated "fl" attribute in XML output; use "loc" attribute instead.
|
||||
* Suppress WIDTH warning on negate using carry bit (#3295). [Peter Monsson]
|
||||
* Add trace dumpvars() call for selective runtime tracing (#3322). [Shunyao CAD]
|
||||
* Add VERILATOR_VERSION_INTEGER for determining API (#3343). [Larry Doolittle]
|
||||
* Improve various V3Combine algorithm details (#3328). [Yutetsu TAKATSUKASA]
|
||||
* Improve various V3Order algorithm details. [Geza Lore]
|
||||
* Fix macOS arm64 build (#3285) (#3291). [Guokai Chen]
|
||||
* Fix signed number operation (#3294) (#3308). [Raynard Qiao]
|
||||
* Fix FST traces to include vector range (#3296) (#3297). [Jamie Iles]
|
||||
* Fix skipping public enum values with four-state values (#3303).
|
||||
* Fix $readmem file not found to be warning not error (#3310). [Alexander Grobman]
|
||||
* Fix class stringification on wide arrays (#3312). [Iru Cai]
|
||||
* Fix $fscanf etc to return -1 on EOF (#3313). [Jose Tejada]
|
||||
* Fix public function arguments that are arrayed (#3316). [pawel256]
|
||||
* Fix unnamedblk error on foreach (#3321). [Aliaksei Chapyzhenka]
|
||||
* Fix crash in recursive module inlining (#3324). [Larry Doolittle]
|
||||
* Fix VL_RESTORER behavior on passing a lvalue reference (#3326). [HungMingWu]
|
||||
* Fix compile error with --trace-fst --sc (#3332). [leavinel]
|
||||
* Fix cast to array types (#3333). [Todd Strader]
|
||||
* Fix Vdeeptemp error with --threads and --compiler clang (#3338). [Per Karlsson]
|
||||
|
||||
|
||||
Verilator 4.218 2022-01-17
|
||||
==========================
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ def diff_dir(a, b):
|
||||
|
||||
anyfile = False
|
||||
for base in sorted(files.keys()):
|
||||
if (not 'a' in files[base]) or (not 'b' in files[base]):
|
||||
continue
|
||||
a = files[base]['a']
|
||||
b = files[base]['b']
|
||||
if not a or not b:
|
||||
continue
|
||||
print("=" * 70)
|
||||
print("= %s <-> %s" % (a, b))
|
||||
diff_file(a, b)
|
||||
|
||||
+3
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.218 2022-01-17],
|
||||
AC_INIT([Verilator],[4.220 2022-03-12],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
@@ -22,6 +22,8 @@ AC_MSG_RESULT([configuring for $PACKAGE_STRING])
|
||||
PACKAGE_VERSION_NUMBER=`AS_ECHO("$PACKAGE_VERSION") | sed 's/ .*//g'`
|
||||
AC_SUBST(PACKAGE_VERSION_NUMBER)
|
||||
AC_DEFINE_UNQUOTED([PACKAGE_VERSION_NUMBER_STRING],["$PACKAGE_VERSION_NUMBER"],[Package version as a number])
|
||||
VERILATOR_VERSION_INTEGER=`AS_ECHO("$PACKAGE_VERSION") | [sed 's/\([0-9]\)\.\([0-9][0-9][0-9]\) .*/\1\2000/g']`
|
||||
AC_SUBST(VERILATOR_VERSION_INTEGER)
|
||||
|
||||
# Ignore automake flags passed by Ubuntu builds
|
||||
AC_ARG_ENABLE([dependency-tracking],
|
||||
|
||||
@@ -10,6 +10,7 @@ Alex Chadwick
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Chris Randall
|
||||
Chuxuan Wang
|
||||
Conor McCullough
|
||||
Dan Petrisko
|
||||
Daniel Bates
|
||||
@@ -17,6 +18,7 @@ David Horton
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
@@ -28,6 +30,7 @@ Geza Lore
|
||||
Gianfranco Costamagna
|
||||
Glen Gibb
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Harald Heckmann
|
||||
Howard Su
|
||||
Huang Rui
|
||||
@@ -40,6 +43,7 @@ James Hanlon
|
||||
James Hutchinson
|
||||
James Pallister
|
||||
Jamey Hicks
|
||||
Jamie Iles
|
||||
Jan Van Winkel
|
||||
Jean Berniolles
|
||||
Jeremy Bennett
|
||||
@@ -55,6 +59,7 @@ Keith Colbert
|
||||
Kevin Kiningham
|
||||
Krzysztof Bieganski
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
Ludwig Rogiers
|
||||
Lukasz Dalek
|
||||
Maarten De Braekeleer
|
||||
@@ -83,6 +88,7 @@ Pieter Kapsenberg
|
||||
Piotr Binkowski
|
||||
Qingyao Sun
|
||||
Rafal Kapuscik
|
||||
Raynard Qiao
|
||||
Richard Myers
|
||||
Rupert Swarbrick
|
||||
Samuel Riedel
|
||||
|
||||
@@ -422,7 +422,7 @@ accesses the above signal "readme" would be:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cat >sim_main.cpp <<'<<EOF'
|
||||
cat >sim_main.cpp <<'EOF'
|
||||
#include "Vour.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h" // Required to get definitions
|
||||
|
||||
@@ -16,14 +16,6 @@ Verilated_heavy.h
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
|
||||
Configuration File -msg
|
||||
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
|
||||
option. "-msg" is planned for removal no sooner than January 2021.
|
||||
|
||||
XML locations
|
||||
The XML "fl" attribute has been replaced with the "loc" attribute. "fl"
|
||||
is planned for removal no sooner than January 2021.
|
||||
|
||||
Option `--cdc`
|
||||
The experimental `--cdc` option is believed to be generally unused and is
|
||||
planned for removeal no sooner than January 2023.
|
||||
planned for removal no sooner than January 2023.
|
||||
|
||||
@@ -884,7 +884,7 @@ Summary:
|
||||
|
||||
Using DPI imports/exports is allowed and generally relatively safe in
|
||||
terms of information disclosed, which is limited to the DPI function
|
||||
prototyptes. Use of the VPI is not recommended as many design details
|
||||
prototypes. Use of the VPI is not recommended as many design details
|
||||
may be exposed, and an INSECURE warning will be issued.
|
||||
|
||||
.. option:: --protect-lib <name>
|
||||
@@ -1514,7 +1514,7 @@ The grammar of configuration commands is as follows:
|
||||
.. option:: no_clocker -module "<modulename>" [-function "<funcname>"] -var "<signame>"
|
||||
|
||||
Indicates that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derrived signals as
|
||||
used by Verilator to mark the signal and any derived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`/*verilator&32;clocker*/` metacomment.
|
||||
|
||||
@@ -67,7 +67,7 @@ which have low coverage are written to the output directory.
|
||||
|
||||
Specifies if the coverage point does not include the count number of
|
||||
coverage hits, then the coverage point will be considered above the
|
||||
threashold, and the coverage report will put a "%" to indicate the coverage
|
||||
threshold, and the coverage report will put a "%" to indicate the coverage
|
||||
is not sufficient. Defaults to 10.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
@@ -13,7 +13,7 @@ For an overview of use of verilator_gantt, see :ref:`Profiling`.
|
||||
Gantt Chart VCD
|
||||
---------------
|
||||
|
||||
Verilated_gnatt creates a value change dump (VCD) format dump file which
|
||||
Verilated_gantt creates a value change dump (VCD) format dump file which
|
||||
may be viewed in a waveform viewer (e.g. C<GTKWave>):
|
||||
|
||||
.. figure:: figures/fig_gantt_min.png
|
||||
|
||||
@@ -185,7 +185,7 @@ or "`ifdef`"'s may break other tools.
|
||||
.. option:: /*verilator&32;no_clocker*/
|
||||
|
||||
Specifies that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derrived signals as
|
||||
used by Verilator to mark the signal and any derived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`clocker` and :option:`no_clocker` in configuration
|
||||
|
||||
+5
-4
@@ -37,15 +37,15 @@ Does Verilator run under Windows?
|
||||
|
||||
Yes, ideally run Ubuntu under Windows Subsystem for Linux (WSL2).
|
||||
Alternatively use Cygwin, though this tends to be slower and is not
|
||||
regurally tested. Verilated output also compiles under Microsoft Visual
|
||||
C++, but this is also not regurally tested.
|
||||
regularly tested. Verilated output also compiles under Microsoft Visual
|
||||
C++, but this is also not regularly tested.
|
||||
|
||||
|
||||
Can you provide binaries?
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
You can install Verilator via the system package manager (apt, yum, etc.)
|
||||
on many Linux distributions, including Debian, Ubuntu, SuSE, RedHat, and
|
||||
on many Linux distributions, including Debian, Ubuntu, SuSE, Red Hat, and
|
||||
others. These packages are provided by the Linux distributions and
|
||||
generally will lag the version of the mainline Verilator repository. If no
|
||||
binary package is available for your distribution, how about you set one
|
||||
@@ -134,7 +134,8 @@ B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
...
|
||||
Verilated::traceEverOn(true);
|
||||
VerilatedVcdC* tfp = new VerilatedVcdC;
|
||||
topp->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
topp->trace(tfp, 99); // Trace 99 levels of hierarchy (or see below)
|
||||
// tfp->dumpvars(1, "t"); // trace 1 level under "t"
|
||||
tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
|
||||
...
|
||||
while (contextp->time() < sim_time && !contextp->gotFinish()) {
|
||||
|
||||
@@ -157,7 +157,7 @@ The Verilated executable may produce the following:
|
||||
* - profile.vlt
|
||||
- -profile data file for :ref:`Thread PGO`
|
||||
* - profile_threads.dat
|
||||
- -profile-threads data file for :command:`verilator_gnatt`
|
||||
- -profile-threads data file for :command:`verilator_gantt`
|
||||
|
||||
Verilator_gantt may produce the following:
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ OS Requirements
|
||||
---------------
|
||||
|
||||
Verilator is developed and has primary testing on Ubuntu, with additional
|
||||
testing on FreeBSD and Apple OS-X. Versions have also built on Redhat
|
||||
testing on FreeBSD and Apple OS-X. Versions have also built on Red Hat
|
||||
Linux, and other flavors of GNU/Linux-ish platforms. Verilator also works
|
||||
on Windows Subsystem for Linux (WSL2), Windows under Cygwin, and Windows
|
||||
under MinGW (gcc -mno-cygwin). Verilated output (not Verilator itself)
|
||||
|
||||
@@ -136,7 +136,7 @@ ___05F (5F is the hex code of an underscore.)
|
||||
Bind
|
||||
----
|
||||
|
||||
sVerilator only supports bind to a target module name, not to an
|
||||
Verilator only supports bind to a target module name, not to an
|
||||
instance path.
|
||||
|
||||
|
||||
@@ -340,7 +340,7 @@ Force statement
|
||||
---------------
|
||||
|
||||
Verilator supports the procedural `force` (and corresponding `release`)
|
||||
statement. The behaviour of the `force` statement however does not entirely
|
||||
statement. The behavior of the `force` statement however does not entirely
|
||||
comply with the IEEE 1800 SystemVerilog standard. According to the standard,
|
||||
when a procedural statement of the form `force a = b;` is executed, the
|
||||
simulation should behave as if from that point onwards, a continuous
|
||||
@@ -350,7 +350,7 @@ More specifically: the value of `a` should be updated, whenever the value of
|
||||
Verilator instead evaluates the current value of `b` at the time the `force`
|
||||
statement is executed, and forces `a` to that value, without updating it
|
||||
until a new `force` or `release` statement is encountered that applies to
|
||||
`a`. This non-standard behaviour is nevertheless consistent with some other
|
||||
`a`. This non-standard behavior is nevertheless consistent with some other
|
||||
simulators.
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ multi-core SoC.
|
||||
|
||||
Verilator is run in hierarchical mode on the whole SoC. Verilator will
|
||||
make two models, one for the CPU hierarchy block, and one for the SoC. The
|
||||
Verialted code for the SoC will automatically call the CPU Verilated model.
|
||||
Verilated code for the SoC will automatically call the CPU Verilated model.
|
||||
|
||||
The current hierarchical Verilation is based on :vlopt:`--lib-create`. Each
|
||||
hierarchy block is Verilated into a library. User modules of the hierarchy
|
||||
|
||||
@@ -447,7 +447,7 @@ List Of Warnings
|
||||
p0();
|
||||
endmodule
|
||||
|
||||
Other tools with similar warnings: Veriable's forbid_defparam_rule.
|
||||
Other tools with similar warnings: Verible's forbid_defparam_rule.
|
||||
|
||||
|
||||
.. option:: DEPRECATED
|
||||
@@ -518,7 +518,7 @@ List Of Warnings
|
||||
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
|
||||
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
|
||||
or :option:`STMTDLY` warning. If these were suppressed, due to the
|
||||
absense of the delay, the code may now oscillate.
|
||||
absence of the delay, the code may now oscillate.
|
||||
|
||||
Finally, rare, more difficult cases can be debugged like a C++ program;
|
||||
either enter :command:`gdb` and use its tracing facilities, or edit the
|
||||
@@ -528,7 +528,7 @@ List Of Warnings
|
||||
.. option:: ENDCAPSULATED
|
||||
|
||||
Warns that a class member is declared is local or protected, but is
|
||||
being accessed from outside that class (if local) or a derrived class
|
||||
being accessed from outside that class (if local) or a derived class
|
||||
(if protected).
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will
|
||||
@@ -924,7 +924,7 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
Other tools with similar warnings: Icarus Verilog's portbind, "warning:
|
||||
Instantiating module ... with dangling impot port (...)". Slang's
|
||||
Instantiating module ... with dangling input port (...)". Slang's
|
||||
unconnected-port, "port '...' has no connection".
|
||||
|
||||
|
||||
@@ -1217,7 +1217,7 @@ List Of Warnings
|
||||
used to cause some important action at a later time, it might be an
|
||||
important difference.
|
||||
|
||||
Some possible work arounds:
|
||||
Some possible workarounds:
|
||||
|
||||
* Move the delayed statement into the C++ wrapper file, where the
|
||||
stimulus and clock generation can be done in C++.
|
||||
|
||||
+1
-1
@@ -1183,7 +1183,7 @@ Verilator ideally would support all of IEEE, and has the goal to get close
|
||||
to full support. However the following IEEE sections and features are not
|
||||
anticipated to be ever implemented for the reasons indicated.
|
||||
|
||||
IEEE 1800-2017 3.3 recursive modules
|
||||
IEEE 1800-2017 3.3 modules within modules
|
||||
Little/no tool support, and arguably not a good practice.
|
||||
IEEE 1800-2017 6.12 "shortreal"
|
||||
Little/no tool support, and easily simply promoted to real.
|
||||
|
||||
@@ -46,9 +46,6 @@ The XML document consists of 4 sections within the top level
|
||||
hierarchy. Each instance is represented with the ``<cell>`` element
|
||||
with the following attributes:
|
||||
|
||||
- ``fl`` (deprecated): The file id and line number where the module
|
||||
was instanced. Use ``loc`` instead.
|
||||
|
||||
- ``loc``: The file id, first line number, last line number, first
|
||||
column number and last column number of the identifier where the
|
||||
module was instanced, separated by commas.
|
||||
|
||||
@@ -140,6 +140,10 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(FST_MACOSX) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__)
|
||||
#define FST_UNBUFFERED_IO
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Boolean expression more often true than false */
|
||||
#define FST_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
@@ -4577,12 +4581,11 @@ if(sectype == FST_BL_ZWRAPPER)
|
||||
if(!fcomp) { tmpfile_close(&fcomp, &xc->f_nam); return(0); }
|
||||
}
|
||||
|
||||
#if defined(FST_MACOSX)
|
||||
#if defined(FST_UNBUFFERED_IO)
|
||||
setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
|
||||
xc->filename_unpacked = hf;
|
||||
#else
|
||||
if(hf)
|
||||
@@ -4880,7 +4883,7 @@ if((!nam)||(!(xc->f=fopen(nam, "rb"))))
|
||||
char *hf = (char *)calloc(1, flen + 6);
|
||||
int rc;
|
||||
|
||||
#if defined(__MINGW32__) || defined(FST_MACOSX)
|
||||
#if defined(FST_UNBUFFERED_IO)
|
||||
setvbuf(xc->f, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1497,7 +1497,7 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* const fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
if (VL_UNLIKELY(!fp)) return ~0U; // -1
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
@@ -1844,7 +1844,7 @@ VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData star
|
||||
m_fp = std::fopen(filename.c_str(), "r");
|
||||
if (VL_UNLIKELY(!m_fp)) {
|
||||
// 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_WARN_MT(filename.c_str(), 0, "", "$readmem file not found");
|
||||
// cppcheck-suppress resourceLeak // m_fp is nullptr - bug in cppcheck
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
// clang-format off
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_config.h"
|
||||
#if VM_SC
|
||||
# include "verilated_sc.h" // Get SYSTEMC_VERSION and time declarations
|
||||
#endif
|
||||
@@ -86,6 +87,7 @@ class VerilatedCovContext;
|
||||
class VerilatedEvalMsgQueue;
|
||||
class VerilatedFst;
|
||||
class VerilatedFstC;
|
||||
class VerilatedFstSc;
|
||||
class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
class VerilatedVar;
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_PRODUCT "@PACKAGE_NAME@"
|
||||
|
||||
/// Verilator version name, e.g. "1.000 2000-01-01"
|
||||
/// Verilator version name, e.g. "1.002 2000-01-01"
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_VERSION "@PACKAGE_VERSION@"
|
||||
|
||||
/// Verilator version number as integer
|
||||
/// As major * 100000 + minor * 1000, e.g. 1002000 == 1.002
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_VERSION_INTEGER @VERILATOR_VERSION_INTEGER@
|
||||
|
||||
@@ -122,7 +122,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
||||
// convert m_code2symbol into an array for fast lookup
|
||||
if (!m_symbolp) {
|
||||
m_symbolp = new fstHandle[nextCode()];
|
||||
m_symbolp = new fstHandle[nextCode()]{0};
|
||||
for (const auto& i : m_code2symbol) m_symbolp[i.first] = i.second;
|
||||
}
|
||||
m_code2symbol.clear();
|
||||
@@ -158,10 +158,12 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elem
|
||||
}
|
||||
|
||||
void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
fstVarType vartype, bool array, int arraynum, bool bussed, int msb,
|
||||
int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
|
||||
const bool enabled = VerilatedTrace<VerilatedFst>::declCode(code, name, bits, false);
|
||||
if (!enabled) return;
|
||||
|
||||
std::string nameasstr = namePrefix() + name;
|
||||
std::istringstream nameiss{nameasstr};
|
||||
@@ -205,6 +207,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
std::stringstream name_ss;
|
||||
name_ss << symbol_name;
|
||||
if (array) name_ss << "[" << arraynum << "]";
|
||||
if (bussed) name_ss << " [" << msb << ":" << lsb << "]";
|
||||
std::string name_str = name_ss.str();
|
||||
|
||||
if (dtypenum > 0) {
|
||||
@@ -223,23 +226,23 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
|
||||
void VerilatedFst::declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, 0, 0);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedFst::declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, msb, lsb);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, msb, lsb);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, msb, lsb);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, 63, 0);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 63, 0);
|
||||
}
|
||||
|
||||
// Note: emit* are only ever called from one place (full* in
|
||||
@@ -248,12 +251,14 @@ void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, f
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitBit(vluint32_t code, CData newval) {
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
|
||||
char buf[VL_BYTESIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
@@ -261,6 +266,7 @@ void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
|
||||
char buf[VL_SHORTSIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
@@ -268,6 +274,7 @@ void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
|
||||
char buf[VL_IDATASIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
@@ -275,6 +282,7 @@ void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
|
||||
char buf[VL_QUADSIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ private:
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
fstVarType vartype, bool array, int arraynum, bool bussed, int msb, int lsb);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
@@ -121,6 +121,7 @@ template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::dumpvars(int level, const std::string& hier);
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
@@ -178,6 +179,11 @@ public:
|
||||
void set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
m_sptrace.set_time_resolution(unit);
|
||||
}
|
||||
// Set variables to dump, using $dumpvars format
|
||||
// If level = 0, dump everything and hier is then ignored
|
||||
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
m_sptrace.dumpvars(level, hier);
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
|
||||
@@ -162,10 +162,6 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
|
||||
// Base macros
|
||||
|
||||
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
|
||||
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit)))
|
||||
#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit)))
|
||||
#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit)))
|
||||
#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit))
|
||||
|
||||
// Shift appropriate word by bit. Does not account for wrapping between two words
|
||||
@@ -343,13 +339,19 @@ double vl_time_multiplier(int scale) VL_PURE;
|
||||
vluint64_t vl_time_pow10(int n) VL_PURE;
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
/// Evaluate statement if Verilated::debug() enabled
|
||||
/// Evaluate statement if VL_DEBUG defined
|
||||
# define VL_DEBUG_IFDEF(stmt) \
|
||||
do { \
|
||||
stmt \
|
||||
} while (false)
|
||||
/// Evaluate statement if VL_DEBUG defined and Verilated::debug() enabled
|
||||
# define VL_DEBUG_IF(stmt) \
|
||||
do { \
|
||||
if (VL_UNLIKELY(Verilated::debug())) {stmt} \
|
||||
} while (false)
|
||||
#else
|
||||
// We intentionally do not compile the stmt to improve compile speed
|
||||
# define VL_DEBUG_IFDEF(stmt) do {} while (false)
|
||||
# define VL_DEBUG_IF(stmt) do {} while (false)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -316,14 +316,14 @@ public: // But only for verilated*.cpp
|
||||
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1
|
||||
return static_cast<IData>(
|
||||
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
|
||||
}
|
||||
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1
|
||||
return static_cast<IData>(std::ftell(*fdlist.begin()));
|
||||
}
|
||||
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
#if defined(__linux)
|
||||
# include <sched.h> // For sched_getcpu()
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__) && !defined(__arm64__)
|
||||
# include <cpuid.h> // For __cpuid_count()
|
||||
#endif
|
||||
// clang-format on
|
||||
@@ -168,7 +168,7 @@ public:
|
||||
static int getcpu() { // Return current executing CPU
|
||||
#if defined(__linux)
|
||||
return sched_getcpu();
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && !defined(__arm64__)
|
||||
vluint32_t info[4];
|
||||
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
|
||||
// info[1] is EBX, bits 24-31 are APIC ID
|
||||
|
||||
+15
-12
@@ -27,6 +27,7 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace_defs.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -142,7 +143,9 @@ private:
|
||||
};
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; // Old value store
|
||||
EData* m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
|
||||
vluint64_t m_timeLastDump; // Last time we did a dump
|
||||
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong
|
||||
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
|
||||
@@ -152,6 +155,7 @@ private:
|
||||
vluint32_t m_numSignals; // Number of distinct signals
|
||||
vluint32_t m_maxBits; // Number of bits in the widest signal
|
||||
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
|
||||
std::vector<std::pair<int, std::string>> m_dumpvars; // dumpvar() entries
|
||||
char m_scopeEscape;
|
||||
double m_timeRes; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit; // Time units (ns/ms etc)
|
||||
@@ -171,27 +175,22 @@ private:
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Number of total trace buffers that have been allocated
|
||||
vluint32_t m_numTraceBuffers;
|
||||
|
||||
// Size of trace buffers
|
||||
size_t m_traceBufferSize;
|
||||
|
||||
// Buffers handed to worker for processing
|
||||
VerilatedThreadQueue<vluint32_t*> m_buffersToWorker;
|
||||
// Buffers returned from worker after processing
|
||||
VerilatedThreadQueue<vluint32_t*> m_buffersFromWorker;
|
||||
// Write pointer into current buffer
|
||||
vluint32_t* m_traceBufferWritep;
|
||||
// End of trace buffer
|
||||
vluint32_t* m_traceBufferEndp;
|
||||
// The worker thread itself
|
||||
std::unique_ptr<std::thread> m_workerThread;
|
||||
|
||||
// Get a new trace buffer that can be populated. May block if none available
|
||||
vluint32_t* getTraceBuffer();
|
||||
|
||||
// Write pointer into current buffer
|
||||
vluint32_t* m_traceBufferWritep;
|
||||
|
||||
// End of trace buffer
|
||||
vluint32_t* m_traceBufferEndp;
|
||||
|
||||
// The worker thread itself
|
||||
std::unique_ptr<std::thread> m_workerThread;
|
||||
|
||||
// The function executed by the worker thread
|
||||
void workerThreadMain();
|
||||
|
||||
@@ -223,7 +222,8 @@ protected:
|
||||
|
||||
void traceInit() VL_MT_UNSAFE;
|
||||
|
||||
void declCode(vluint32_t code, vluint32_t bits, bool tri);
|
||||
// Declare new signal and return true if enabled
|
||||
bool declCode(vluint32_t code, const char* namep, vluint32_t bits, bool tri);
|
||||
|
||||
// Is this an escape?
|
||||
bool isScopeEscape(char c) { return std::isspace(c) || c == m_scopeEscape; }
|
||||
@@ -259,6 +259,9 @@ public:
|
||||
// Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const char* unitp) VL_MT_SAFE;
|
||||
void set_time_resolution(const std::string& unit) VL_MT_SAFE;
|
||||
// Set variables to dump, using $dumpvars format
|
||||
// If level = 0, dump everything and hier is then ignored
|
||||
void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
|
||||
|
||||
// Call
|
||||
void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
@@ -282,6 +282,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::onExit(void* selfp) {
|
||||
template <>
|
||||
VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
||||
: m_sigs_oldvalp{nullptr}
|
||||
, m_sigs_enabledp{nullptr}
|
||||
, m_timeLastDump{0}
|
||||
, m_fullDump{true}
|
||||
, m_nextCode{0}
|
||||
@@ -302,6 +303,7 @@ VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
||||
|
||||
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
|
||||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
|
||||
#ifdef VL_TRACE_THREADED
|
||||
@@ -320,9 +322,10 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
m_nextCode = 1;
|
||||
m_numSignals = 0;
|
||||
m_maxBits = 0;
|
||||
m_sigs_enabledVec.clear();
|
||||
|
||||
// Call all initialize callbacks, which will:
|
||||
// - Call decl* for each signal
|
||||
// - Call decl* for each signal (these eventually call ::declCode)
|
||||
// - Store the base code
|
||||
for (vluint32_t i = 0; i < m_initCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_initCbs[i];
|
||||
@@ -338,6 +341,23 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
// holding previous signal values.
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[nextCode()];
|
||||
|
||||
// Apply enables
|
||||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
if (!m_sigs_enabledVec.empty()) {
|
||||
// Else if was empty, m_sigs_enabledp = nullptr to short circuit tests
|
||||
// But it isn't, so alloc one bit for each code to indicate enablement
|
||||
// We don't want to still use m_signs_enabledVec as std::vector<bool> is not
|
||||
// guarenteed to be fast
|
||||
m_sigs_enabledp = new vluint32_t[1 + VL_WORDS_I(nextCode())]{0};
|
||||
m_sigs_enabledVec.reserve(nextCode());
|
||||
for (size_t code = 0; code < nextCode(); ++code) {
|
||||
if (m_sigs_enabledVec[code]) {
|
||||
m_sigs_enabledp[VL_BITWORD_I(code)] |= 1U << VL_BITBIT_I(code);
|
||||
}
|
||||
}
|
||||
m_sigs_enabledVec.clear();
|
||||
}
|
||||
|
||||
// Set callback so flush/abort will flush this file
|
||||
Verilated::addFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
|
||||
@@ -356,10 +376,38 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, vluint32_t bits, bool tri) {
|
||||
bool VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, const char* namep, vluint32_t bits,
|
||||
bool tri) {
|
||||
if (VL_UNCOVERABLE(!code)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
// To keep it simple, this is O(enables * signals), but we expect few enables
|
||||
std::string declName = namePrefix() + namep;
|
||||
bool enabled = false;
|
||||
if (m_dumpvars.empty()) enabled = true;
|
||||
for (const auto& item : m_dumpvars) {
|
||||
const int dumpvarsLevel = item.first;
|
||||
const char* dvp = item.second.c_str();
|
||||
const char* np = declName.c_str();
|
||||
while (*dvp && *dvp == *np) {
|
||||
++dvp;
|
||||
++np;
|
||||
}
|
||||
if (*dvp) continue; // Didn't match dumpvar item
|
||||
if (*np && *np != ' ') continue; // e.g. "t" isn't a match for "top"
|
||||
int levels = 0;
|
||||
while (*np) {
|
||||
if (*np++ == ' ') ++levels;
|
||||
}
|
||||
if (levels > dumpvarsLevel) continue; // Too deep
|
||||
// We only need to set first code word if it's a multicode signal
|
||||
// as that's all we'll check for later
|
||||
if (m_sigs_enabledVec.size() <= code) m_sigs_enabledVec.resize((code + 1024) * 2);
|
||||
m_sigs_enabledVec[code] = true;
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: The tri-state flag is not used by Verilator, but is here for
|
||||
// compatibility with some foreign code.
|
||||
int codesNeeded = VL_WORDS_I(bits);
|
||||
@@ -367,6 +415,7 @@ void VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, vluint32_t bits, bo
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
++m_numSignals;
|
||||
m_maxBits = std::max(m_maxBits, bits);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -392,6 +441,19 @@ template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_resolution(unit.c_str());
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
if (level == 0) {
|
||||
m_dumpvars.clear(); // empty = everything on
|
||||
} else {
|
||||
// Convert Verilog . separators to trace space separators
|
||||
std::string hierSpaced = hier;
|
||||
for (auto& i : hierSpaced) {
|
||||
if (i == '.') i = ' ';
|
||||
}
|
||||
m_dumpvars.push_back(std::make_pair(level, hierSpaced));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
@@ -523,44 +585,58 @@ template <> void VerilatedTrace<VL_DERIVED_T>::popNamePrefix(unsigned count) {
|
||||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(vluint32_t* oldp, CData newval) {
|
||||
*oldp = newval;
|
||||
self()->emitBit(oldp - m_sigs_oldvalp, newval);
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitBit(code, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitCData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitCData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitSData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitSData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitIData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitIData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<QData*>(oldp) = newval;
|
||||
self()->emitQData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitQData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
|
||||
self()->emitWData(oldp - m_sigs_oldvalp, newvalp, bits);
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitWData(code, newvalp, bits);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
self()->emitDouble(oldp - m_sigs_oldvalp, newval);
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
self()->emitDouble(code, newval);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -463,7 +463,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::declCode(code, bits, tri);
|
||||
const bool enabled = VerilatedTrace<VerilatedVcd>::declCode(code, name, bits, tri);
|
||||
|
||||
if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
|
||||
m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
|
||||
@@ -472,6 +472,8 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
// Make sure write buffer is large enough (one character per bit), plus header
|
||||
bufferResize(bits + 1024);
|
||||
|
||||
if (!enabled) return;
|
||||
|
||||
// Split name into basename
|
||||
// Spaces and tabs aren't legal in VCD signal names, so:
|
||||
// Space separates each level of scope
|
||||
@@ -606,6 +608,7 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
|
||||
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
VL_DEBUG_IFDEF(assert(suffixp[0]););
|
||||
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
|
||||
|
||||
// Now write back the write pointer incremented by the actual size of the
|
||||
|
||||
@@ -328,6 +328,7 @@ template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::dumpvars(int level, const std::string& hier);
|
||||
#endif // DOXYGEN
|
||||
|
||||
//=============================================================================
|
||||
@@ -392,6 +393,11 @@ public:
|
||||
void set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
m_sptrace.set_time_resolution(unit);
|
||||
}
|
||||
// Set variables to dump, using $dumpvars format
|
||||
// If level = 0, dump everything and hier is then ignored
|
||||
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
m_sptrace.dumpvars(level, hier);
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
|
||||
@@ -414,6 +414,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
#define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q) ///< Bit number for a bit in a quad
|
||||
#define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E) ///< Bit number for a bit in a EData
|
||||
|
||||
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
|
||||
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit)))
|
||||
#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit)))
|
||||
#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit)))
|
||||
|
||||
//=========================================================================
|
||||
// Floating point
|
||||
// #defines, to avoid requiring math.h on all compile runs
|
||||
|
||||
+14
-27
@@ -32,9 +32,7 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Active.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3SenTree.h" // for SenTreeSet
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -211,9 +209,8 @@ private:
|
||||
AstActive* m_iActivep = nullptr; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep = nullptr; // For current scope, the SActive(combo) we're building
|
||||
|
||||
SenTreeSet m_activeSens; // Sen lists for each active we've made
|
||||
using ActiveMap = std::unordered_map<AstSenTree*, AstActive*>;
|
||||
ActiveMap m_activeMap; // Map sentree to active, for folding.
|
||||
// Map from AstSenTree (equivalence) to the corresponding AstActive created.
|
||||
std::unordered_map<VNRef<AstSenTree>, AstActive*> m_activeMap;
|
||||
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
@@ -225,7 +222,6 @@ private:
|
||||
m_scopep = nodep;
|
||||
m_iActivep = nullptr;
|
||||
m_cActivep = nullptr;
|
||||
m_activeSens.clear();
|
||||
m_activeMap.clear();
|
||||
iterateChildren(nodep);
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
@@ -259,29 +255,20 @@ public:
|
||||
}
|
||||
return m_iActivep;
|
||||
}
|
||||
|
||||
// Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep.
|
||||
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
||||
// Return a sentree in this scope that matches given sense list.
|
||||
|
||||
AstActive* activep = nullptr;
|
||||
AstSenTree* const activeSenp = m_activeSens.find(sensesp);
|
||||
if (activeSenp) {
|
||||
const auto it = m_activeMap.find(activeSenp);
|
||||
UASSERT(it != m_activeMap.end(), "Corrupt active map");
|
||||
activep = it->second;
|
||||
}
|
||||
auto it = m_activeMap.find(*sensesp);
|
||||
// If found matching AstActive, return it
|
||||
if (it != m_activeMap.end()) return it->second;
|
||||
|
||||
// Not found, form a new one
|
||||
if (!activep) {
|
||||
AstSenTree* const newsenp = sensesp->cloneTree(false);
|
||||
activep = new AstActive(fl, "sequent", newsenp);
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
UINFO(8, " New ACTIVE " << activep << endl);
|
||||
// Form the sensitivity list
|
||||
addActive(activep);
|
||||
m_activeMap[newsenp] = activep;
|
||||
m_activeSens.add(newsenp);
|
||||
// Note actives may have also been added above in the Active visitor
|
||||
}
|
||||
// No such AstActive yet, creat it, and add to map.
|
||||
AstSenTree* const newsenp = sensesp->cloneTree(false);
|
||||
AstActive* const activep = new AstActive(fl, "sequent", newsenp);
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
addActive(activep);
|
||||
m_activeMap.emplace(*newsenp, activep);
|
||||
return activep;
|
||||
}
|
||||
|
||||
@@ -476,7 +463,7 @@ private:
|
||||
m_scopeFinalp = new AstCFunc(
|
||||
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
m_scopeFinalp->formCallTree(true);
|
||||
m_scopeFinalp->isFinal(true);
|
||||
m_scopeFinalp->isStatic(false);
|
||||
m_scopeFinalp->isLoose(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
|
||||
+47
-2
@@ -2025,12 +2025,15 @@ public:
|
||||
#include "V3Ast__gen_impl.h" // From ./astgen
|
||||
|
||||
// Specializations of privateMayBeUnder
|
||||
template <> inline bool AstNode::mayBeUnder<AstCell>(const AstNode* nodep) {
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstCell>(const AstNode* nodep) {
|
||||
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
|
||||
}
|
||||
template <> inline bool AstNode::mayBeUnder<AstNodeAssign>(const AstNode* nodep) {
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstNodeAssign>(const AstNode* nodep) {
|
||||
return !VN_IS(nodep, NodeMath);
|
||||
}
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstVarScope>(const AstNode* nodep) {
|
||||
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
|
||||
if (!rhs) {
|
||||
@@ -2043,6 +2046,47 @@ inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
|
||||
inline void VNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
// VNRef is std::reference_wrapper that can only hold AstNode subtypes
|
||||
template <typename T_Node> //
|
||||
class VNRef final : public std::reference_wrapper<T_Node> {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value,
|
||||
"Type parameter 'T_Node' must be a subtype of AstNode");
|
||||
|
||||
public:
|
||||
template <typename U>
|
||||
VNRef(U&& x)
|
||||
: std::reference_wrapper<T_Node>{x} {}
|
||||
|
||||
VNRef(const VNRef& other) noexcept
|
||||
: std::reference_wrapper<T_Node>{other} {}
|
||||
};
|
||||
|
||||
static_assert(sizeof(VNRef<AstNode>) == sizeof(std::reference_wrapper<AstNode>),
|
||||
"VNRef should not contain extra members");
|
||||
|
||||
// Specializations of std::hash and std::equal_to for VNRef. This in turn
|
||||
// enables us to use for example std::unordered_set<VNRef<AstNode>> for
|
||||
// sets using equality (AstNode::sameTree) rather than identity comparisons,
|
||||
// without having to copy nodes into the collections.
|
||||
|
||||
// Forward declaration to avoid including V3Hasher.h which needs V3Ast.h (this file).
|
||||
size_t V3HasherUncachedHash(AstNode&);
|
||||
|
||||
// Specialization of std::hash for VNRef
|
||||
template <typename T_Node> //
|
||||
struct std::hash<VNRef<T_Node>> final {
|
||||
size_t operator()(VNRef<T_Node> r) const { return V3HasherUncachedHash(r); }
|
||||
};
|
||||
|
||||
// Specialization of std::equal_to for VNRef
|
||||
template <typename T_Node> //
|
||||
struct std::equal_to<VNRef<T_Node>> final {
|
||||
size_t operator()(VNRef<T_Node> ra, VNRef<T_Node> rb) const {
|
||||
return ra.get().sameTree(&(rb.get()));
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
//=== AstNode* : Derived generic node types
|
||||
|
||||
@@ -2833,6 +2877,7 @@ public:
|
||||
virtual bool isPure() const override;
|
||||
virtual bool isOutputter() const override { return !isPure(); }
|
||||
AstCFunc* funcp() const { return m_funcp; }
|
||||
void funcp(AstCFunc* funcp) { m_funcp = funcp; }
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
// op1p reserved for AstCMethodCall
|
||||
|
||||
+25
-17
@@ -454,25 +454,33 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||
if (forReturn) named = false;
|
||||
string arg;
|
||||
if (isWide() && isReadOnly()) arg += "const ";
|
||||
if (widthMin() == 1) {
|
||||
arg += "bool";
|
||||
} else if (widthMin() <= VL_IDATASIZE) {
|
||||
arg += "uint32_t";
|
||||
} else if (widthMin() <= VL_QUADSIZE) {
|
||||
arg += "vluint64_t";
|
||||
} else {
|
||||
arg += "uint32_t"; // []'s added later
|
||||
}
|
||||
if (isWide()) {
|
||||
if (forReturn) {
|
||||
v3warn(E_UNSUPPORTED, "Unsupported: Public functions with >64 bit outputs; "
|
||||
"make an output of a public task instead");
|
||||
const bool isRef = !forReturn && (isWritable() || direction().isRefOrConstRef());
|
||||
if (VN_IS(dtypeSkipRefp(), BasicDType) && !dtypeSkipRefp()->isDouble()
|
||||
&& !dtypeSkipRefp()->isString()) {
|
||||
// Backward compatible type declaration
|
||||
if (widthMin() == 1) {
|
||||
arg += "bool";
|
||||
} else if (widthMin() <= VL_IDATASIZE) {
|
||||
arg += "uint32_t";
|
||||
} else if (widthMin() <= VL_QUADSIZE) {
|
||||
arg += "vluint64_t";
|
||||
} else {
|
||||
arg += "uint32_t"; // []'s added later
|
||||
}
|
||||
if (isWide()) {
|
||||
if (forReturn) {
|
||||
v3warn(E_UNSUPPORTED, "Unsupported: Public functions with >64 bit outputs; "
|
||||
"make an output of a public task instead");
|
||||
}
|
||||
arg += " (& " + name();
|
||||
arg += ")[" + cvtToStr(widthWords()) + "]";
|
||||
} else {
|
||||
if (isRef) arg += "&";
|
||||
if (named) arg += " " + name();
|
||||
}
|
||||
arg += " (& " + name();
|
||||
arg += ")[" + cvtToStr(widthWords()) + "]";
|
||||
} else {
|
||||
if (!forReturn && (isWritable() || direction().isRefOrConstRef())) arg += "&";
|
||||
if (named) arg += " " + name();
|
||||
// Newer internal-compatible types
|
||||
arg += dtypep()->cType((named ? name() : string{}), true, isRef);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
+12
-5
@@ -8874,7 +8874,7 @@ private:
|
||||
bool m_isTrace : 1; // Function is related to tracing
|
||||
bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special
|
||||
bool m_declPrivate : 1; // Declare it private
|
||||
bool m_formCallTree : 1; // Make a global function to call entire tree of functions
|
||||
bool m_isFinal : 1; // This is a function corresponding to a SystemVerilog 'final' block
|
||||
bool m_slow : 1; // Slow routine, called once or just at init time
|
||||
bool m_funcPublic : 1; // From user public task/function
|
||||
bool m_isConstructor : 1; // Is C class constructor
|
||||
@@ -8903,7 +8903,7 @@ public:
|
||||
m_isTrace = false;
|
||||
m_dontCombine = false;
|
||||
m_declPrivate = false;
|
||||
m_formCallTree = false;
|
||||
m_isFinal = false;
|
||||
m_slow = false;
|
||||
m_funcPublic = false;
|
||||
m_isConstructor = false;
|
||||
@@ -8956,13 +8956,13 @@ public:
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
void scopep(AstScope* nodep) { m_scopep = nodep; }
|
||||
string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); }
|
||||
bool dontCombine() const { return m_dontCombine || isTrace(); }
|
||||
bool dontCombine() const { return m_dontCombine || isTrace() || entryPoint(); }
|
||||
void dontCombine(bool flag) { m_dontCombine = flag; }
|
||||
bool dontInline() const { return dontCombine() || slow() || funcPublic(); }
|
||||
bool declPrivate() const { return m_declPrivate; }
|
||||
void declPrivate(bool flag) { m_declPrivate = flag; }
|
||||
bool formCallTree() const { return m_formCallTree; }
|
||||
void formCallTree(bool flag) { m_formCallTree = flag; }
|
||||
bool isFinal() const { return m_isFinal; }
|
||||
void isFinal(bool flag) { m_isFinal = flag; }
|
||||
bool slow() const { return m_slow; }
|
||||
void slow(bool flag) { m_slow = flag; }
|
||||
bool funcPublic() const { return m_funcPublic; }
|
||||
@@ -9185,6 +9185,13 @@ public:
|
||||
}
|
||||
AstNode* stmtsp() const { return op1p(); }
|
||||
void addStmtsp(AstNode* nodep) { addOp1p(nodep); }
|
||||
void addStmtsFirstp(AstNode* nodep) {
|
||||
if (stmtsp()) {
|
||||
stmtsp()->addHereThisAsNext(nodep);
|
||||
} else {
|
||||
addStmtsp(nodep);
|
||||
}
|
||||
}
|
||||
ExecMTask* execMTaskp() const { return m_execMTaskp; }
|
||||
void execMTaskp(ExecMTask* execMTaskp) { m_execMTaskp = execMTaskp; }
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
|
||||
+27
-79
@@ -117,8 +117,8 @@ public:
|
||||
class ChangedInsertVisitor final : public VNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
ChangedState* m_statep = nullptr; // Shared state across visitors
|
||||
AstVarScope* m_vscp = nullptr; // Original (non-change) variable we're change-detecting
|
||||
ChangedState& m_state; // Shared state across visitors
|
||||
AstVarScope* const m_vscp; // Original (non-change) variable we're change-detecting
|
||||
AstVarScope* m_newvscp = nullptr; // New (change detect) variable we're change-detecting
|
||||
AstNode* m_varEqnp = nullptr; // Original var's equation to get var value
|
||||
AstNode* m_newLvEqnp = nullptr; // New var's equation to read value
|
||||
@@ -126,34 +126,32 @@ private:
|
||||
uint32_t m_detects = 0; // # detects created
|
||||
|
||||
// CONSTANTS
|
||||
enum MiscConsts {
|
||||
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
||||
// Ok to increase this, but may result in much slower model
|
||||
};
|
||||
// How many indexes before error. Ok to increase this, but may result in much slower model
|
||||
static constexpr uint32_t DETECTARRAY_MAX_INDEXES = 256;
|
||||
|
||||
void newChangeDet() {
|
||||
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
||||
m_vscp->v3warn(E_DETECTARRAY,
|
||||
"Unsupported: Can't detect more than "
|
||||
<< cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||
<< DETECTARRAY_MAX_INDEXES
|
||||
<< " array indexes (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->prettyName() << '\n'
|
||||
<< m_vscp->warnMore()
|
||||
<< "... Could recompile with DETECTARRAY_MAX_INDEXES increased");
|
||||
return;
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
m_state.maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* const changep = new AstChangeDet{
|
||||
m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true)};
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
m_state.m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true)};
|
||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||
m_state.m_chgFuncp->addFinalsp(initp);
|
||||
|
||||
// Later code will expand words which adds to GCC compile time,
|
||||
// so add penalty based on word width also
|
||||
m_statep->m_numStmts += initp->nodeCount() + m_varEqnp->widthWords();
|
||||
m_state.m_numStmts += initp->nodeCount() + m_varEqnp->widthWords();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType*) override { //
|
||||
@@ -202,13 +200,13 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState& state)
|
||||
: m_state{state}
|
||||
, m_vscp{vscp} {
|
||||
// DPI export trigger should never need change detect. See similar assertions in V3Order
|
||||
// (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk).
|
||||
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
|
||||
"DPI export trigger should not need change detect");
|
||||
m_statep = statep;
|
||||
m_vscp = vscp;
|
||||
{
|
||||
AstVar* const varp = m_vscp->varp();
|
||||
const string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__"
|
||||
@@ -219,9 +217,9 @@ public:
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* const newvarp
|
||||
= new AstVar{varp->fileline(), VVarType::MODULETEMP, newvarname, varp};
|
||||
m_statep->m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp};
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
m_state.m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope{m_vscp->fileline(), m_state.m_scopetopp, newvarp};
|
||||
m_state.m_scopetopp->addVarp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
|
||||
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
|
||||
@@ -236,72 +234,22 @@ public:
|
||||
VL_UNCOPYABLE(ChangedInsertVisitor);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Changed state, as a visitor of each AstNode
|
||||
|
||||
class ChangedVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1() -> bool. True indicates processed
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
ChangedState* const m_statep; // Shared state across visitors
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
||||
{ ChangedInsertVisitor{vscp, m_statep}; }
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
if (nodep->isTop()) m_statep->m_topModp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstTopScope* nodep) override {
|
||||
UINFO(4, " TS " << nodep << endl);
|
||||
// Clearing
|
||||
AstNode::user1ClearTree();
|
||||
// Prep for if make change detection function
|
||||
AstScope* const scopep = nodep->scopep();
|
||||
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) override {
|
||||
if (nodep->isCircular()) {
|
||||
UINFO(8, " CIRC " << nodep << endl);
|
||||
if (!nodep->user1SetOnce()) genChangeDet(nodep);
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ChangedVisitor(AstNetlist* nodep, ChangedState* statep)
|
||||
: m_statep{statep} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ChangedVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Changed class functions
|
||||
|
||||
void V3Changed::changedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
ChangedState state;
|
||||
ChangedVisitor{nodep, &state};
|
||||
} // Destruct before checking
|
||||
|
||||
ChangedState state;
|
||||
state.m_scopetopp = nodep->topScopep()->scopep();
|
||||
state.m_topModp = nodep->topModulep();
|
||||
nodep->foreach<AstVarScope>([&state](AstVarScope* vscp) {
|
||||
if (vscp->isCircular()) {
|
||||
vscp->v3warn(IMPERFECTSCH,
|
||||
"Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
||||
ChangedInsertVisitor{vscp, state};
|
||||
}
|
||||
});
|
||||
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+2
-2
@@ -322,8 +322,8 @@ private:
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree " << nodep << endl);
|
||||
if (nodep->isFinal()) {
|
||||
UINFO(4, " isFinal " << nodep << endl);
|
||||
AstCCall* const callp = new AstCCall(nodep->fileline(), nodep);
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
|
||||
+165
-157
@@ -27,195 +27,203 @@
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3AstUserAllocator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CombBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
protected:
|
||||
// STATE
|
||||
|
||||
// METHODS
|
||||
virtual ~CombBaseVisitor() override = default;
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Combine replacement function
|
||||
|
||||
class CombCallVisitor final : CombBaseVisitor {
|
||||
// Find all CCALLS of each CFUNC, so that we can later rename them
|
||||
private:
|
||||
class CombineVisitor final : VNVisitor {
|
||||
// NODE STATE
|
||||
std::multimap<AstCFunc*, AstCCall*> m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
if (oldfuncp == newfuncp) return;
|
||||
if (newfuncp) {
|
||||
UINFO(4, " Replace " << oldfuncp << " -WITH-> " << newfuncp << endl);
|
||||
} else {
|
||||
UINFO(4, " Remove " << oldfuncp << endl);
|
||||
}
|
||||
// Note: m_callMmap modified in loop, so not using equal_range.
|
||||
for (auto it = m_callMmap.find(oldfuncp); it != m_callMmap.end();
|
||||
it = m_callMmap.find(oldfuncp)) {
|
||||
AstCCall* const oldp = it->second;
|
||||
UINFO(4, " Called " << oldp << endl);
|
||||
UASSERT_OBJ(oldp->funcp() == oldfuncp, oldp,
|
||||
"Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
// Replace call to oldfuncp with call to newfuncp
|
||||
AstNode* const argsp
|
||||
= oldp->argsp() ? oldp->argsp()->unlinkFrBackWithNext() : nullptr;
|
||||
AstCCall* const newp = new AstCCall(oldp->fileline(), newfuncp, argsp);
|
||||
newp->selfPointer(oldp->selfPointer());
|
||||
newp->argTypes(oldp->argTypes());
|
||||
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
|
||||
oldp->replaceWith(newp);
|
||||
} else {
|
||||
// Just deleting empty function
|
||||
oldp->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(oldp), oldp);
|
||||
m_callMmap.erase(it); // Fix the table, This call has been replaced
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
|
||||
// AstNodeModule::user1() List of AstCFuncs in this module (via m_cfuncs)
|
||||
// AstCFunc::user1() List of AstCCalls to this function (via m_callSites)
|
||||
// AstCFunc::user2() bool: Already replaced (in 'process')
|
||||
// AstCFunc::user3() bool: Marks functions earlier in iteration order (in 'combinePass')
|
||||
// *::user4() Used by V3Hasher
|
||||
const VNUser1InUse m_user1InUse;
|
||||
|
||||
private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCCall* nodep) override {
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
addCall(nodep);
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
virtual void visit(AstAddrOfCFunc* nodep) override {
|
||||
// We cannot yet handle references via AstAddrOfCFunc, but currently those are
|
||||
// only used in tracing functions, which are not combined. Blow up in case this changes.
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
nodep->v3fatalSrc(
|
||||
"Don't know how to combine functions that are referenced via AstAddrOfCFunc");
|
||||
}
|
||||
// LCOV_EXCL_END
|
||||
// Speed things up
|
||||
virtual void visit(AstNodeAssign*) override {}
|
||||
virtual void visit(AstNodeMath*) override {}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
CombCallVisitor() = default;
|
||||
virtual ~CombCallVisitor() override = default;
|
||||
void main(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Combine state, as a visitor of each AstNode
|
||||
|
||||
class CombineVisitor final : CombBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
const VNUser3InUse m_user3InUse; // Marks replaced AstCFuncs
|
||||
// VNUser4InUse part of V3Hasher in V3DupFinder
|
||||
// TYPES
|
||||
using funcit_t = std::list<AstCFunc*>::iterator;
|
||||
struct CFuncs {
|
||||
std::list<AstCFunc*> m_fast;
|
||||
std::list<AstCFunc*> m_slow;
|
||||
};
|
||||
|
||||
// STATE
|
||||
AstUser1Allocator<AstNodeModule, CFuncs> m_cfuncs; // AstCFuncs under module
|
||||
AstUser1Allocator<AstCFunc, std::vector<AstCCall*>> m_callSites; // Call sites of the AstCFunc
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const V3Hasher m_hasher; // For hashing
|
||||
VDouble0 m_cfuncsCombined; // Statistic tracking
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
V3DupFinder m_dupFinder; // Duplicate finder for CFuncs in module
|
||||
|
||||
// METHODS
|
||||
void walkEmptyFuncs() {
|
||||
for (const auto& itr : m_dupFinder) {
|
||||
AstCFunc* const oldfuncp = VN_AS(itr.second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash");
|
||||
if (!oldfuncp->emptyBody()) continue;
|
||||
UASSERT_OBJ(!oldfuncp->dontCombine(), oldfuncp,
|
||||
"dontCombine function should not be in hash");
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Remove calls to empty function
|
||||
UASSERT_OBJ(!oldfuncp->user3(), oldfuncp, "Should not be processed yet");
|
||||
UINFO(5, " Drop empty CFunc " << itr.first << " " << oldfuncp << endl);
|
||||
oldfuncp->user3SetOnce(); // Mark replaced
|
||||
m_call.replaceFunc(oldfuncp, nullptr);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
void removeEmptyFunctions(std::list<AstCFunc*>& funcps) {
|
||||
for (funcit_t it = funcps.begin(), nit; it != funcps.end(); it = nit) {
|
||||
AstCFunc* const funcp = *it;
|
||||
nit = it;
|
||||
++nit;
|
||||
|
||||
if (funcp->emptyBody()) {
|
||||
// Delete call sites
|
||||
for (AstCCall* const callp : m_callSites(funcp)) {
|
||||
VL_DO_DANGLING(callp->unlinkFrBack()->deleteTree(), callp);
|
||||
}
|
||||
m_callSites(funcp).clear();
|
||||
// Remove from list
|
||||
funcps.erase(it);
|
||||
// Delete function
|
||||
VL_DO_DANGLING(funcp->unlinkFrBack()->deleteTree(), funcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void walkDupFuncs() {
|
||||
// Do non-slow first as then favors naming functions based on fast name
|
||||
for (const bool slow : {false, true}) {
|
||||
for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) {
|
||||
AstCFunc* const newfuncp = VN_AS(newIt->second, CFunc);
|
||||
UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash");
|
||||
if (newfuncp->user3()) continue; // Already replaced
|
||||
if (newfuncp->slow() != slow) continue;
|
||||
auto oldIt = newIt;
|
||||
++oldIt; // Skip over current position
|
||||
for (; oldIt != m_dupFinder.end(); ++oldIt) {
|
||||
AstCFunc* const oldfuncp = VN_AS(oldIt->second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash");
|
||||
UASSERT_OBJ(newfuncp != oldfuncp, newfuncp,
|
||||
"Same function hashed multiple times");
|
||||
if (newIt->first != oldIt->first) break; // Iterate over same hashes only
|
||||
if (oldfuncp->user3()) continue; // Already replaced
|
||||
if (!newfuncp->sameTree(oldfuncp)) continue; // Different functions
|
||||
// One pass of combining. Returns true if did replacement.
|
||||
bool combinePass(std::list<AstCFunc*>& funcps, V3DupFinder& dupFinder) {
|
||||
const VNUser3InUse user3InUse;
|
||||
|
||||
// Replace calls to oldfuncp with calls to newfuncp
|
||||
UINFO(5, " Replace CFunc " << newIt->first << " " << newfuncp << endl);
|
||||
UINFO(5, " with " << oldIt->first << " " << oldfuncp << endl);
|
||||
++m_cfuncsCombined;
|
||||
oldfuncp->user3SetOnce(); // Mark replaced
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!oldfuncp->slow()) newfuncp->slow(false);
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
bool replaced = false;
|
||||
|
||||
// Replace all identical functions with the first function in the list
|
||||
for (funcit_t it = funcps.begin(), nit; it != funcps.end(); it = nit) {
|
||||
AstCFunc* const funcp = *it;
|
||||
nit = it;
|
||||
++nit;
|
||||
|
||||
// Remove functions already replaced in the previous iteration
|
||||
if (funcp->user2()) {
|
||||
funcps.erase(it);
|
||||
VL_DO_DANGLING(funcp->unlinkFrBack()->deleteTree(), funcp);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
auto dit = dupFinder.findDuplicate(funcp);
|
||||
if (dit == dupFinder.end()) break;
|
||||
|
||||
AstCFunc* oldp = VN_AS(dit->second, CFunc);
|
||||
AstCFunc* newp = funcp;
|
||||
UASSERT_OBJ(!oldp->user2(), oldp, "Should have been removed from dupFinder");
|
||||
|
||||
// Swap them, if the duplicate is earlier in the list of functions. This is
|
||||
// necessary because replacing a call site in a later function might have made that
|
||||
// function equivalent to an earlier function, but we want the first equivalent
|
||||
// function in the list to be the canonical one.
|
||||
if (oldp->user3()) std::swap(oldp, newp);
|
||||
|
||||
// Something is being replaced
|
||||
UINFO(9, "Replacing " << oldp << endl);
|
||||
UINFO(9, " with " << newp << endl);
|
||||
++m_cfuncsCombined;
|
||||
replaced = true;
|
||||
|
||||
// Mark as replaced
|
||||
oldp->user2(true);
|
||||
|
||||
// Redirect the calls
|
||||
for (AstCCall* const callp : m_callSites(oldp)) {
|
||||
// For sanity check only
|
||||
const V3Hash oldHash = m_hasher(callp);
|
||||
|
||||
// Redirect the call
|
||||
callp->funcp(newp);
|
||||
|
||||
// When redirecting a call to an equivalent function, we do not need to re-hash
|
||||
// the caller, because the hash of the two calls must be the same, and hence
|
||||
// the hash of the caller should not change.
|
||||
UASSERT_OBJ(oldHash == m_hasher.rehash(callp), callp, "Hash changed");
|
||||
}
|
||||
|
||||
// Erase the replaced duplicate
|
||||
UASSERT_OBJ(dupFinder.erase(oldp) == 1, oldp, "Replaced node not in dupFinder");
|
||||
|
||||
// If we just replaced the function we are iterating (because there was an
|
||||
// equivalent earlier in the list), then move on, as this is on longer a candidate
|
||||
if (oldp == funcp) break;
|
||||
}
|
||||
|
||||
// Mark as function earlier in list of functions.
|
||||
funcp->user3(true);
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
void process(AstNetlist* netlistp) {
|
||||
// First, remove empty functions. We need to do this separately, because removing
|
||||
// calls can change the hashes of the callers.
|
||||
for (AstNodeModule* modulep = netlistp->modulesp(); modulep;
|
||||
modulep = VN_AS(modulep->nextp(), NodeModule)) {
|
||||
removeEmptyFunctions(m_cfuncs(modulep).m_fast);
|
||||
removeEmptyFunctions(m_cfuncs(modulep).m_slow);
|
||||
}
|
||||
|
||||
// Combine functions within each module
|
||||
for (AstNodeModule* modulep = netlistp->modulesp(); modulep;
|
||||
modulep = VN_AS(modulep->nextp(), NodeModule)) {
|
||||
// Put fast functions first, so they are preferred over slow functions
|
||||
auto funcps = std::move(m_cfuncs(modulep).m_fast);
|
||||
funcps.splice(funcps.end(), m_cfuncs(modulep).m_slow);
|
||||
|
||||
V3DupFinder dupFinder{m_hasher};
|
||||
|
||||
// First, hash all functions
|
||||
for (AstCFunc* const funcp : funcps) dupFinder.insert(funcp);
|
||||
|
||||
// Iterate to fixed point
|
||||
{
|
||||
const VNUser2InUse user2InUse;
|
||||
while (combinePass(funcps, dupFinder)) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
m_call.main(nodep); // Track all call sites of each function
|
||||
iterateChildren(nodep);
|
||||
// Gather functions and references
|
||||
iterateChildrenConst(nodep);
|
||||
// Combine functions
|
||||
process(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
m_dupFinder.clear();
|
||||
// Compute hash of all CFuncs in the module
|
||||
iterateChildren(nodep);
|
||||
if (debug() >= 9) m_dupFinder.dumpFilePrefixed("combine");
|
||||
// Walk the hashes removing empty functions
|
||||
walkEmptyFuncs();
|
||||
// Walk the hashes looking for duplicate functions
|
||||
walkDupFuncs();
|
||||
UASSERT_OBJ(!m_modp, nodep, "Should not nest");
|
||||
m_modp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
m_modp = nullptr;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
iterateChildrenConst(nodep);
|
||||
if (nodep->dontCombine()) return;
|
||||
// Hash the entire function
|
||||
m_dupFinder.insert(nodep);
|
||||
auto& coll = nodep->slow() ? m_cfuncs(m_modp).m_slow : m_cfuncs(m_modp).m_fast;
|
||||
coll.emplace_back(nodep);
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) override {
|
||||
iterateChildrenConst(nodep);
|
||||
AstCFunc* const funcp = nodep->funcp();
|
||||
if (funcp->dontCombine()) return;
|
||||
m_callSites(funcp).emplace_back(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstAddrOfCFunc* nodep) override {
|
||||
iterateChildrenConst(nodep);
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
// LCOV_EXCL_START
|
||||
// We cannot yet handle references via AstAddrOfCFunc, but currently those are
|
||||
// only used in tracing functions, which are not combined. Blow up in case this changes.
|
||||
nodep->v3fatalSrc(
|
||||
"Don't know how to combine functions that are referenced via AstAddrOfCFunc");
|
||||
// LCOV_EXCL_END
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstVar*) override {} // Accelerate
|
||||
virtual void visit(AstNodeStmt* nodep) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CombineVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
|
||||
}
|
||||
~CombineVisitor() { V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined); }
|
||||
|
||||
public:
|
||||
static void apply(AstNetlist* netlistp) { CombineVisitor{netlistp}; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -223,6 +231,6 @@ public:
|
||||
|
||||
void V3Combine::combineAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CombineVisitor{nodep}; } // Destruct before checking
|
||||
CombineVisitor::apply(nodep);
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
comma = ", ";
|
||||
stmt += itemp->origNameProtect();
|
||||
stmt += ":\" + ";
|
||||
if (itemp->isWide()) {
|
||||
if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W(";
|
||||
stmt += cvtToStr(itemp->widthWords());
|
||||
stmt += ", ";
|
||||
|
||||
+22
-2
@@ -41,6 +41,7 @@ private:
|
||||
|
||||
// STATE
|
||||
AstCFunc* m_cfuncp = nullptr; // Current block
|
||||
AstMTaskBody* m_mtaskbodyp = nullptr; // Current mtaskbody
|
||||
AstNode* m_stmtp = nullptr; // Current statement
|
||||
int m_depth = 0; // How deep in an expression
|
||||
int m_maxdepth = 0; // Maximum depth in an expression
|
||||
@@ -54,8 +55,13 @@ private:
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::STMTTEMP,
|
||||
m_tempNames.get(nodep), nodep->dtypep()};
|
||||
UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function");
|
||||
m_cfuncp->addInitsp(varp);
|
||||
if (m_cfuncp) {
|
||||
m_cfuncp->addInitsp(varp);
|
||||
} else if (m_mtaskbodyp) {
|
||||
m_mtaskbodyp->addStmtsFirstp(varp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Deep expression not under a function");
|
||||
}
|
||||
// Replace node tree with reference to var
|
||||
AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
nodep->replaceWith(newp);
|
||||
@@ -71,14 +77,28 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
VL_RESTORER(m_mtaskbodyp);
|
||||
{
|
||||
m_cfuncp = nodep;
|
||||
m_mtaskbodyp = nullptr;
|
||||
m_depth = 0;
|
||||
m_maxdepth = 0;
|
||||
m_tempNames.reset();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMTaskBody* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
VL_RESTORER(m_mtaskbodyp);
|
||||
{
|
||||
m_cfuncp = nullptr;
|
||||
m_mtaskbodyp = nodep;
|
||||
m_depth = 0;
|
||||
m_maxdepth = 0;
|
||||
// We don't reset the names, as must share across tasks
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visitStmt(AstNodeStmt* nodep) {
|
||||
VL_RESTORER(m_stmtp);
|
||||
{
|
||||
|
||||
+12
-1
@@ -30,6 +30,17 @@
|
||||
//######################################################################
|
||||
// V3DupFinder class functions
|
||||
|
||||
V3DupFinder::size_type V3DupFinder::erase(AstNode* nodep) {
|
||||
const auto& er = equal_range(m_hasher(nodep));
|
||||
for (iterator it = er.first; it != er.second; ++it) {
|
||||
if (nodep == it->second) {
|
||||
erase(it);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp) {
|
||||
const auto& er = equal_range(m_hasher(nodep));
|
||||
for (iterator it = er.first; it != er.second; ++it) {
|
||||
@@ -37,7 +48,7 @@ V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUser
|
||||
if (nodep == node2p) continue; // Same node is not a duplicate
|
||||
if (checkp && !checkp->isSame(nodep, node2p)) continue; // User says it is not a duplicate
|
||||
if (!nodep->sameTree(node2p)) continue; // Not the same trees
|
||||
// Found duplicate!
|
||||
// Found duplicate
|
||||
return it;
|
||||
}
|
||||
return end();
|
||||
|
||||
+16
-3
@@ -28,6 +28,7 @@
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
//============================================================================
|
||||
|
||||
@@ -43,12 +44,20 @@ class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
|
||||
using Super = std::multimap<V3Hash, AstNode*>;
|
||||
|
||||
// MEMBERS
|
||||
const V3Hasher m_hasher;
|
||||
const V3Hasher* const m_hasherp; // Pointer to owned hasher
|
||||
const V3Hasher& m_hasher; // Reference to hasher
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
V3DupFinder(){};
|
||||
~V3DupFinder() = default;
|
||||
V3DupFinder()
|
||||
: m_hasherp{new V3Hasher}
|
||||
, m_hasher{*m_hasherp} {}
|
||||
V3DupFinder(const V3Hasher& hasher)
|
||||
: m_hasherp{nullptr}
|
||||
, m_hasher{hasher} {}
|
||||
~V3DupFinder() {
|
||||
if (m_hasherp) delete m_hasherp;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
@@ -63,10 +72,14 @@ public:
|
||||
using Super::end;
|
||||
using Super::erase;
|
||||
using Super::iterator;
|
||||
using Super::size_type;
|
||||
|
||||
// Insert node into data structure
|
||||
iterator insert(AstNode* nodep) { return emplace(m_hasher(nodep), nodep); }
|
||||
|
||||
// Erase node from data structure
|
||||
size_type erase(AstNode* nodep);
|
||||
|
||||
// Return duplicate, if one was inserted, with optional user check for sameness
|
||||
iterator findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp = nullptr);
|
||||
|
||||
|
||||
@@ -192,6 +192,12 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
puts("enum " + tdefp->name() + " {\n");
|
||||
for (const AstEnumItem* itemp = edtypep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
if (const AstConst* const constp = VN_CAST(itemp->valuep(), Const)) {
|
||||
if (constp->num().isFourState()) {
|
||||
puts("// " + itemp->nameProtect() + " is four-state\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
puts(itemp->nameProtect());
|
||||
puts(" = ");
|
||||
iterate(itemp->valuep());
|
||||
|
||||
+4
-5
@@ -61,7 +61,7 @@ class EmitXmlFileVisitor final : public VNVisitor {
|
||||
void outputTag(AstNode* nodep, const string& tagin) {
|
||||
string tag = tagin;
|
||||
if (tag == "") tag = VString::downcase(nodep->typeName());
|
||||
puts("<" + tag + " " + nodep->fileline()->xml());
|
||||
puts("<" + tag);
|
||||
puts(" " + nodep->fileline()->xmlDetailedLocation());
|
||||
if (VN_IS(nodep, NodeDType)) {
|
||||
puts(" id=");
|
||||
@@ -386,8 +386,7 @@ private:
|
||||
if (nodep->level() >= 0
|
||||
&& nodep->level() <= 2) { // ==2 because we don't add wrapper when in XML mode
|
||||
m_os << "<cells>\n";
|
||||
m_os << "<cell " << nodep->fileline()->xml() << " "
|
||||
<< nodep->fileline()->xmlDetailedLocation() //
|
||||
m_os << "<cell " << nodep->fileline()->xmlDetailedLocation() //
|
||||
<< " name=\"" << nodep->prettyName() << "\""
|
||||
<< " submodname=\"" << nodep->prettyName() << "\""
|
||||
<< " hier=\"" << nodep->prettyName() << "\"";
|
||||
@@ -405,8 +404,8 @@ private:
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
if (nodep->modp()->dead()) return;
|
||||
if (!m_hasChildren) m_os << ">\n";
|
||||
m_os << "<cell " << nodep->fileline()->xml() << " "
|
||||
<< nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name() << "\""
|
||||
m_os << "<cell " << nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name()
|
||||
<< "\""
|
||||
<< " submodname=\"" << nodep->modName() << "\""
|
||||
<< " hier=\"" << m_hier + nodep->name() << "\"";
|
||||
const std::string hier = m_hier;
|
||||
|
||||
@@ -200,7 +200,6 @@ public:
|
||||
string filebasenameNoExt() const;
|
||||
string firstColumnLetters() const;
|
||||
string profileFuncname() const;
|
||||
string xml() const { return "fl=\"" + filenameLetters() + cvtToStr(lastLineno()) + "\""; }
|
||||
string xmlDetailedLocation() const;
|
||||
string lineDirectiveStrg(int enterExit) const;
|
||||
|
||||
|
||||
+9
-21
@@ -53,9 +53,7 @@ private:
|
||||
|
||||
// METHODS
|
||||
AstVarScope* genInpClk(AstVarScope* vscp) {
|
||||
if (vscp->user2p()) {
|
||||
return VN_AS(vscp->user2p(), VarScope);
|
||||
} else {
|
||||
if (!vscp->user2p()) {
|
||||
// In order to create a __VinpClk* for a signal, it needs to be marked circular.
|
||||
// The DPI export trigger is never marked circular by V3Order (see comments in
|
||||
// OrderVisitor::nodeMarkCircular). The only other place where one might mark
|
||||
@@ -86,22 +84,15 @@ private:
|
||||
m_scopetopp->addFinalClkp(asninitp);
|
||||
//
|
||||
vscp->user2p(newvscp);
|
||||
return newvscp;
|
||||
}
|
||||
return VN_AS(vscp->user2p(), VarScope);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) override {
|
||||
AstNode::user2ClearTree(); // user2p() used on entire tree
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//----
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
// Consumption/generation of a variable,
|
||||
AstVarScope* const vscp = nodep->varScopep();
|
||||
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
||||
if (m_activep && !nodep->user3()) {
|
||||
nodep->user3(true);
|
||||
if (m_activep && !nodep->user3SetOnce()) {
|
||||
AstVarScope* const vscp = nodep->varScopep();
|
||||
if (vscp->isCircular()) {
|
||||
UINFO(8, " VarActReplace " << nodep << endl);
|
||||
// Replace with the new variable
|
||||
@@ -115,10 +106,8 @@ private:
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
m_activep = nodep;
|
||||
UASSERT_OBJ(nodep->sensesp(), nodep, "Unlinked");
|
||||
iterate(nodep->sensesp());
|
||||
m_activep = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//-----
|
||||
@@ -141,7 +130,6 @@ private:
|
||||
// NODE STATE
|
||||
// Cleared on top scope
|
||||
// AstVarScope::user() -> bool. Set when the var has been used as clock
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
bool m_tracingCall = false; // Iterating into a call to a cfunc
|
||||
@@ -151,13 +139,13 @@ private:
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) override {
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
iterateChildren(nodep);
|
||||
{
|
||||
// Make the new clock signals and replace any activate references
|
||||
// See rename, it does some AstNode::userClearTree()'s
|
||||
GenClkRenameVisitor{nodep, m_topModp};
|
||||
const VNUser1InUse user1InUse;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// Make the new clock signals and replace any activate references
|
||||
// See rename, it does some AstNode::userClearTree()'s
|
||||
GenClkRenameVisitor{nodep, m_topModp};
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
// Only track the top scopes, not lower level functions
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@ class V3HierBlockPlan;
|
||||
/// end-of-stope.
|
||||
// Object must be named, or it will not persist until end-of-scope.
|
||||
// Constructor needs () or GCC 4.8 false warning.
|
||||
#define VL_RESTORER(var) const VRestorer<decltype(var)> restorer_##var(var);
|
||||
#define VL_RESTORER(var) \
|
||||
const VRestorer<typename std::decay<decltype(var)>::type> restorer_##var(var);
|
||||
|
||||
// Object used by VL_RESTORER. This object must be an auto variable, not
|
||||
// allocated on the heap or otherwise.
|
||||
|
||||
@@ -516,7 +516,21 @@ V3Hash V3Hasher::operator()(AstNode* nodep) const {
|
||||
return V3Hash(nodep->user4());
|
||||
}
|
||||
|
||||
V3Hash V3Hasher::rehash(AstNode* nodep) const {
|
||||
nodep->user4(0);
|
||||
HasherVisitor{nodep};
|
||||
return V3Hash(nodep->user4());
|
||||
}
|
||||
|
||||
V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
|
||||
const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}};
|
||||
return visitor.finalHash();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// This is used by the std::hash specialization for VNRef.
|
||||
// Declared separately to avoid a circular header dependency.
|
||||
|
||||
size_t V3HasherUncachedHash(AstNode& node) {
|
||||
return static_cast<size_t>(V3Hasher::uncachedHash(&node).value());
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ public:
|
||||
// Compute hash of node. This method caches the hash in the node's user4().
|
||||
V3Hash operator()(AstNode* nodep) const;
|
||||
|
||||
// Re-compute hash of this node, discarding cached value, but used cached hash of children.
|
||||
V3Hash rehash(AstNode* nodep) const;
|
||||
|
||||
// Compute hash of node, without caching in user4.
|
||||
static V3Hash uncachedHash(const AstNode* nodep);
|
||||
};
|
||||
|
||||
+6
-6
@@ -320,7 +320,7 @@ private:
|
||||
if (!nodep->exprp()) return; // No-connect
|
||||
if (m_cellRangep) {
|
||||
UINFO(4, " PIN " << nodep << endl);
|
||||
const int pinwidth = nodep->modVarp()->width();
|
||||
const int modwidth = nodep->modVarp()->width();
|
||||
const int expwidth = nodep->exprp()->width();
|
||||
const std::pair<uint32_t, uint32_t> pinDim
|
||||
= nodep->modVarp()->dtypep()->dimensions(false);
|
||||
@@ -328,7 +328,7 @@ private:
|
||||
= nodep->exprp()->dtypep()->dimensions(false);
|
||||
UINFO(4, " PINVAR " << nodep->modVarp() << endl);
|
||||
UINFO(4, " EXP " << nodep->exprp() << endl);
|
||||
UINFO(4, " pinwidth ew=" << expwidth << " pw=" << pinwidth << " ed=" << expDim.first
|
||||
UINFO(4, " modwidth ew=" << expwidth << " pw=" << modwidth << " ed=" << expDim.first
|
||||
<< "," << expDim.second << " pd=" << pinDim.first << ","
|
||||
<< pinDim.second << endl);
|
||||
if (expDim.first == pinDim.first && expDim.second == pinDim.second + 1) {
|
||||
@@ -341,11 +341,11 @@ private:
|
||||
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
||||
exprp = new AstArraySel(exprp->fileline(), exprp, arraySelNum);
|
||||
nodep->exprp(exprp);
|
||||
} else if (expwidth == pinwidth) {
|
||||
} else if (expwidth == modwidth) {
|
||||
// NOP: Arrayed instants: widths match so connect to each instance
|
||||
} else if (expwidth == pinwidth * m_cellRangep->elementsConst()) {
|
||||
} else if (expwidth == modwidth * m_cellRangep->elementsConst()) {
|
||||
// Arrayed instants: one bit for each of the instants (each
|
||||
// assign is 1 pinwidth wide)
|
||||
// assign is 1 modwidth wide)
|
||||
if (m_cellRangep->littleEndian()) {
|
||||
nodep->exprp()->v3warn(LITENDIAN, "Little endian instance range connecting to "
|
||||
"vector: left < right of instance range: ["
|
||||
@@ -362,7 +362,7 @@ private:
|
||||
"with output connections to non-wires.");
|
||||
// Note spec allows more complicated matches such as slices and such
|
||||
}
|
||||
exprp = new AstSel(exprp->fileline(), exprp, pinwidth * m_instSelNum, pinwidth);
|
||||
exprp = new AstSel(exprp->fileline(), exprp, modwidth * m_instSelNum, modwidth);
|
||||
nodep->exprp(exprp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Width mismatch; V3Width should have errored out.");
|
||||
|
||||
+5
-1
@@ -942,7 +942,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
// places such as tasks, where "task ...; begin ... end"
|
||||
// are common.
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (VN_IS(stmtp, Var)) {
|
||||
if (VN_IS(stmtp, Var) || VN_IS(stmtp, Foreach)) {
|
||||
++m_modBlockNum;
|
||||
nodep->name("unnamedblk" + cvtToStr(m_modBlockNum));
|
||||
break;
|
||||
@@ -1092,6 +1092,10 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
} else {
|
||||
findvarp->combineType(nodep);
|
||||
findvarp->fileline()->modifyStateInherit(nodep->fileline());
|
||||
if (nodep->getChildDTypep()->numeric().isSigned()
|
||||
&& !findvarp->getChildDTypep()->numeric().isSigned()) {
|
||||
findvarp->getChildDTypep()->numeric(VSigning(true));
|
||||
}
|
||||
AstBasicDType* const bdtypep
|
||||
= VN_CAST(findvarp->childDTypep(), BasicDType);
|
||||
if (bdtypep && bdtypep->implicit()) {
|
||||
|
||||
+16
-3
@@ -62,6 +62,9 @@ private:
|
||||
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
||||
bool m_unsupportedHere = false; // Used to detect where it's not supported yet
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
||||
// Return node that must be visited, if any
|
||||
// See also AstNode::addBeforeStmt; this predates that function
|
||||
@@ -86,7 +89,7 @@ private:
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
// Reset increments count
|
||||
VL_RESTORER(m_modIncrementsNum);
|
||||
m_modIncrementsNum = 0;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -131,6 +134,16 @@ private:
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_insStmtp = nullptr;
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep) override {
|
||||
m_insMode = IM_BEFORE;
|
||||
{
|
||||
VL_RESTORER(m_unsupportedHere);
|
||||
m_unsupportedHere = true;
|
||||
iterateAndNextNull(nodep->condsp());
|
||||
}
|
||||
m_insStmtp = nullptr; // Next thing should be new statement
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc(
|
||||
"For statements should have been converted to while statements in V3Begin.cpp");
|
||||
@@ -146,10 +159,10 @@ private:
|
||||
m_insStmtp = nullptr; // Next thing should be new statement
|
||||
}
|
||||
void unsupported_visit(AstNode* nodep) {
|
||||
VL_RESTORER(m_unsupportedHere);
|
||||
m_unsupportedHere = true;
|
||||
UINFO(9, "Marking unsupported " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
m_unsupportedHere = false;
|
||||
}
|
||||
virtual void visit(AstLogAnd* nodep) override { unsupported_visit(nodep); }
|
||||
virtual void visit(AstLogOr* nodep) override { unsupported_visit(nodep); }
|
||||
@@ -262,5 +275,5 @@ public:
|
||||
void V3LinkInc::linkIncrements(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ LinkIncVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("linkInc", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("linkinc", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+4
-1
@@ -1170,7 +1170,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
case 'l': m_oLife = flag; break;
|
||||
case 'm': m_oAssemble = flag; break;
|
||||
// n
|
||||
case 'o': m_oConstBitOpTree = flag; break; // Can remove ~2022-01 when stable
|
||||
case 'o':
|
||||
m_oConstBitOpTree = flag;
|
||||
break; // Can remove ~2022-01 when stable
|
||||
// o will be used as an escape for a second character of optimization disables
|
||||
case 'p':
|
||||
m_public = !flag;
|
||||
break; // With -Op so flag=0, we want public on so few optimizations done
|
||||
|
||||
+27
-12
@@ -535,6 +535,22 @@ class OrderBuildVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// Roles of vertices:
|
||||
// VarVertexType::STD: Data dependencies for combinational logic and delayed
|
||||
// assignment updates (AssignPost).
|
||||
// VarVertexType::POST: Ensures all sequential blocks reading a signal do so before
|
||||
// any combinational or delayed assignments update that signal.
|
||||
// VarVertexType::PORD: Ensures a _d = _q AssignPre is the first write of a _d,
|
||||
// before any sequential blocks write to that _d.
|
||||
// VarVertexType::PRE: This is an optimization. Try to ensure that a _d = _q
|
||||
// AssignPre is the last read of a _q, after all reads of that
|
||||
// _q by sequential logic. Note: The model is still correct if we
|
||||
// cannot satisfy this due to other constraints. If this ordering
|
||||
// is possible, then combined with the PORD constraint we get
|
||||
// that all writes to _d are after all reads of a _q, which then
|
||||
// allows us to eliminate the _d completely and assign to the _q
|
||||
// directly (this is what V3LifePost does).
|
||||
|
||||
// Variable is produced
|
||||
if (gen) {
|
||||
// Update VarUsage
|
||||
@@ -1038,8 +1054,8 @@ public:
|
||||
|
||||
class OrderProcess final : VNDeleter {
|
||||
// NODE STATE
|
||||
// AstNodeModule::user3 -> int: Number of AstCFuncs created under this module
|
||||
// AstNode::user4 -> Used by V3Const::constifyExpensiveEdit
|
||||
// AstNode::user3 -> Used by loop reporting
|
||||
// AstNode::user4 -> Used by V3Const::constifyExpensiveEdit
|
||||
const VNUser3InUse user3InUse;
|
||||
|
||||
// STATE
|
||||
@@ -1057,6 +1073,7 @@ class OrderProcess final : VNDeleter {
|
||||
friend class OrderMoveDomScope;
|
||||
V3List<OrderMoveDomScope*> m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId
|
||||
std::vector<OrderVarStdVertex*> m_unoptflatVars; // Vector of variables in UNOPTFLAT loop
|
||||
std::map<std::pair<AstNodeModule*, std::string>, unsigned> m_funcNums; // Function ordinals
|
||||
|
||||
// STATS
|
||||
std::array<VDouble0, OrderVEdgeType::_ENUM_END> m_statCut; // Count of each edge type cut
|
||||
@@ -1099,16 +1116,14 @@ class OrderProcess final : VNDeleter {
|
||||
|
||||
string cfuncName(AstNodeModule* modp, AstSenTree* domainp, AstScope* scopep,
|
||||
AstNode* forWhatp) {
|
||||
modp->user3Inc();
|
||||
const int funcnum = modp->user3();
|
||||
string name = (domainp->hasCombo()
|
||||
? "_combo"
|
||||
: (domainp->hasInitial()
|
||||
? "_initial"
|
||||
: (domainp->hasSettle()
|
||||
? "_settle"
|
||||
: (domainp->isMulti() ? "_multiclk" : "_sequent"))));
|
||||
name = name + "__" + scopep->nameDotless() + "__" + cvtToStr(funcnum);
|
||||
string name = domainp->hasCombo() ? "_combo"
|
||||
: domainp->hasInitial() ? "_initial"
|
||||
: domainp->hasSettle() ? "_settle"
|
||||
: domainp->isMulti() ? "_multiclk"
|
||||
: "_sequent";
|
||||
name = name + "__" + scopep->nameDotless();
|
||||
const unsigned funcnum = m_funcNums.emplace(std::make_pair(modp, name), 0).first->second++;
|
||||
name = name + "__" + cvtToStr(funcnum);
|
||||
if (v3Global.opt.profCFuncs()) {
|
||||
name += "__PROF__" + forWhatp->fileline()->profileFuncname();
|
||||
}
|
||||
|
||||
+7
-2
@@ -554,10 +554,15 @@ class ParamProcessor final {
|
||||
cellp->v3error("Exceeded maximum --module-recursion-depth of "
|
||||
<< v3Global.opt.moduleRecursionDepth());
|
||||
}
|
||||
// Keep tree sorted by level
|
||||
// Keep tree sorted by level. Append to end of sub-list at the same level. This is
|
||||
// important because due to the way recursive modules are handled, different
|
||||
// parametrizations of the same recursive module end up with the same level (which in
|
||||
// itself is a bit unfortunate). Nevertheless, as a later parametrization must not be above
|
||||
// an earlier parametrization of a recursive module, it is sufficient to add to the end of
|
||||
// the sub-list to keep the modules topologically sorted.
|
||||
AstNodeModule* insertp = srcModp;
|
||||
while (VN_IS(insertp->nextp(), NodeModule)
|
||||
&& VN_AS(insertp->nextp(), NodeModule)->level() < newmodp->level()) {
|
||||
&& VN_AS(insertp->nextp(), NodeModule)->level() <= newmodp->level()) {
|
||||
insertp = VN_AS(insertp->nextp(), NodeModule);
|
||||
}
|
||||
insertp->addNextHere(newmodp);
|
||||
|
||||
+14
-53
@@ -31,51 +31,11 @@
|
||||
// Collect SenTrees under the entire scope
|
||||
// And provide functions to find/add a new one
|
||||
|
||||
class SenTreeSet final {
|
||||
// Hash table of sensitive blocks.
|
||||
private:
|
||||
// TYPES
|
||||
struct HashSenTree {
|
||||
size_t operator()(const AstSenTree* kp) const {
|
||||
return V3Hasher::uncachedHash(kp).value();
|
||||
}
|
||||
};
|
||||
|
||||
struct EqSenTree {
|
||||
bool operator()(const AstSenTree* ap, const AstSenTree* bp) const {
|
||||
return ap->sameTree(bp);
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
using Set = std::unordered_set<AstSenTree*, HashSenTree, EqSenTree>;
|
||||
Set m_trees; // Set of sensitive blocks, for folding.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
SenTreeSet() = default;
|
||||
|
||||
// METHODS
|
||||
void add(AstSenTree* nodep) { m_trees.insert(nodep); }
|
||||
|
||||
AstSenTree* find(AstSenTree* likep) {
|
||||
AstSenTree* resultp = nullptr;
|
||||
const auto it = m_trees.find(likep);
|
||||
if (it != m_trees.end()) resultp = *it;
|
||||
return resultp;
|
||||
}
|
||||
|
||||
void clear() { m_trees.clear(); }
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(SenTreeSet);
|
||||
};
|
||||
|
||||
class SenTreeFinder final {
|
||||
private:
|
||||
// STATE
|
||||
AstTopScope* const m_topScopep; // Top scope to add global SenTrees to
|
||||
SenTreeSet m_trees; // Set of global SenTrees
|
||||
std::unordered_set<VNRef<AstSenTree>> m_trees; // Set of global SenTrees
|
||||
|
||||
VL_UNCOPYABLE(SenTreeFinder);
|
||||
|
||||
@@ -87,25 +47,26 @@ public:
|
||||
explicit SenTreeFinder(AstNetlist* netlistp)
|
||||
: m_topScopep{netlistp->topScopep()} {
|
||||
// Gather existing global SenTrees
|
||||
for (AstNode* nodep = m_topScopep->senTreesp(); nodep; nodep = nodep->nextp()) {
|
||||
m_trees.add(VN_AS(nodep, SenTree));
|
||||
for (AstSenTree* senTreep = m_topScopep->senTreesp(); senTreep;
|
||||
senTreep = VN_AS(senTreep->nextp(), SenTree)) {
|
||||
m_trees.emplace(*senTreep);
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// Return a global AstSenTree that matches given SenTree.
|
||||
// Return a global AstSenTree equivalent to the given senTreep.
|
||||
// If no such global AstSenTree exists create one and add it to the stored AstTopScope.
|
||||
AstSenTree* getSenTree(AstSenTree* senTreep) {
|
||||
AstSenTree* treep = m_trees.find(senTreep);
|
||||
if (!treep) {
|
||||
// Not found, form a new one
|
||||
treep = senTreep->cloneTree(false);
|
||||
m_topScopep->addSenTreep(treep);
|
||||
UINFO(8, " New SENTREE " << treep << endl);
|
||||
m_trees.add(treep);
|
||||
}
|
||||
return treep;
|
||||
auto it = m_trees.find(*senTreep);
|
||||
// If match found, return it.
|
||||
if (it != m_trees.end()) return &(*it).get();
|
||||
|
||||
// Not found, create a new one
|
||||
AstSenTree* const newSenTreep = senTreep->cloneTree(false);
|
||||
m_topScopep->addSenTreep(newSenTreep);
|
||||
m_trees.emplace(*newSenTreep);
|
||||
return newSenTreep;
|
||||
}
|
||||
|
||||
// Return the global combinational AstSenTree.
|
||||
|
||||
+53
-18
@@ -254,14 +254,11 @@ protected:
|
||||
SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
bool m_inDly; // Inside ASSIGNDLY
|
||||
VDouble0 m_statSplits; // Statistic tracking
|
||||
|
||||
// CONSTRUCTORS
|
||||
public:
|
||||
SplitReorderBaseVisitor() { scoreboardClear(); }
|
||||
virtual ~SplitReorderBaseVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Split always", m_statSplits);
|
||||
}
|
||||
virtual ~SplitReorderBaseVisitor() override = default;
|
||||
|
||||
// METHODS
|
||||
protected:
|
||||
@@ -794,21 +791,55 @@ private:
|
||||
};
|
||||
|
||||
class RemovePlaceholdersVisitor final : public VNVisitor {
|
||||
std::unordered_set<AstNode*> m_removeSet; // placeholders to be removed
|
||||
public:
|
||||
explicit RemovePlaceholdersVisitor(AstNode* nodep) {
|
||||
iterate(nodep);
|
||||
for (AstNode* np : m_removeSet) {
|
||||
np->unlinkFrBack(); // Without next
|
||||
VL_DO_DANGLING(np->deleteTree(), np);
|
||||
// MEMBERS
|
||||
bool m_isPure = true;
|
||||
int m_emptyAlways = 0;
|
||||
|
||||
// CONSTRUCTORS
|
||||
RemovePlaceholdersVisitor() = default;
|
||||
virtual ~RemovePlaceholdersVisitor() override = default;
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstSplitPlaceholder* nodep) override { pushDeletep(nodep->unlinkFrBack()); }
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
VL_RESTORER(m_isPure);
|
||||
m_isPure = true;
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->ifsp() && !nodep->elsesp() && m_isPure) pushDeletep(nodep->unlinkFrBack());
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
VL_RESTORER(m_isPure);
|
||||
m_isPure = true;
|
||||
iterateChildren(nodep);
|
||||
if (m_isPure) {
|
||||
bool emptyOrCommentOnly = true;
|
||||
for (AstNode* bodysp = nodep->bodysp(); bodysp; bodysp = bodysp->nextp()) {
|
||||
// If this always block contains only AstComment, remove here.
|
||||
// V3Gate will remove anyway.
|
||||
if (!VN_IS(bodysp, Comment)) {
|
||||
emptyOrCommentOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (emptyOrCommentOnly) {
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
++m_emptyAlways;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual ~RemovePlaceholdersVisitor() override = default;
|
||||
virtual void visit(AstSplitPlaceholder* nodep) override { m_removeSet.insert(nodep); }
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
m_isPure &= nodep->isPure();
|
||||
iterateChildren(nodep); // must visit regardless of m_isPure to remove placeholders
|
||||
}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(RemovePlaceholdersVisitor);
|
||||
|
||||
public:
|
||||
static int exec(AstAlways* nodep) {
|
||||
RemovePlaceholdersVisitor visitor;
|
||||
visitor.iterate(nodep);
|
||||
return visitor.m_emptyAlways;
|
||||
}
|
||||
};
|
||||
|
||||
class SplitVisitor final : public SplitReorderBaseVisitor {
|
||||
@@ -820,6 +851,7 @@ private:
|
||||
|
||||
// AstNodeIf* whose condition we're currently visiting
|
||||
const AstNode* m_curIfConditional = nullptr;
|
||||
VDouble0 m_statSplits; // Statistic tracking
|
||||
|
||||
// CONSTRUCTORS
|
||||
public:
|
||||
@@ -834,14 +866,17 @@ public:
|
||||
for (AlwaysVec::iterator addme = it->second.begin(); addme != it->second.end();
|
||||
++addme) {
|
||||
origp->addNextHere(*addme);
|
||||
RemovePlaceholdersVisitor{*addme};
|
||||
const int numRemoved = RemovePlaceholdersVisitor::exec(*addme);
|
||||
m_statSplits -= numRemoved;
|
||||
}
|
||||
origp->unlinkFrBack(); // Without next
|
||||
VL_DO_DANGLING(origp->deleteTree(), origp);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~SplitVisitor() override = default;
|
||||
virtual ~SplitVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Split always", m_statSplits);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
protected:
|
||||
@@ -944,7 +979,7 @@ protected:
|
||||
// Counting original always blocks rather than newly-split
|
||||
// always blocks makes it a little easier to use this stat to
|
||||
// check the result of the t_alw_split test:
|
||||
++m_statSplits;
|
||||
m_statSplits += ifColor.colors().size() - 1; // -1 for the original always
|
||||
|
||||
// Visit through the original always block one more time,
|
||||
// and emit the split always blocks into m_replaceBlocks:
|
||||
|
||||
+29
-12
@@ -4403,26 +4403,26 @@ private:
|
||||
modDTypep = modDTypep->skipRefp();
|
||||
conDTypep = conDTypep->skipRefp();
|
||||
AstNodeDType* subDTypep = modDTypep;
|
||||
const int pinwidth = modDTypep->width();
|
||||
const int modwidth = modDTypep->width();
|
||||
const int conwidth = conDTypep->width();
|
||||
if (conDTypep == modDTypep // If match, we're golden
|
||||
|| similarDTypeRecurse(conDTypep, modDTypep)) {
|
||||
userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p());
|
||||
} else if (m_cellp->rangep()) {
|
||||
const int numInsts = m_cellp->rangep()->elementsConst();
|
||||
if (conwidth == pinwidth) {
|
||||
if (conwidth == modwidth) {
|
||||
// Arrayed instants: widths match so connect to each instance
|
||||
subDTypep = conDTypep; // = same expr dtype
|
||||
} else if (conwidth == numInsts * pinwidth) {
|
||||
} else if (conwidth == numInsts * modwidth) {
|
||||
// Arrayed instants: one bit for each of the instants (each
|
||||
// assign is 1 pinwidth wide)
|
||||
// assign is 1 modwidth wide)
|
||||
subDTypep = conDTypep; // = same expr dtype (but numInst*pin_dtype)
|
||||
} else {
|
||||
// Must be a error according to spec
|
||||
// (Because we need to know if to connect to one or all instants)
|
||||
nodep->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " as part of a module instance array"
|
||||
<< " requires " << pinwidth << " or " << pinwidth * numInsts
|
||||
<< " requires " << modwidth << " or " << modwidth * numInsts
|
||||
<< " bits, but connection's "
|
||||
<< nodep->exprp()->prettyTypeName() << " generates " << conwidth
|
||||
<< " bits. (IEEE 1800-2017 23.3.3)");
|
||||
@@ -4438,12 +4438,12 @@ private:
|
||||
<< " data type but connection is "
|
||||
<< conDTypep->prettyDTypeNameQ() << " data type.");
|
||||
} else if (nodep->modVarp()->isTristate()) {
|
||||
if (pinwidth != conwidth) {
|
||||
if (modwidth != conwidth) {
|
||||
// Ideally should call pinReconnectSimple which would tolerate this
|
||||
// then have a conversion warning
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: " << ucfirst(nodep->prettyOperatorName())
|
||||
<< " to inout signal requires " << pinwidth
|
||||
<< " to inout signal requires " << modwidth
|
||||
<< " bits, but connection's "
|
||||
<< nodep->exprp()->prettyTypeName()
|
||||
<< " generates " << conwidth << " bits.");
|
||||
@@ -4524,9 +4524,9 @@ private:
|
||||
// Very much like like an pin
|
||||
const AstNodeDType* const conDTypep = nodep->exprp()->dtypep();
|
||||
const int numInsts = nodep->rangep()->elementsConst();
|
||||
const int pinwidth = numInsts;
|
||||
const int modwidth = numInsts;
|
||||
const int conwidth = conDTypep->width();
|
||||
if (conwidth == 1 && pinwidth > 1) { // Multiple connections
|
||||
if (conwidth == 1 && modwidth > 1) { // Multiple connections
|
||||
AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, conDTypep->numeric());
|
||||
userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p());
|
||||
AstNode* const newp = new AstReplicate(nodep->fileline(),
|
||||
@@ -5049,7 +5049,7 @@ private:
|
||||
|
||||
void visit_negate_not(AstNodeUniop* nodep, bool real_ok) {
|
||||
// CALLER: (real_ok=false) Not
|
||||
// CALLER: (real_ok=true) Negate
|
||||
// CALLER: (real_ok=true) Negate - allow real numbers
|
||||
// Signed: From lhs
|
||||
// IEEE-2012 Table 11-21:
|
||||
// Widths: out width = lhs width
|
||||
@@ -5076,7 +5076,14 @@ private:
|
||||
AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
|
||||
nodep->dtypep(expDTypep); // Propagate expression type to negation
|
||||
AstNodeDType* const subDTypep = expDTypep;
|
||||
iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP);
|
||||
// Some warning suppressions
|
||||
bool lhsWarn = true;
|
||||
if (VN_IS(nodep, Negate)) {
|
||||
// Warn if user wants extra bit from carry
|
||||
if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false;
|
||||
}
|
||||
iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP,
|
||||
lhsWarn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6292,8 +6299,18 @@ private:
|
||||
const bool fromNumericable = VN_IS(fromBaseDtp, BasicDType)
|
||||
|| VN_IS(fromBaseDtp, EnumDType)
|
||||
|| VN_IS(fromBaseDtp, NodeUOrStructDType);
|
||||
|
||||
const AstNodeDType* toBaseDtp = toDtp;
|
||||
while (const AstPackArrayDType* const packp = VN_CAST(toBaseDtp, PackArrayDType)) {
|
||||
toBaseDtp = packp->subDTypep();
|
||||
while (const AstRefDType* const refp = VN_CAST(toBaseDtp, RefDType)) {
|
||||
toBaseDtp = refp->refDTypep();
|
||||
}
|
||||
}
|
||||
const bool toNumericable
|
||||
= VN_IS(toBaseDtp, BasicDType) || VN_IS(toBaseDtp, NodeUOrStructDType);
|
||||
// UNSUP unpacked struct/unions (treated like BasicDType)
|
||||
if (VN_IS(toDtp, BasicDType) || VN_IS(toDtp, NodeUOrStructDType)) {
|
||||
if (toNumericable) {
|
||||
if (fromNumericable) return COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, EnumDType)) {
|
||||
if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp)) return ENUM_IMPLICIT;
|
||||
|
||||
@@ -142,7 +142,6 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||
-?"-match" { FL; return yVLT_D_MATCH; }
|
||||
-?"-model" { FL; return yVLT_D_MODEL; }
|
||||
-?"-module" { FL; return yVLT_D_MODULE; }
|
||||
-?"-msg" { FL; return yVLT_D_MSG; }
|
||||
-?"-mtask" { FL; return yVLT_D_MTASK; }
|
||||
-?"-rule" { FL; return yVLT_D_RULE; }
|
||||
-?"-task" { FL; return yVLT_D_TASK; }
|
||||
|
||||
@@ -370,7 +370,6 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
||||
%token<fl> yVLT_D_MATCH "--match"
|
||||
%token<fl> yVLT_D_MODEL "--model"
|
||||
%token<fl> yVLT_D_MODULE "--module"
|
||||
%token<fl> yVLT_D_MSG "--msg"
|
||||
%token<fl> yVLT_D_MTASK "--mtask"
|
||||
%token<fl> yVLT_D_RULE "--rule"
|
||||
%token<fl> yVLT_D_TASK "--task"
|
||||
@@ -6436,10 +6435,6 @@ vltOffFront<errcodeen>:
|
||||
yVLT_COVERAGE_OFF { $$ = V3ErrorCode::I_COVERAGE; }
|
||||
| yVLT_TRACING_OFF { $$ = V3ErrorCode::I_TRACING; }
|
||||
| yVLT_LINT_OFF { $$ = V3ErrorCode::I_LINT; }
|
||||
| yVLT_LINT_OFF yVLT_D_MSG idAny
|
||||
{ $$ = V3ErrorCode((*$3).c_str());
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); }
|
||||
$2->v3warn(DEPRECATED, "Deprecated -msg in configuration files, use -rule instead."); }
|
||||
| yVLT_LINT_OFF yVLT_D_RULE idAny
|
||||
{ $$ = V3ErrorCode((*$3).c_str());
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); } }
|
||||
@@ -6449,10 +6444,6 @@ vltOnFront<errcodeen>:
|
||||
yVLT_COVERAGE_ON { $$ = V3ErrorCode::I_COVERAGE; }
|
||||
| yVLT_TRACING_ON { $$ = V3ErrorCode::I_TRACING; }
|
||||
| yVLT_LINT_ON { $$ = V3ErrorCode::I_LINT; }
|
||||
| yVLT_LINT_ON yVLT_D_MSG idAny
|
||||
{ $$ = V3ErrorCode((*$3).c_str());
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); }
|
||||
$2->v3warn(DEPRECATED, "Deprecated -msg in configuration files, use -rule instead."); }
|
||||
| yVLT_LINT_ON yVLT_D_RULE idAny
|
||||
{ $$ = V3ErrorCode((*$3).c_str());
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); } }
|
||||
|
||||
@@ -2434,7 +2434,7 @@ sub file_grep {
|
||||
return if ($contents eq "_Already_Errored_");
|
||||
if ($contents !~ /$regexp/) {
|
||||
$self->error("File_grep: $filename: Regexp not found: $regexp\n");
|
||||
} elsif ($expvalue && $expvalue ne $1) {
|
||||
} elsif (defined($expvalue) && $expvalue ne $1) {
|
||||
$self->error("File_grep: $filename: Got='$1' Expected='$expvalue' in regexp: $regexp\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,21 @@ module t (/*AUTOARG*/
|
||||
f_split_1 = m_din;
|
||||
end
|
||||
|
||||
reg [15:0] l_split_1, l_split_2;
|
||||
always @ (posedge clk) begin
|
||||
l_split_2 <= l_split_1;
|
||||
l_split_1 <= l_split_2 | m_din;
|
||||
end
|
||||
function logic[15:0] sideeffect_func(logic [15:0] v);
|
||||
/*verilator no_inline_task */
|
||||
$display(" sideeffect_func() is called %t", $time);
|
||||
return ~v;
|
||||
endfunction
|
||||
reg [15:0] m_split_1 = 0;
|
||||
reg [15:0] m_split_2 = 0;
|
||||
always @(posedge clk) begin
|
||||
if (sideeffect_func(m_split_1) != 16'b0) begin
|
||||
m_split_1 <= m_din;
|
||||
end else begin
|
||||
m_split_2 <= m_din;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
reg [15:0] z_split_1, z_split_2;
|
||||
always @ (posedge clk) begin
|
||||
@@ -104,6 +114,7 @@ module t (/*AUTOARG*/
|
||||
if (!(c_split_1==16'h0112 && c_split_2==16'hfeed)) $stop;
|
||||
if (!(e_split_1==16'hfeed && e_split_2==16'hfeed)) $stop;
|
||||
if (!(f_split_1==16'hfeed && f_split_2==16'hfeed)) $stop;
|
||||
if (!(m_split_1==16'hfeed && m_split_2==16'h0000)) $stop;
|
||||
if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop;
|
||||
end
|
||||
if (cyc==5) begin
|
||||
@@ -113,6 +124,7 @@ module t (/*AUTOARG*/
|
||||
// Two valid orderings, as we don't know which posedge clk gets evaled first
|
||||
if (!(e_split_1==16'hfeed && e_split_2==16'hfeed) && !(e_split_1==16'he11e && e_split_2==16'he11e)) $stop;
|
||||
if (!(f_split_1==16'hfeed && f_split_2==16'hfeed) && !(f_split_1==16'he11e && f_split_2==16'hfeed)) $stop;
|
||||
if (!(m_split_1==16'hfeed && m_split_2==16'h0000)) $stop;
|
||||
if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop;
|
||||
end
|
||||
if (cyc==6) begin
|
||||
@@ -122,6 +134,7 @@ module t (/*AUTOARG*/
|
||||
// Two valid orderings, as we don't know which posedge clk gets evaled first
|
||||
if (!(e_split_1==16'he11e && e_split_2==16'he11e) && !(e_split_1==16'he22e && e_split_2==16'he22e)) $stop;
|
||||
if (!(f_split_1==16'he11e && f_split_2==16'hfeed) && !(f_split_1==16'he22e && f_split_2==16'he11e)) $stop;
|
||||
if (!(m_split_1==16'he11e && m_split_2==16'h0000)) $stop;
|
||||
if (!(z_split_1==16'h1ee1 && z_split_2==16'h0112)) $stop;
|
||||
end
|
||||
if (cyc==7) begin
|
||||
|
||||
@@ -15,7 +15,7 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 3);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 4);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -50,6 +50,12 @@ module t (/*AUTOARG*/
|
||||
end
|
||||
end
|
||||
|
||||
reg [15:0] l_split_1, l_split_2;
|
||||
always @ (posedge clk) begin
|
||||
l_split_2 <= l_split_1;
|
||||
l_split_1 <= l_split_2 | m_din;
|
||||
end
|
||||
|
||||
// (The checker block is an exception, it won't split.)
|
||||
always @ (posedge clk) begin
|
||||
if (cyc!=0) begin
|
||||
|
||||
@@ -16,7 +16,7 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 12);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -15,7 +15,7 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 5);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -16,8 +16,7 @@ compile(
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep($Self->{stats}, qr/Optimizations, Tables created\s+(\d+)/i, 10);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Combined CFuncs\s+(\d+)/i,
|
||||
($Self->{vltmt} ? 0 : 8));
|
||||
file_grep($Self->{stats}, qr/Optimizations, Combined CFuncs\s+(\d+)/i, 8);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--trace --Os -x-assign 0"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,156 @@
|
||||
module t
|
||||
(/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
logic [1:0] case_sel;
|
||||
|
||||
always @ (posedge clk) begin : main
|
||||
cyc <= cyc + 1;
|
||||
|
||||
case_sel <= 2'($urandom);
|
||||
|
||||
if (cyc > 100) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
// -------------------------
|
||||
// Simple case-stmt with ++/--
|
||||
|
||||
logic [3:0] count_d;
|
||||
logic [3:0] count_q = '0;
|
||||
|
||||
logic [3:0] want_count_d;
|
||||
logic [3:0] want_count_q = '0;
|
||||
|
||||
always_ff @(posedge clk) begin : flops
|
||||
count_q <= count_d;
|
||||
want_count_q <= want_count_d;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin : simple_check
|
||||
if (cyc > 0) begin
|
||||
if (count_q !== want_count_q) begin
|
||||
$error("%m: Checks cyc=%0d, count_q (%0d) !== want_count_q (%0d)",
|
||||
cyc, count_q, want_count_q);
|
||||
$stop; // don't finish to fail the test.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin : update_golden_counts
|
||||
want_count_d = want_count_q;
|
||||
if (case_sel == 2'b10)
|
||||
want_count_d++;
|
||||
else if (case_sel == 2'b01)
|
||||
want_count_d--;
|
||||
end
|
||||
|
||||
// Make sure the ++ and -- operators are handled correctly in case stmts.
|
||||
// Test for https://github.com/verilator/verilator/issues/3346
|
||||
always_comb begin : update_counts
|
||||
count_d = count_q;
|
||||
case (case_sel)
|
||||
2'b10: count_d++;
|
||||
2'b01: count_d--;
|
||||
default : ;
|
||||
endcase // case (case_sel)
|
||||
end
|
||||
|
||||
// -------------------------
|
||||
// FSM with ++/--
|
||||
// A more elaborate case statement, with if-else, for loops, etc
|
||||
// to confirm that ++/-- is handled by V3LinkInc.cpp
|
||||
logic [3:0] state_d, state_q;
|
||||
initial state_q = '0;
|
||||
logic [3:0] state_counter_d, state_counter_q;
|
||||
always_ff @(posedge clk) begin
|
||||
state_q <= state_d;
|
||||
state_counter_q <= state_counter_d;
|
||||
end
|
||||
|
||||
|
||||
always_comb begin : update_state
|
||||
state_d = state_q;
|
||||
state_counter_d = state_counter_q;
|
||||
case (state_q)
|
||||
|
||||
// state 0, no begin/end, goes to state 1
|
||||
4'd0: state_d = 4'd1;
|
||||
|
||||
// state 1, clears state_counter_d, goes to state 2
|
||||
4'd1: begin
|
||||
state_d = 4'd2;
|
||||
state_counter_d = '0;
|
||||
end
|
||||
|
||||
// state 2, wait until state_counter_d increments to 4.
|
||||
4'd2: begin
|
||||
state_counter_d++;
|
||||
if (state_counter_q == 4) begin
|
||||
state_d = 4'd3;
|
||||
end
|
||||
end
|
||||
// state 3, decrements state_counter_d from 5 to 0.
|
||||
4'd3: begin
|
||||
state_counter_d--;
|
||||
if (state_counter_q == 1) begin
|
||||
state_d = 4'd4;
|
||||
end
|
||||
end
|
||||
|
||||
4'd4: begin
|
||||
// add 4 with for-loop and ++.
|
||||
for (int unsigned i = 0; i < 4; i++) begin
|
||||
state_counter_d++;
|
||||
end
|
||||
|
||||
if (state_counter_q == 12) begin
|
||||
state_counter_d = '0;
|
||||
state_d = 4'd5;
|
||||
end
|
||||
end
|
||||
|
||||
4'd5: begin
|
||||
// add 8 with a while loop and go to state 6.
|
||||
while (state_counter_d <= 7) begin
|
||||
state_counter_d++;
|
||||
end
|
||||
|
||||
if (state_counter_d == 8) begin
|
||||
state_d = 4'd15;
|
||||
end
|
||||
end
|
||||
4'd15 : begin
|
||||
// success, stay here.
|
||||
state_counter_d = 4'd7; // pick and hold some success number.
|
||||
end
|
||||
default: ;
|
||||
endcase // case (state_q)
|
||||
|
||||
end // block: state
|
||||
|
||||
always @(posedge clk) begin : simple_state_check
|
||||
//$display("%m: debug, cyc=%0d, state_q=%0d, state_counter_q=%0d",
|
||||
// cyc, state_q, state_counter_q);
|
||||
if (cyc >= 90) begin
|
||||
// the above FSM should finish before 90 cycles.
|
||||
// Make sure we made it to state 4'd15.
|
||||
if (state_q !== 4'd15 ||
|
||||
state_counter_q !== 4'd7) begin
|
||||
$error("%m: EOT checks, cyc=%0d, state_q=%0d (want 15), state_counter_q=%0d (want 7)",
|
||||
cyc, state_q, state_counter_q);
|
||||
$stop; // don't finish to fail the test.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule : t
|
||||
@@ -15,6 +15,7 @@ module t;
|
||||
|
||||
typedef logic [3:0] mc_t;
|
||||
typedef mc_t tocast_t;
|
||||
typedef logic [2:0] [7:0] two_dee_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [15:0] data;
|
||||
@@ -42,6 +43,8 @@ module t;
|
||||
logic [15:0] allones = 16'hffff;
|
||||
parameter FOUR = 4;
|
||||
|
||||
localparam two_dee_t two_dee = two_dee_t'(32'habcdef);
|
||||
|
||||
// bug925
|
||||
localparam [6:0] RESULT = 7'((6*9+92)%96);
|
||||
|
||||
@@ -63,6 +66,9 @@ module t;
|
||||
logic [32:0] b33 = {32'(0), one};
|
||||
logic [31:0] b32 = {31'(0), one};
|
||||
|
||||
logic [31:0] thirty_two_bits;
|
||||
two_dee_t two_dee_sig;
|
||||
|
||||
initial begin
|
||||
if (logic8bit != 8'h12) $stop;
|
||||
if (4'shf > 4'sh0) $stop;
|
||||
@@ -107,6 +113,17 @@ module t;
|
||||
if (b33 != 33'b1) $stop;
|
||||
if (b32 != 32'b1) $stop;
|
||||
|
||||
if (two_dee[0] != 8'hef) $stop;
|
||||
if (two_dee[1] != 8'hcd) $stop;
|
||||
if (two_dee[2] != 8'hab) $stop;
|
||||
|
||||
thirty_two_bits = 32'h123456;
|
||||
two_dee_sig = two_dee_t'(thirty_two_bits);
|
||||
|
||||
if (two_dee_sig[0] != 8'h56) $stop;
|
||||
if (two_dee_sig[1] != 8'h34) $stop;
|
||||
if (two_dee_sig[2] != 8'h12) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , name:"object_name"}'
|
||||
''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , name:"object_name"}'
|
||||
''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , cwide:'{'h0, 'h0} , name:"object_name"}'
|
||||
''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , cwide:'{'h0, 'h0} , name:"object_name"}'
|
||||
DEBUG: object_name (@0) message
|
||||
*-* All Finished *-*
|
||||
|
||||
@@ -15,6 +15,7 @@ class Cls;
|
||||
bit b;
|
||||
int i;
|
||||
bit [15:0] carray4 [4];
|
||||
bit [64:0] cwide[2];
|
||||
string name;
|
||||
task debug();
|
||||
$display("DEBUG: %s (@%0t) %s", this.name, $realtime, "message");
|
||||
|
||||
@@ -17,9 +17,9 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="d74" loc=".*?" name="clk0" .*dir="input" .*vartype="logic" origName="clk0" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="d75" loc=".*?" name="clk1" .*dir="input" .*vartype="logic" origName="clk1" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="d76" loc=".*?" name="clk2" .*dir="input" .*vartype="logic" origName="clk2" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,74,.*?" name="clk0" .*dir="input" .*vartype="logic" origName="clk0" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,75,.*?" name="clk1" .*dir="input" .*vartype="logic" origName="clk1" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,76,.*?" name="clk2" .*dir="input" .*vartype="logic" origName="clk2" clocker="true" public="true"\/\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -18,10 +18,10 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="e78" loc=".*?" name="clk0" .*dir="input" .*vartype="logic" origName="clk0" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e79" loc=".*?" name="clk1" .*dir="input" .*vartype="logic" origName="clk1" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e80" loc=".*?" name="clk2" .*dir="input" .*vartype="logic" origName="clk2" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e82" loc=".*?" name="data_in" .*dir="input" .*vartype="logic" origName="data_in" clocker="false" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,78,.*?" name="clk0" .*dir="input" .*vartype="logic" origName="clk0" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,79,.*?" name="clk1" .*dir="input" .*vartype="logic" origName="clk1" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,80,.*?" name="clk2" .*dir="input" .*vartype="logic" origName="clk2" clocker="true" public="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,82,.*?" name="data_in" .*dir="input" .*vartype="logic" origName="data_in" clocker="false" public="true"\/\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -17,7 +17,7 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="e44" loc=".*?" name="t.f0.clock_gate.clken_latched" dtype_id="1" vartype="logic" origName="clken_latched" clock_enable="true" latched="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,44,.*?" name="t.f0.clock_gate.clken_latched" dtype_id="1" vartype="logic" origName="clken_latched" clock_enable="true" latched="true"\/\>/i);
|
||||
file_grep($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 4);
|
||||
}
|
||||
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 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.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(vltmt => 1); # Note issue shows up with --threads
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ['--compiler clang --threads 2 -Wno-UNOPTTHREADS'],
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,83 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2022 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t(/*AUTOARG*/
|
||||
// Outputs
|
||||
out,
|
||||
// Inputs
|
||||
clk_0, clk_1, clk_2, clk_3, clk_4, clk_5, clk_6, clk_7, clk_8, clk_9, clk_10,
|
||||
clk_11, clk_12, clk_13, clk_14, clk_15, clk_16, clk_17, clk_18, clk_19,
|
||||
rstn_0, rstn_1, rstn_2, rstn_3, rstn_4, rstn_5, rstn_6, rstn_7, rstn_8,
|
||||
rstn_9, rstn_10, rstn_11, rstn_12, rstn_13, rstn_14, rstn_15, rstn_16,
|
||||
rstn_17, rstn_18, rstn_19
|
||||
);
|
||||
|
||||
input clk_0;
|
||||
input clk_1;
|
||||
input clk_2;
|
||||
input clk_3;
|
||||
input clk_4;
|
||||
input clk_5;
|
||||
input clk_6;
|
||||
input clk_7;
|
||||
input clk_8;
|
||||
input clk_9;
|
||||
input clk_10;
|
||||
input clk_11;
|
||||
input clk_12;
|
||||
input clk_13;
|
||||
input clk_14;
|
||||
input clk_15;
|
||||
input clk_16;
|
||||
input clk_17;
|
||||
input clk_18;
|
||||
input clk_19;
|
||||
input rstn_0;
|
||||
input rstn_1;
|
||||
input rstn_2;
|
||||
input rstn_3;
|
||||
input rstn_4;
|
||||
input rstn_5;
|
||||
input rstn_6;
|
||||
input rstn_7;
|
||||
input rstn_8;
|
||||
input rstn_9;
|
||||
input rstn_10;
|
||||
input rstn_11;
|
||||
input rstn_12;
|
||||
input rstn_13;
|
||||
input rstn_14;
|
||||
input rstn_15;
|
||||
input rstn_16;
|
||||
input rstn_17;
|
||||
input rstn_18;
|
||||
input rstn_19;
|
||||
|
||||
// verilator lint_off MULTIDRIVEN
|
||||
output reg out [0:29-1];
|
||||
|
||||
always_ff @(posedge clk_0, negedge rstn_0) if ((rstn_0 == 0)) out[0] <= 0;
|
||||
always_ff @(posedge clk_1, negedge rstn_1) if ((rstn_1 == 0)) out[1] <= 0;
|
||||
always_ff @(posedge clk_2, negedge rstn_2) if ((rstn_2 == 0)) out[2] <= 0;
|
||||
always_ff @(posedge clk_3, negedge rstn_3) if ((rstn_3 == 0)) out[3] <= 0;
|
||||
always_ff @(posedge clk_4, negedge rstn_4) if ((rstn_4 == 0)) out[4] <= 0;
|
||||
always_ff @(posedge clk_5, negedge rstn_5) if ((rstn_5 == 0)) out[5] <= 0;
|
||||
always_ff @(posedge clk_6, negedge rstn_6) if ((rstn_6 == 0)) out[6] <= 0;
|
||||
always_ff @(posedge clk_7, negedge rstn_7) if ((rstn_7 == 0)) out[7] <= 0;
|
||||
always_ff @(posedge clk_8, negedge rstn_8) if ((rstn_8 == 0)) out[8] <= 0;
|
||||
always_ff @(posedge clk_9, negedge rstn_9) if ((rstn_9 == 0)) out[9] <= 0;
|
||||
always_ff @(posedge clk_10, negedge rstn_10) if ((rstn_10 == 0)) out[10] <= 0;
|
||||
always_ff @(posedge clk_11, negedge rstn_11) if ((rstn_11 == 0)) out[11] <= 0;
|
||||
always_ff @(posedge clk_12, negedge rstn_12) if ((rstn_12 == 0)) out[12] <= 0;
|
||||
always_ff @(posedge clk_13, negedge rstn_13) if ((rstn_13 == 0)) out[13] <= 0;
|
||||
always_ff @(posedge clk_14, negedge rstn_14) if ((rstn_14 == 0)) out[14] <= 0;
|
||||
always_ff @(posedge clk_15, negedge rstn_15) if ((rstn_15 == 0)) out[15] <= 0;
|
||||
always_ff @(posedge clk_16, negedge rstn_16) if ((rstn_16 == 0)) out[16] <= 0;
|
||||
always_ff @(posedge clk_17, negedge rstn_17) if ((rstn_17 == 0)) out[17] <= 0;
|
||||
always_ff @(posedge clk_18, negedge rstn_18) if ((rstn_18 == 0)) out[18] <= 0;
|
||||
always_ff @(posedge clk_19, negedge rstn_19) if ((rstn_19 == 0)) out[19] <= 0;
|
||||
|
||||
endmodule
|
||||
@@ -18,10 +18,10 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="d56" loc=".*?" name="formatted" dtype_id="\d+" dir="input" vartype="string" origName="formatted" sformat="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="d77" loc=".*?" name="t.sub.in" dtype_id="\d+" vartype="int" origName="in" public="true" public_flat_rd="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="d78" loc=".*?" name="t.sub.fr_a" dtype_id="\d+" vartype="int" origName="fr_a" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="d79" loc=".*?" name="t.sub.fr_b" dtype_id="\d+" vartype="int" origName="fr_b" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,56,.*?" name="formatted" dtype_id="\d+" dir="input" vartype="string" origName="formatted" sformat="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,77,.*?" name="t.sub.in" dtype_id="\d+" vartype="int" origName="in" public="true" public_flat_rd="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,78,.*?" name="t.sub.fr_a" dtype_id="\d+" vartype="int" origName="fr_a" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="d,79,.*?" name="t.sub.fr_b" dtype_id="\d+" vartype="int" origName="fr_b" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -20,10 +20,10 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="e58" loc=".*?" name="formatted" dtype_id="\d+" dir="input" vartype="string" origName="formatted" sformat="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e81" loc=".*?" name="t.sub.in" dtype_id="\d+" vartype="int" origName="in" public="true" public_flat_rd="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e82" loc=".*?" name="t.sub.fr_a" dtype_id="\d+" vartype="int" origName="fr_a" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e83" loc=".*?" name="t.sub.fr_b" dtype_id="\d+" vartype="int" origName="fr_b" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,58,.*?" name="formatted" dtype_id="\d+" dir="input" vartype="string" origName="formatted" sformat="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,81,.*?" name="t.sub.in" dtype_id="\d+" vartype="int" origName="in" public="true" public_flat_rd="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,82,.*?" name="t.sub.fr_a" dtype_id="\d+" vartype="int" origName="fr_a" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,83,.*?" name="t.sub.fr_b" dtype_id="\d+" vartype="int" origName="fr_b" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -8,6 +8,9 @@ package p3;
|
||||
typedef enum logic [2:0] {
|
||||
ZERO = 3'b0,
|
||||
ONE = 3'b1 } e3_t /*verilator public*/;
|
||||
typedef enum logic [2:0] {
|
||||
TWO = 3'd2,
|
||||
XES = 3'b?1? } has_x_t /*verilator public*/;
|
||||
endpackage
|
||||
|
||||
package p62;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%Error: t/t_force_bad_rw.v:14:20: Unsupported: Signals used via read-write reference cannot be forced
|
||||
: ... In instance t.unnamedblk1.index
|
||||
: ... In instance t.unnamedblk1.unnamedblk1.index
|
||||
14 | foreach (ass[index]) begin
|
||||
| ^~~~~
|
||||
%Error: Exiting due to
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 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.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,28 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2022 by Pawel Jewstafjew ([email protected]).
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Iface (input bit [31:0] regs [1]);
|
||||
initial begin
|
||||
string instance_path = $sformatf("%m");
|
||||
$display("Iface path %s\n", instance_path);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
bit [0:0] ppp;
|
||||
always_comb begin
|
||||
// Ok:
|
||||
//for (int index = 1 ; index < 2 ; ++index) begin
|
||||
foreach (regs[index]) begin
|
||||
ppp[index] = 1;
|
||||
end
|
||||
end
|
||||
|
||||
endinterface
|
||||
|
||||
module top (input bit [31:0] regs [1]);
|
||||
Iface t1(.regs(regs));
|
||||
endmodule
|
||||
@@ -18,10 +18,10 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<module fl="d84" loc=".*?" name="ma" origName="ma" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="d99" loc=".*?" name="mb" origName="mb" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="d127" loc=".*?" name="mc" origName="mc" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="d127" loc=".*?" name="mc__PB1" origName="mc" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="d,84,.*?" name="ma" origName="ma" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="d,99,.*?" name="mb" origName="mb" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="d,127,.*?" name="mc" origName="mc" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="d,127,.*?" name="mc__PB1" origName="mc" public="true"\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -18,10 +18,10 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<module fl="e84" loc=".*?" name="ma" origName="ma" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e99" loc=".*?" name="mb" origName="mb" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e127" loc=".*?" name="mc" origName="mc" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e127" loc=".*?" name="mc__PB1" origName="mc" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,84,.*?" name="ma" origName="ma" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,99,.*?" name="mb" origName="mb" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,127,.*?" name="mc" origName="mc" public="true"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,127,.*?" name="mc__PB1" origName="mc" public="true"\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -18,8 +18,8 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<instance fl="d87" loc=".*?" name="t.ma0.mb0" defName="mb" origName="mb0"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="d99" loc=".*?" name="mb" origName="mb"\>/i);
|
||||
file_grep("$out_filename", qr/\<instance loc="d,87,.*?" name="t.ma0.mb0" defName="mb" origName="mb0"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="d,99,.*?" name="mb" origName="mb"\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -18,8 +18,8 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<instance fl="e87" loc=".*?" name="t.ma0.mb0" defName="mb" origName="mb0"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e99" loc=".*?" name="mb" origName="mb"\>/i);
|
||||
file_grep("$out_filename", qr/\<instance loc="e,87,.*?" name="t.ma0.mb0" defName="mb" origName="mb0"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,99,.*?" name="mb" origName="mb"\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -32,6 +32,13 @@ module t (clk);
|
||||
$write("Hello in publicTop\n");
|
||||
endtask
|
||||
|
||||
task test_task(input [19:0] in [2], output [19:0] out [2]);
|
||||
// Issue 3316
|
||||
// verilator public
|
||||
out[0] = in[1];
|
||||
out[1] = in[0];
|
||||
endtask
|
||||
|
||||
endmodule
|
||||
|
||||
module tpub (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1913
-1917
File diff suppressed because it is too large
Load Diff
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--Os -x-assign 0"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,55 @@
|
||||
module t
|
||||
(/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
always @ (posedge clk) begin : main
|
||||
cyc <= cyc + 1;
|
||||
|
||||
if (cyc > 100) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
logic [3:0] count_d;
|
||||
logic [3:0] count_q = '0;
|
||||
|
||||
logic [3:0] want_count_d;
|
||||
logic [3:0] want_count_q = '0;
|
||||
|
||||
always_ff @(posedge clk) begin : flops
|
||||
count_q <= count_d;
|
||||
want_count_q <= want_count_d;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin : simple_check
|
||||
if (cyc > 0) begin
|
||||
if (count_q !== want_count_q) begin
|
||||
$error("%m: cyc=%0d, count_q (%0d) !== want_count_q (%0d)",
|
||||
cyc, count_q, want_count_q);
|
||||
$stop; // don't finish to fail the test.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin : update_golden_counts
|
||||
want_count_d = want_count_q;
|
||||
want_count_d += 1'b1;
|
||||
end
|
||||
|
||||
// make sure an implicit void cast on n++ works as expected.
|
||||
always_comb begin : update_counts
|
||||
count_d = count_q;
|
||||
count_d++;
|
||||
end
|
||||
|
||||
|
||||
endmodule
|
||||
@@ -18,12 +18,12 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<module fl="e56" loc=".*?" name="l1" origName="l1"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e62" loc=".*?" name="l2" origName="l2"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e69" loc=".*?" name="l3" origName="l3"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e76" loc=".*?" name="l4" origName="l4"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e83" loc=".*?" name="l5__P2" origName="l5"\>/i);
|
||||
file_grep("$out_filename", qr/\<module fl="e83" loc=".*?" name="l5__P1" origName="l5"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,56,.*?" name="l1" origName="l1"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,62,.*?" name="l2" origName="l2"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,69,.*?" name="l3" origName="l3"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,76,.*?" name="l4" origName="l4"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,83,.*?" name="l5__P2" origName="l5"\>/i);
|
||||
file_grep("$out_filename", qr/\<module loc="e,83,.*?" name="l5__P1" origName="l5"\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -18,9 +18,9 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="e70" loc=".*?" name="t.u.u0.u0.z1" dtype_id="\d+" vartype="logic" origName="z1"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e70" loc=".*?" name="t.u.u0.u1.z1" dtype_id="\d+" vartype="logic" origName="z1"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e70" loc=".*?" name="t.u.u1.u0.z0" dtype_id="\d+" vartype="logic" origName="z0"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,70,.*?" name="t.u.u0.u0.z1" dtype_id="\d+" vartype="logic" origName="z1"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,70,.*?" name="t.u.u0.u1.z1" dtype_id="\d+" vartype="logic" origName="z1"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,70,.*?" name="t.u.u1.u0.z0" dtype_id="\d+" vartype="logic" origName="z0"\/\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
@@ -19,9 +19,9 @@ compile(
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
file_grep("$out_filename", qr/\<var fl="e70" loc=".*?" name="u.u0.u0.z0" dtype_id="\d+" vartype="logic" origName="z0" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e85" loc=".*?" name="u.u0.u0.u0.u0.z1" dtype_id="\d+" vartype="logic" origName="z1" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var fl="e83" loc=".*?" name="u.u0.u1.u0.u0.z" dtype_id="\d+" vartype="logic" origName="z" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,70,.*?" name="u.u0.u0.z0" dtype_id="\d+" vartype="logic" origName="z0" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,85,.*?" name="u.u0.u0.u0.u0.z1" dtype_id="\d+" vartype="logic" origName="z1" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
file_grep("$out_filename", qr/\<var loc="e,83,.*?" name="u.u0.u1.u0.u0.z" dtype_id="\d+" vartype="logic" origName="z" public="true" public_flat_rd="true" public_flat_rw="true"\/\>/i);
|
||||
}
|
||||
|
||||
execute(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
$date
|
||||
Thu Apr 1 14:51:24 2021
|
||||
Tue Feb 22 23:55:19 2022
|
||||
|
||||
$end
|
||||
$version
|
||||
@@ -11,220 +11,212 @@ $end
|
||||
$scope module top $end
|
||||
$scope module t $end
|
||||
$var wire 1 ! clk $end
|
||||
$var integer 32 " cyc $end
|
||||
$scope interface intf_1 $end
|
||||
$var integer 32 " cyc [31:0] $end
|
||||
$scope module a $end
|
||||
$scope module ac1 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module s1 $end
|
||||
$upscope $end
|
||||
$scope module ac2 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac3 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 ) value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 * val100 [31:0] $end
|
||||
$var logic 32 + val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module as3 $end
|
||||
$scope interface intf_for_struct $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 ) value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$var logic 32 * val100 [31:0] $end
|
||||
$var logic 32 + val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_in_sub_all $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 ) value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 * val100 [31:0] $end
|
||||
$var logic 32 + val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_one $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_two $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module abcdefghijklmnopqrstuvwxyz $end
|
||||
$scope module ac1 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac2 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac3 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 , value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 - val100 [31:0] $end
|
||||
$var logic 32 . val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module as3 $end
|
||||
$scope interface intf_for_struct $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 , value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 - val100 [31:0] $end
|
||||
$var logic 32 . val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_in_sub_all $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 , value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 - val100 [31:0] $end
|
||||
$var logic 32 . val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_one $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_two $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module c1 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module a $end
|
||||
$scope interface intf_one $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac1 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module abcdefghijklmnopqrstuvwxyz $end
|
||||
$scope interface intf_two $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac2 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 # value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 $ val100 $end
|
||||
$var logic 32 % val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope interface intf_2 $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module s2 $end
|
||||
$scope interface intf_for_struct $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module c2 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module a $end
|
||||
$scope interface intf_two $end
|
||||
$scope interface intf_1 $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac2 $end
|
||||
$scope interface intf_for_check $end
|
||||
$scope interface intf_2 $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module abcdefghijklmnopqrstuvwxyz $end
|
||||
$scope interface intf_one $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac1 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 & value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 ' val100 $end
|
||||
$var logic 32 ( val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module a $end
|
||||
$scope interface intf_in_sub_all $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 ) value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 * val100 $end
|
||||
$var logic 32 + val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module as3 $end
|
||||
$scope module s1 $end
|
||||
$scope interface intf_for_struct $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 ) value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 # value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 * val100 $end
|
||||
$var logic 32 + val200 $end
|
||||
$var logic 32 $ val100 [31:0] $end
|
||||
$var logic 32 % val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac3 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 ) value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 * val100 $end
|
||||
$var logic 32 + val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module abcdefghijklmnopqrstuvwxyz $end
|
||||
$scope interface intf_in_sub_all $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 , value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 - val100 $end
|
||||
$var logic 32 . val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module as3 $end
|
||||
$scope module s2 $end
|
||||
$scope interface intf_for_struct $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 , value $end
|
||||
$var wire 32 " cyc [31:0] $end
|
||||
$var integer 32 & value [31:0] $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 - val100 $end
|
||||
$var logic 32 . val200 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope module ac3 $end
|
||||
$scope interface intf_for_check $end
|
||||
$var wire 1 ! clk $end
|
||||
$var wire 32 " cyc $end
|
||||
$var integer 32 , value $end
|
||||
$scope struct the_struct $end
|
||||
$var logic 32 - val100 $end
|
||||
$var logic 32 . val200 $end
|
||||
$upscope $end
|
||||
$var logic 32 ' val100 [31:0] $end
|
||||
$var logic 32 ( val200 [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%Warning-UNUSED: t/t_lint_once_bad.v:19:14: Signal is not driven, nor used: 'unus1'
|
||||
: ... In instance t.sub3
|
||||
: ... In instance t.sub1
|
||||
19 | reg [A:0] unus1; reg [A:0] unus2;
|
||||
| ^~~~~
|
||||
... For warning description see https://verilator.org/warn/UNUSED?v=latest
|
||||
... Use "/* verilator lint_off UNUSED */" and lint_on around source to disable this message.
|
||||
%Warning-UNUSED: t/t_lint_once_bad.v:19:34: Signal is not driven, nor used: 'unus2'
|
||||
: ... In instance t.sub3
|
||||
: ... In instance t.sub1
|
||||
19 | reg [A:0] unus1; reg [A:0] unus2;
|
||||
| ^~~~~
|
||||
%Error: Exiting due to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%Warning-WIDTH: t/t_lint_repeat_bad.v:18:17: Operator ASSIGNW expects 1 bits on the Assign RHS, but Assign RHS's VARREF 'a' generates 2 bits.
|
||||
: ... In instance t.sub2
|
||||
: ... In instance t.sub3
|
||||
18 | wire [0:0] b = a;
|
||||
| ^
|
||||
... For warning description see https://verilator.org/warn/WIDTH?v=latest
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user