mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 00:23:35 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0478dbdd1f | ||
|
|
38b0e8a777 | ||
|
|
fe9f1add6f | ||
|
|
ae9179f412 | ||
|
|
4c35a76cdb | ||
|
|
8ca3428fb6 | ||
|
|
7369500bb7 | ||
|
|
45702e319b | ||
|
|
813468bfe6 | ||
|
|
78192308e1 | ||
|
|
cb72390b57 | ||
|
|
71b2eeef67 | ||
|
|
57c8590b21 | ||
|
|
f8ad0a0923 | ||
|
|
7c443ab108 | ||
|
|
0d645757e7 | ||
|
|
dd37c2ea86 | ||
|
|
d3032bfc21 | ||
|
|
bd6ecdfa6f | ||
|
|
3558e70722 | ||
|
|
a579e9273b | ||
|
|
21369bec95 | ||
|
|
d119d10569 | ||
|
|
38988c005c | ||
|
|
8cc4b588b2 | ||
|
|
12607abb33 | ||
|
|
562f17ea4b | ||
|
|
0aea9b7709 | ||
|
|
c0afe96b80 | ||
|
|
5c919cd0de | ||
|
|
f555c93c11 | ||
|
|
cbb7cd16d0 | ||
|
|
4d074b5414 | ||
|
|
dfd22b2b07 | ||
|
|
6edaef9598 | ||
|
|
f63e946f04 | ||
|
|
add5cc8b56 | ||
|
|
2a4e830bee | ||
|
|
eb87232472 | ||
|
|
52c3031a82 | ||
|
|
df471e87eb | ||
|
|
ec6fae5d1a | ||
|
|
cb422a9a02 | ||
|
|
f91bac7b31 | ||
|
|
d542921ff7 | ||
|
|
ad693d67b2 | ||
|
|
350932e9c0 | ||
|
|
4e98d96755 | ||
|
|
c17a9eb5f1 | ||
|
|
252ed4c1ae | ||
|
|
f9fd634fae | ||
|
|
c17c81254e | ||
|
|
5ead61dc7b | ||
|
|
e403db5877 | ||
|
|
32874fa848 | ||
|
|
b90f383cfe | ||
|
|
eb65984368 | ||
|
|
10e0d34140 | ||
|
|
f4b00d3c64 | ||
|
|
cc0b780412 | ||
|
|
fbbbe4ec87 | ||
|
|
1372d62186 | ||
|
|
a69936de74 | ||
|
|
f5dfa1e9c3 | ||
|
|
5fb21c2159 |
@@ -2,6 +2,31 @@ Revision history for Verilator
|
||||
|
||||
The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
* Verilator 3.916 2017-11-25
|
||||
|
||||
*** Support self-recursive modules, bug659. [Sean Moore, et al]
|
||||
|
||||
*** Support $error/$warning in elaboration time blocks.
|
||||
|
||||
*** Support $size/$bits/etc on type references.
|
||||
|
||||
*** Add error when driving input-only modport, bug1110. [Trevor Elbourne]
|
||||
|
||||
*** Add BSSPACE and COLONPLUS lint warnings.
|
||||
|
||||
**** Detect MSB overflow when under VL_DEBUG, bug1238. [Junyi Xi]
|
||||
|
||||
**** Add data types to --xml. [Rui Terra]
|
||||
|
||||
**** Fix partial slicing with pattern assignments, bug991. [Johan Bjork]
|
||||
|
||||
**** Fix false unused warning on interfaces, bug1241. [Laurens van Dam]
|
||||
|
||||
**** Fix error on "unique case" with no cases.
|
||||
|
||||
**** Fix MacOS portability, bug1232. [Jeff Bush]
|
||||
|
||||
|
||||
* Verilator 3.914 2017-10-14
|
||||
|
||||
** Added new examples/ directory with appropriate examples. This replaces the
|
||||
|
||||
+13
-1
@@ -92,6 +92,7 @@ datarootdir = @datarootdir@
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
CFG_WITH_THREADED = @CFG_WITH_THREADED@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
@@ -213,7 +214,7 @@ test_regress: all_nomsg
|
||||
|
||||
examples: all_nomsg
|
||||
for p in examples/* ; do \
|
||||
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` ; \
|
||||
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
|
||||
done
|
||||
|
||||
info: $(INFOS)
|
||||
@@ -429,6 +430,14 @@ cppcheck: $(CPPCHECK_DEP)
|
||||
%.cppcheck: %.cpp
|
||||
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $<
|
||||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
scan-build $(MAKE) -k verilator_coverage_bin_dbg verilator_bin_dbg
|
||||
|
||||
analyzer-include:
|
||||
-rm -rf examples/*/obj*
|
||||
scan-build $(MAKE) -k examples
|
||||
|
||||
ftp: info
|
||||
|
||||
install-msg:
|
||||
@@ -505,6 +514,9 @@ dist-file-list:
|
||||
@echo $(wildcard $(DISTFILES))
|
||||
@echo "end-dist-file-list:"; # Scripts look for this
|
||||
|
||||
print-cfg-with-threaded:
|
||||
@echo $(CFG_WITH_THREADED)
|
||||
|
||||
######################################################################
|
||||
# Distributions
|
||||
|
||||
|
||||
+131
-89
@@ -547,7 +547,7 @@ Coverage analysis adds statements at each code flow change point, which are
|
||||
the branches of IF and CASE statements, a super-set of normal Verilog Line
|
||||
Coverage. At each such branch a unique counter is incremented. At the end
|
||||
of a test, the counters along with the filename and line number
|
||||
corresponding to each counter are written into logs/coverage.pl.
|
||||
corresponding to each counter are written into logs/coverage.dat.
|
||||
|
||||
Verilator automatically disables coverage of branches that have a $stop in
|
||||
them, as it is assumed $stop branches contain an error check that should
|
||||
@@ -1251,11 +1251,11 @@ directives in the source, i.e. the warning will still not be printed.
|
||||
=item -Wno-lint
|
||||
|
||||
Disable all lint related warning messages, and all style warnings. This is
|
||||
equivalent to "-Wno-ALWCOMBORDER -Wno-CASEINCOMPLETE -Wno-CASEOVERLAP
|
||||
-Wno-CASEX -Wno-CASEWITHX -Wno-CMPCONST -Wno-ENDLABEL -Wno-IMPLICIT
|
||||
-Wno-LITENDIAN -Wno-PINCONNECTEMPTY -Wno-PINMISSING -Wno-SYNCASYNCNET
|
||||
-Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED -Wno-WIDTH" plus the list shown for
|
||||
Wno-style.
|
||||
equivalent to "-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE
|
||||
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
|
||||
-Wno-ENDLABEL -Wno-IMPLICIT -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
|
||||
-Wno-WIDTH" plus the list shown for Wno-style.
|
||||
|
||||
It is strongly recommended you cleanup your code rather than using this
|
||||
option, it is only intended to be use when running test-cases of code
|
||||
@@ -1284,10 +1284,10 @@ Enables the specified warning message.
|
||||
|
||||
Enable all lint related warning messages (note by default they are already
|
||||
enabled), but do not affect style messages. This is equivalent to
|
||||
"-Wwarn-ALWCOMBORDER -Wwarn-CASEINCOMPLETE -Wwarn-CASEOVERLAP -Wwarn-CASEX
|
||||
-Wwarn-CASEWITHX -Wwarn-CMPCONST -Wwarn-ENDLABEL -Wwarn-IMPLICIT
|
||||
-Wwarn-LITENDIAN -Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED
|
||||
-Wwarn-WIDTH".
|
||||
"-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
|
||||
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASEWITHX -Wwarn-CMPCONST
|
||||
-Wwarn-COLONPLUS -Wwarn-ENDLABEL -Wwarn-IMPLICIT -Wwarn-LITENDIAN
|
||||
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH".
|
||||
|
||||
=item -Wwarn-style
|
||||
|
||||
@@ -1734,7 +1734,7 @@ Verilator RPM as the hardcoded value should be correct.
|
||||
=head1 CONNECTING TO C++
|
||||
|
||||
Verilator creates a .h and .cpp file for the top level module and all
|
||||
modules under it. See the test_c directory in the kit for an example.
|
||||
modules under it. See the examples directory in the kit for examples.
|
||||
|
||||
After the modules are completed, there will be a I<module>.mk file that may
|
||||
be used with Make to produce a I<module>__ALL.a file with all required
|
||||
@@ -2349,7 +2349,7 @@ Verilator configuration commands.
|
||||
|
||||
Switch back to processing Verilog code after a `systemc_... mode switch.
|
||||
The Verilog code returns to the last language mode specified with
|
||||
`begin_keywords, or SystemVerilog if none were specified.
|
||||
`begin_keywords, or SystemVerilog if none was specified.
|
||||
|
||||
=item /*verilator clock_enable*/
|
||||
|
||||
@@ -2466,6 +2466,13 @@ lint_restore*/. For example:
|
||||
If SOME_WARNING was on before the lint_off, it will now be restored to on,
|
||||
and if it was off before the lint_off it will remain off.
|
||||
|
||||
=item /*verilator no_inline_module*/
|
||||
|
||||
Specifies the module the comment appears in should not be inlined into any
|
||||
modules that use this module. This is useful especially at the top level
|
||||
module to reduce the size of the interface class, to aid compile time at a
|
||||
small performance loss.
|
||||
|
||||
=item /*verilator no_inline_task*/
|
||||
|
||||
Used in a function or task variable definition section to specify the
|
||||
@@ -3074,6 +3081,17 @@ generally unrolls small loops. You may want to try increasing
|
||||
--unroll-count (and occasionally --unroll-stmts) which will raise the small
|
||||
loop bar to avoid this error.
|
||||
|
||||
=item BSSPACE
|
||||
|
||||
Warns that a backslash is followed by a space then a newline. Likely the
|
||||
intent was to have a backslash directly followed by a newline (e.g. when
|
||||
making a `define) and there's accidentally whitespace at the end of the
|
||||
line. If the space is not accidental, suggest removing the backslash in
|
||||
the code as it serves no function.
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
|
||||
=item CASEINCOMPLETE
|
||||
|
||||
Warns that inside a case statement there is a stimulus pattern for which
|
||||
@@ -3113,6 +3131,15 @@ instead intended is to use a casez with C<?>.
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
|
||||
=item COLONPLUS
|
||||
|
||||
Warns that a :+ is seen. Likely the intent was to use +: to select a range
|
||||
of bits. If the intent was a range that is explicitly positive, suggest
|
||||
adding a space, e.g. use ": +".
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
|
||||
=item CDCRSTLOGIC
|
||||
|
||||
With --cdc only, warns that asynchronous flop reset terms come from other
|
||||
@@ -3644,24 +3671,24 @@ program above:
|
||||
|
||||
=item Does it run under Windows?
|
||||
|
||||
Yes, using Cygwin. Verilated output should also compile under Microsoft
|
||||
Visual C++ Version 7 or newer, but this is not tested by the author.
|
||||
Yes, using Cygwin. Verilated output also compiles under Microsoft Visual
|
||||
C++ Version 7 or newer, but this is not tested every release.
|
||||
|
||||
=item Can you provide binaries?
|
||||
|
||||
Verilator is available as a RPM for SuSE, Fedora, and perhaps other
|
||||
systems; this is done by porters and may slightly lag the primary
|
||||
distribution. If there isn't a binary build for your distribution, how
|
||||
about you set one up? Please contact the authors for assistance.
|
||||
Verilator is available as a RPM for Debian/Ubuntu, SuSE, Fedora, and
|
||||
perhaps other systems; this is done by porters and may slightly lag the
|
||||
primary distribution. If there isn't a binary build for your distribution,
|
||||
how about you set one up? Please contact the authors for assistance.
|
||||
|
||||
Note people sometimes request binaries when they are having problems with
|
||||
their C++ compiler. Alas, binaries won't help this, as in the end a fully
|
||||
working C++ compiler is required to compile the output of Verilator.
|
||||
|
||||
=item How can it be faster than (name-the-simulator)?
|
||||
=item How can it be faster than (name-the-commercial-simulator)?
|
||||
|
||||
Generally, the implied part of the question is "... with all of their
|
||||
manpower they can put into it."
|
||||
Generally, the implied part is of the question is "... with all of the
|
||||
manpower they can put into developing it."
|
||||
|
||||
Most commercial simulators have to be Verilog compliant, meaning event
|
||||
driven. This prevents them from being able to reorder blocks and make
|
||||
@@ -3694,17 +3721,18 @@ source code available.
|
||||
As is standard with Open Source, contributions back to Verilator will be
|
||||
placed under the Verilator copyright and LGPL/Artistic license. Small test
|
||||
cases will be released into the public domain so they can be used anywhere,
|
||||
large tests under the LGPL/Artistic, unless requested otherwise.
|
||||
and large tests under the LGPL/Artistic, unless requested otherwise.
|
||||
|
||||
=item Why is Verilation so slow?
|
||||
|
||||
Verilator needs more memory than the resulting simulator will require, as
|
||||
Verilator creates internally all of the state of the resulting simulator in
|
||||
order to optimize it. If it takes more than a minute or so (and you're not
|
||||
using --debug since debug is disk bound), see if your machine is paging;
|
||||
most likely you need to run it on a machine with more memory. Verilator is
|
||||
a full 64-bit application and may use more than 4GB, but about 1GB is the
|
||||
maximum typically needed.
|
||||
Verilator creates internally all of the state of the resulting generated
|
||||
simulator in order to optimize it. If it takes more than a minute or so
|
||||
(and you're not using --debug since debug is disk bound), see if your
|
||||
machine is paging; most likely you need to run it on a machine with more
|
||||
memory. Verilator is a full 64-bit application and may use more than 4GB,
|
||||
but about 1GB is the maximum typically needed, and very large commercial
|
||||
designs have topped 16GB.
|
||||
|
||||
=item How do I generate waveforms (traces) in C++?
|
||||
|
||||
@@ -3714,7 +3742,7 @@ Add the --trace switch to Verilator, and in your top level C code, call
|
||||
Verilated::traceEverOn(true). Then create a VerilatedVcdC object, and
|
||||
in your main loop call "trace_object->dump(time)" every time step, and
|
||||
finally call "trace_object->close()". For an example, see below and the
|
||||
test_c/sim_main.cpp file of the distribution.
|
||||
examples/tracing_c/sim_main.cpp file of the distribution.
|
||||
|
||||
You also need to compile verilated_vcd_c.cpp and add it to your link,
|
||||
preferably by adding the dependencies in $(VK_GLOBAL_OBJS) to your
|
||||
@@ -3740,15 +3768,13 @@ trace file if you want all data to land in the same output file.
|
||||
tfp->close();
|
||||
}
|
||||
|
||||
Also see the examples/tracing_cc directory in the distribution.
|
||||
|
||||
=item How do I generate waveforms (traces) in SystemC?
|
||||
|
||||
Add the --trace switch to Verilator, and in your top level C sc_main code,
|
||||
include verilated_vcd_sc.h. Then call Verilated::traceEverOn(true). Then
|
||||
create a VerilatedVcdSc object as you would create a normal SystemC trace
|
||||
file. For an example, see the call to VerilatedVcdSc in the
|
||||
test_sc/sc_main.cpp file of the distribution, and below.
|
||||
examples/tracing_sc/sc_main.cpp file of the distribution, and below.
|
||||
|
||||
Alternatively you may use the C++ trace mechanism described in the previous
|
||||
question, however the timescale and timeprecision will not inherited from
|
||||
@@ -3776,8 +3802,6 @@ trace file if you want all data to land in the same output file.
|
||||
tfp->close();
|
||||
}
|
||||
|
||||
Also see the examples/tracing_cc directory in the distribution.
|
||||
|
||||
=item How do I view waveforms (traces)?
|
||||
|
||||
Verilator makes standard VCD (Value Change Dump) files. They are viewable
|
||||
@@ -3796,8 +3820,8 @@ want to trace (such as perhaps library cells). Finally, use the
|
||||
--trace-depth option to limit the depth of tracing, for example
|
||||
--trace-depth 1 to see only the top level signals.
|
||||
|
||||
Also be sure you write your trace files to a local disk, instead of to a
|
||||
network disk. Network disks are generally far slower.
|
||||
Also be sure you write your trace files to a local solid-state disk,
|
||||
instead of to a network disk. Network disks are generally far slower.
|
||||
|
||||
=item How do I do coverage analysis?
|
||||
|
||||
@@ -3812,14 +3836,14 @@ At the end of your test, call VerilatedCov::write passing the name of the
|
||||
coverage data file (typically "logs/coverage.dat").
|
||||
|
||||
Run each of your tests in different directories. Each test will create a
|
||||
logs/coverage.pl file.
|
||||
logs/coverage.dat file.
|
||||
|
||||
After running all of your tests, verilator_coverage is executed.
|
||||
Verilator_coverage reads the logs/coverage.pl file(s), and creates an
|
||||
Verilator_coverage reads the logs/coverage.dat file(s), and creates an
|
||||
annotated source code listing showing code coverage details.
|
||||
|
||||
For an example, after running 'make test' in the Verilator distribution,
|
||||
see the test_sc/logs directory. Grep for lines starting
|
||||
see the examples/tracing_c/logs directory. Grep for lines starting
|
||||
with '%' to see what lines Verilator believes need more coverage.
|
||||
|
||||
=item Where is the translate_off command? (How do I ignore a construct?)
|
||||
@@ -3833,6 +3857,8 @@ the "VERILATOR" define for you, so just wrap the code in an ifndef region:
|
||||
Something_Verilator_Dislikes;
|
||||
`endif
|
||||
|
||||
Most synthesis tools similarly define SYNTHESIS for you.
|
||||
|
||||
=item Why do I get "unexpected `do'" or "unexpected `bit'" errors?
|
||||
|
||||
Do, bit, ref, return, and other words are now SystemVerilog keywords. You
|
||||
@@ -3869,7 +3895,7 @@ simulator knows the current time. See the "CONNECTING TO C++" examples.
|
||||
You need to link your compiled Verilated code against the verilated.cpp
|
||||
file found in the include directory of the Verilator kit. This is one
|
||||
target in the $(VK_GLOBAL_OBJS) make variable, which should be part of your
|
||||
Makefile's link rule.
|
||||
Makefile's link rule. If you use --exe, this is done for you.
|
||||
|
||||
=item Is the PLI supported?
|
||||
|
||||
@@ -3891,13 +3917,20 @@ test_regress/t/t_extend_class files show an example of how to do this.
|
||||
|
||||
=item How do I get faster build times?
|
||||
|
||||
Between GCC 3.0 to 3.3, each compiled progressively slower, thus if you can
|
||||
use GCC 2.95, or GCC 3.4 you'll have faster builds. Two ways to cheat are
|
||||
to compile on parallel machines and avoid compilations altogether. See the
|
||||
--output-split option, and the web for the ccache, distcc and icecream
|
||||
packages. ccache will skip GCC runs between identical source builds, even
|
||||
across different users. You can use the OBJCACHE environment variable to
|
||||
use these CC wrappers.
|
||||
Use a recent compiler. Newer compilers tend do be faster, with the
|
||||
now relatively old GCC 3.0 to 3.3 being horrible.
|
||||
|
||||
Compile in parallel on many machines and use caching; see the web for the
|
||||
ccache, distcc and icecream packages. ccache will skip GCC runs between
|
||||
identical source builds, even across different users. You can use the
|
||||
OBJCACHE environment variable to use these CC wrappers. Also see the
|
||||
--output-split option.
|
||||
|
||||
To reduce the compile time of classes that use a Verilated module (e.g. a
|
||||
top CPP file) you may wish to add /*verilator no_inline_module*/ to your
|
||||
top level module. This will decrease the amount of code in the model's
|
||||
Verilated class, improving compile times of any instantiating top level C++
|
||||
code, at a relatively small cost of execution performance.
|
||||
|
||||
=item Why do so many files need to recompile when I add a signal?
|
||||
|
||||
@@ -4037,7 +4070,7 @@ License.)
|
||||
In 2001, Wilson Snyder took the kit, and added a SystemC mode, and
|
||||
called it Verilator2. This was the first packaged public release.
|
||||
|
||||
In 2002, Wilson Snyder created Verilator3 by rewriting Verilator from
|
||||
In 2002, Wilson Snyder created Verilator 3.000 by rewriting Verilator from
|
||||
scratch in C++. This added many optimizations, yielding about a 2-5x
|
||||
performance gain.
|
||||
|
||||
@@ -4069,50 +4102,59 @@ Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus Networks Inc.,
|
||||
and SiCortex Inc.
|
||||
|
||||
The people who have contributed major functionality are Byron Bradley,
|
||||
Jeremy Bennett, Jie Xu, Lane Brooks, Duane Galbi, Paul Wasson, and Wilson
|
||||
Snyder. Major testers include Jeff Dutton, Jonathon Donaldson, Ralf Karge,
|
||||
David Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov, Sebastien Van
|
||||
Cauwenberghe, Gene Weber, and Clifford Wolf.
|
||||
Jeremy Bennett, Jie Xu, Lane Brooks, John Coiner, Duane Galbi, Paul Wasson,
|
||||
and Wilson Snyder. Major testers included Jeff Dutton, Jonathon Donaldson,
|
||||
Ralf Karge, David Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov,
|
||||
Sebastien Van Cauwenberghe, Gene Weber, and Clifford Wolf.
|
||||
|
||||
Some of the people who have provided ideas and feedback for Verilator
|
||||
include: Yves Mathieu, David Addison, Nikana Anastasiadis, Hans Van
|
||||
Antwerpen, Vasu Arasanipalai, Jens Arm, Sharad Bagri, Andrew Bardsley,
|
||||
Geoff Barrett, J Baxter, Julius Baxter, Jeremy Bennett, Michael Berman,
|
||||
David Binderman, David Black, Daniel Bone, Gregg Bouchard, Christopher
|
||||
Boumenot, Nick Bowler, Byron Bradley, Bryan Brady, Charlie Brej, Lane
|
||||
Brooks, John Brownlee, Jeff Bush, Lawrence Butcher, Ted Campbell, Chris
|
||||
Candler, Lauren Carlson, Donal Casey, Terry Chen, Robert A. Clark, Allan
|
||||
Cochrane, Gunter Dannoritzer, Ashutosh Das, Bernard Deadman, Mike Denio,
|
||||
John Deroo, Philip Derrick, John Dickol, R. Diez, Ruben Diez, Danny Ding,
|
||||
Ivan Djordjevic, Jonathon Donaldson, Alex Duller, Jeff Dutton, Chandan
|
||||
Egbert, Joe Eiler, Ahmed El-Mahmoudy, Robert Farrell, Eugen Fekete,
|
||||
Fabrizio Ferrandi, Andrea Foletto, Bob Fredieu, Christian Gelinek, Glen
|
||||
Gibb, Shankar Giri, Sam Gladstone, Amir Gonnen, Chitlesh Goorah, Neil
|
||||
Hamilton, Junji Hashimoto, Thomas Hawkins, David Hewson, Hiroki Honda, Alex
|
||||
Hornung, Jae Hossell, Ben Jackson, Krzysztof Jankowski, HyungKi Jeong,
|
||||
Iztok Jeras, James Johnson, Christophe Joly, Franck Jullien, Mike Kagen,
|
||||
Kaalia Kahn, Guy-Armand Kamendje, Vasu Kandadi, Patricio Kaplan, Ralf
|
||||
Karge, Dan Katz, Sol Katzman, Jonathan Kimmitt, Sobhan Klnv, Gernot Koch,
|
||||
Soon Koh, Steve Kolecki, Brett Koonce, Wojciech Koszek, Varun Koyyalagunta,
|
||||
David Kravitz, Roland Kruse, Ed Lander, Steve Lang, Stephane Laurent,
|
||||
Walter Lavino, Christian Leber, Igor Lesik, John Li, Eivind Liland, Charlie
|
||||
Lind, Andrew Ling, Paul Liu, Derek Lockhart, Arthur Low, Stefan Ludwig, Dan
|
||||
Lussier, Fred Ma, Duraid Madina, Mark Marshall, Jason McMullan, Wim
|
||||
Michiels, Wai Sum Mong, Sean Moore, Dennis Muhlestein, John Murphy, Richard
|
||||
Myers, Dimitris Nalbantis, Bob Newgard, Cong Van Nguyen, Paul Nitza, Pete
|
||||
Nixon, Lisa Noack, Mark Nodine, Andreas Olofsson, Brad Parker, David
|
||||
Pierce, Dominic Plunkett, David Poole, Rich Porter, Niranjan Prabhu, Usha
|
||||
Priyadharshini, Mark Jackson Pulver, Prateek Puri, Chris Randall, Frederic
|
||||
Requin, Alberto Del Rio, Oleg Rodionov, Jan Egil Ruud, John Sanguinetti,
|
||||
Salman Sheikh, Mike Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Rodney
|
||||
Sinclair, Steven Slatter, Brian Small, Wilson Snyder, Alex Solomatnikov,
|
||||
Art Stamness, John Stevenson, Todd Strader, John Stroebel, Emerson
|
||||
include: Ahmed El-Mahmoudy, David Addison, Tariq B. Ahmad, Nikana
|
||||
Anastasiadis, Hans Van Antwerpen, Vasu Arasanipalai, Jens Arm, Sharad
|
||||
Bagri, Andrew Bardsley, Matthew Barr, Geoff Barrett, Julius Baxter, Jeremy
|
||||
Bennett, Michael Berman, David Binderman, Johan Bjork, David Black, Daniel
|
||||
Bone, Gregg Bouchard, Christopher Boumenot, Nick Bowler, Byron Bradley,
|
||||
Bryan Brady, Charlie Brej, J Briquet, Lane Brooks, John Brownlee, Jeff
|
||||
Bush, Lawrence Butcher, Ted Campbell, Chris Candler, Lauren Carlson, Donal
|
||||
Casey, Terry Chen, Enzo Chi, Robert A. Clark, Allan Cochrane, John Coiner,
|
||||
Gunter Dannoritzer, Ashutosh Das, Bernard Deadman, John Demme, Mike Denio,
|
||||
John Deroo, Philip Derrick, John Dickol, Ruben Diez, Danny Ding, Ivan
|
||||
Djordjevic, Jonathon Donaldson, Sebastian Dressler, Alex Duller, Jeff
|
||||
Dutton, Usuario Eda, Chandan Egbert, Joe Eiler, Ahmed El-Mahmoudy, Robert
|
||||
Farrell, Eugen Fekete, Fabrizio Ferrandi, Brian Flachs, Andrea Foletto, Bob
|
||||
Fredieu, Christian Gelinek, Glen Gibb, Shankar Giri, Dan Gisselquist, Sam
|
||||
Gladstone, Amir Gonnen, Chitlesh Goorah, Xuan Guo, Neil Hamilton, Jannis
|
||||
Harder, Junji Hashimoto, Thomas Hawkins, Robert Henry, David Hewson, Jamey
|
||||
Hicks, Hiroki Honda, Alex Hornung, David Horton, Jae Hossell, Alan Hunter,
|
||||
Jamie Iles, Ben Jackson, Shareef Jalloq, Krzysztof Jankowski, HyungKi
|
||||
Jeong, Iztok Jeras, James Johnson, Christophe Joly, Franck Jullien, Mike
|
||||
Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand Kamendje, Vasu Kandadi,
|
||||
Patricio Kaplan, Ralf Karge, Dan Katz, Sol Katzman, Jonathan Kimmitt, Olof
|
||||
Kindgren, Sobhan Klnv, Gernot Koch, Soon Koh, Steve Kolecki, Brett Koonce,
|
||||
Wojciech Koszek, Varun Koyyalagunta, David Kravitz, Roland Kruse, Sergey
|
||||
Kvachonok, Ed Lander, Steve Lang, Stephane Laurent, Walter Lavino,
|
||||
Christian Leber, Igor Lesik, John Li, Eivind Liland, Charlie Lind, Andrew
|
||||
Ling, Paul Liu, Derek Lockhart, Arthur Low, Stefan Ludwig, Dan Lussier,
|
||||
Fred Ma, Duraid Madina, Mark Marshall, Alfonso Martinez, Yves Mathieu,
|
||||
Patrick Maupin, Jason McMullan, Elliot Mednick, Wim Michiels, Miodrag
|
||||
Milanovic, Wai Sum Mong, Sean Moore, Dennis Muhlestein, John Murphy,
|
||||
Richard Myers, Dimitris Nalbantis, Bob Newgard, Cong Van Nguyen, Paul
|
||||
Nitza, Pete Nixon, Lisa Noack, Mark Nodine, Andreas Olofsson, James
|
||||
Pallister, Brad Parker, Maciej Piechotka, David Pierce, Dominic Plunkett,
|
||||
David Poole, Mike Popoloski, Rich Porter, Niranjan Prabhu, Usha
|
||||
Priyadharshini, Mark Jackson Pulver, Prateek Puri, Chris Randall, Anton
|
||||
Rapp, Odd Magne Reitan, Frederic Requin, Alberto Del Rio, Oleg Rodionov,
|
||||
Paul Rolfe, Jan Egil Ruud, John Sanguinetti, Galen Seitz, Salman Sheikh,
|
||||
Mike Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Rodney Sinclair, Steven
|
||||
Slatter, Brian Small, Alex Solomatnikov, Wei Song, Art Stamness, John
|
||||
Stevenson, Rob Stoddard, Todd Strader, John Stroebel, Sven Stucki, Emerson
|
||||
Suguimoto, Gene Sullivan, Renga Sundararajan, Yutetsu Takatsukasa, Peter
|
||||
Tengstrand, Stefan Thiede, Gary Thomas, Kevin Thompson, Mike Thyer, Steve
|
||||
Tong, Holger Waechtler, Stefan Wallentowitz, Shawn Wang, Greg Waters,
|
||||
Thomas Watts, Eugene Weber, David Welch, Leon Wildman, Gerald Williams,
|
||||
Trevor Williams, Jeff Winston, Joshua Wise, Clifford Wolf, Johan Wouters,
|
||||
Ding Xiaoliang, Jie Xu, and Amir Yazdanbakhsh.
|
||||
Tengstrand, Wesley Terpstra, Stefan Thiede, Gary Thomas, Kevin Thompson,
|
||||
Ian Thompson, Mike Thyer, Hans Tichelaar, Steve Tong, Michael Tresidder,
|
||||
Holger Waechtler, Stefan Wallentowitz, Shawn Wang, Greg Waters, Thomas
|
||||
Watts, Eugene Weber, David Welch, Thomas J Whatson, Leon Wildman, Gerald
|
||||
Williams, Trevor Williams, Jeff Winston, Joshua Wise, Clifford Wolf, Johan
|
||||
Wouters, Ding Xiaoliang, Jie Xu, Mandy Xu, Luke Yang, and Amir
|
||||
Yazdanbakhsh.
|
||||
|
||||
Thanks to them, and all those we've missed including above.
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ additional points.
|
||||
Additional Verilog-standard arguments specify the search paths necessary to
|
||||
find the source code that the coverage analysis was performed on.
|
||||
|
||||
To get correct coverage percentages, you may wish to read logs/coverage.pl
|
||||
To get correct coverage percentages, you may wish to read logs/coverage.dat
|
||||
into Emacs and do a M-x keep-lines to include only those statistics of
|
||||
interest.
|
||||
|
||||
|
||||
+21
-3
@@ -6,7 +6,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[3.914 2017-10-14])
|
||||
AC_INIT([Verilator],[3.916 2017-11-25])
|
||||
# When releasing, also update header of Changes file
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
|
||||
@@ -107,7 +107,7 @@ AC_TYPE_SIZE_T
|
||||
AC_C_INLINE
|
||||
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([], [[int* a=new int; delete a; ]])],
|
||||
[AC_LANG_PROGRAM([#include <new>], [[int* a=new int; delete a; ]])],
|
||||
[], [AC_MSG_ERROR([$CXX does not seem to successfully compile a simple C++ program])])
|
||||
|
||||
AC_DEFUN([_MY_CXX_CHECK_FLAG],
|
||||
@@ -119,7 +119,7 @@ AC_DEFUN([_MY_CXX_CHECK_FLAG],
|
||||
CXXFLAGS="$CXXFLAGS $1 -Werror"
|
||||
AC_MSG_CHECKING([whether $CXX accepts $1])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([], [[int* a=new int; delete a; ]])],
|
||||
[AC_LANG_PROGRAM([#include <new>], [[int* a=new int; delete a; ]])],
|
||||
[_my_result=yes
|
||||
if test -s conftest.err; then
|
||||
if grep -e "$1" conftest.err >/dev/null; then
|
||||
@@ -190,6 +190,7 @@ AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wextra)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wfloat-conversion)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wlogical-op)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
|
||||
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
||||
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
@@ -226,6 +227,23 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-unused-variable)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-shadow)
|
||||
AC_SUBST(CFG_CXXFLAGS_NO_UNUSED)
|
||||
|
||||
# Set CFG_WITH_THREADED if can support threading
|
||||
AC_MSG_CHECKING(whether $CXX supports Verilated threads)
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $CFG_CXXFLAGS_STD_NEWEST $CFG_CXXFLAGS_NO_UNUSED"
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([#include <thread>], [[ ]])],
|
||||
[_my_result=yes
|
||||
if test -s conftest.err; then
|
||||
if grep -e "$1" conftest.err >/dev/null; then
|
||||
_my_result=no
|
||||
fi
|
||||
fi],
|
||||
[_my_result=no])
|
||||
CFG_WITH_THREADED=$_my_result
|
||||
AC_SUBST(CFG_WITH_THREADED)
|
||||
AC_MSG_RESULT($CFG_WITH_THREADED)
|
||||
|
||||
# Checks for library functions.
|
||||
AC_CHECK_MEMBER([struct stat.st_mtim.tv_nsec],
|
||||
[AC_DEFINE([HAVE_STAT_NSEC],[1],[Defined if struct stat has st_mtim.tv_nsec])],
|
||||
|
||||
@@ -73,7 +73,7 @@ run:
|
||||
@echo
|
||||
@echo "-- RUN ---------------------"
|
||||
@mkdir -p logs
|
||||
obj_dir/Vtop | tee logs/sim.log
|
||||
obj_dir/Vtop +trace
|
||||
|
||||
@echo
|
||||
@echo "-- COVERAGE ----------------"
|
||||
|
||||
@@ -41,14 +41,19 @@ int main(int argc, char** argv, char** env) {
|
||||
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
|
||||
Vtop* top = new Vtop; // Or use a const unique_ptr, or the VL_UNIQUE_PTR wrapper
|
||||
|
||||
// If verilator was invoked with --trace, open trace
|
||||
#if VM_TRACE
|
||||
Verilated::traceEverOn(true); // Verilator must compute traced signals
|
||||
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
|
||||
VerilatedVcdC* tfp = new VerilatedVcdC;
|
||||
top->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
mkdir("logs", 0777);
|
||||
tfp->open("logs/vlt_dump.vcd"); // Open the dump file
|
||||
// If verilator was invoked with --trace argument,
|
||||
// and if at run time passed the +trace argument, turn on tracing
|
||||
VerilatedVcdC* tfp = NULL;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0==strcmp(flag, "+trace")) {
|
||||
Verilated::traceEverOn(true); // Verilator must compute traced signals
|
||||
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
|
||||
tfp = new VerilatedVcdC;
|
||||
top->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
mkdir("logs", 0777);
|
||||
tfp->open("logs/vlt_dump.vcd"); // Open the dump file
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set some inputs
|
||||
@@ -57,9 +62,9 @@ int main(int argc, char** argv, char** env) {
|
||||
top->clk = 0;
|
||||
top->in_small = 1;
|
||||
top->in_quad = 0x1234;
|
||||
top->in_wide[0] = 0x1;
|
||||
top->in_wide[0] = 0x11111111;
|
||||
top->in_wide[1] = 0x22222222;
|
||||
top->in_wide[2] = 0x33333333;
|
||||
top->in_wide[2] = 0x3;
|
||||
|
||||
// Simulate until $finish
|
||||
while (!Verilated::gotFinish()) {
|
||||
|
||||
@@ -81,7 +81,7 @@ run:
|
||||
@echo
|
||||
@echo "-- RUN ---------------------"
|
||||
@mkdir -p logs
|
||||
obj_dir/Vtop | tee logs/sim.log
|
||||
obj_dir/Vtop +trace
|
||||
|
||||
@echo
|
||||
@echo "-- COVERAGE ----------------"
|
||||
|
||||
@@ -87,13 +87,18 @@ int sc_main(int argc, char* argv[]) {
|
||||
sc_start(1);
|
||||
#endif
|
||||
|
||||
// Turn on waves
|
||||
#if VM_TRACE
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
VerilatedVcdSc* tfp = new VerilatedVcdSc;
|
||||
top->trace (tfp, 99);
|
||||
mkdir("logs", 0777);
|
||||
tfp->open ("logs/vlt_dump.vcd");
|
||||
// If verilator was invoked with --trace argument,
|
||||
// and if at run time passed the +trace argument, turn on tracing
|
||||
VerilatedVcdSc* tfp = NULL;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0==strcmp(flag, "+trace")) {
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
VerilatedVcdSc* tfp = new VerilatedVcdSc;
|
||||
top->trace (tfp, 99);
|
||||
mkdir("logs", 0777);
|
||||
tfp->open ("logs/vlt_dump.vcd");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Simulate until $finish
|
||||
|
||||
+341
-112
@@ -33,13 +33,13 @@
|
||||
// Global variables
|
||||
|
||||
// Slow path variables
|
||||
VerilatedMutex Verilated::m_mutex;
|
||||
VerilatedVoidCb Verilated::s_flushCb = NULL;
|
||||
|
||||
// Keep below together in one cache line
|
||||
Verilated::Serialized Verilated::s_s;
|
||||
VL_THREAD_LOCAL const VerilatedScope* Verilated::t_dpiScopep = NULL;
|
||||
VL_THREAD_LOCAL const char* Verilated::t_dpiFilename = "";
|
||||
VL_THREAD_LOCAL int Verilated::t_dpiLineno = 0;
|
||||
VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s;
|
||||
|
||||
struct Verilated::CommandArgValues Verilated::s_args = {0, NULL};
|
||||
|
||||
VerilatedImp VerilatedImp::s_s;
|
||||
@@ -48,11 +48,11 @@ VerilatedImp VerilatedImp::s_s;
|
||||
// User definable functions
|
||||
|
||||
#ifndef VL_USER_FINISH // Define this to override this function
|
||||
void vl_finish (const char* filename, int linenum, const char* hier) {
|
||||
void vl_finish (const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
||||
if (0 && hier) {}
|
||||
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum);
|
||||
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum); // Not VL_PRINTF_MT, already on main thread
|
||||
if (Verilated::gotFinish()) {
|
||||
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
|
||||
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum); // Not VL_PRINTF_MT, already on main thread
|
||||
Verilated::flushCall();
|
||||
exit(0);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ void vl_finish (const char* filename, int linenum, const char* hier) {
|
||||
#endif
|
||||
|
||||
#ifndef VL_USER_STOP // Define this to override this function
|
||||
void vl_stop (const char* filename, int linenum, const char* hier) {
|
||||
void vl_stop (const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
||||
Verilated::gotFinish(true);
|
||||
Verilated::flushCall();
|
||||
vl_fatal (filename,linenum,hier,"Verilog $stop");
|
||||
@@ -69,18 +69,111 @@ void vl_stop (const char* filename, int linenum, const char* hier) {
|
||||
#endif
|
||||
|
||||
#ifndef VL_USER_FATAL // Define this to override this function
|
||||
void vl_fatal (const char* filename, int linenum, const char* hier, const char* msg) {
|
||||
void vl_fatal (const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
|
||||
if (0 && hier) {}
|
||||
Verilated::gotFinish(true);
|
||||
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
|
||||
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg); // Not VL_PRINTF_MT, already on main thread
|
||||
Verilated::flushCall();
|
||||
|
||||
VL_PRINTF("Aborting...\n");
|
||||
VL_PRINTF("Aborting...\n"); // Not VL_PRINTF_MT, already on main thread
|
||||
Verilated::flushCall(); // Second flush in case VL_PRINTF does something needing a flush
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
// Wrapper to call certain functions via messages when multithreaded
|
||||
|
||||
void VL_FINISH_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
vl_finish(filename, linenum, hier);
|
||||
}));
|
||||
#else
|
||||
vl_finish(filename, linenum, hier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_STOP_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
vl_stop(filename, linenum, hier);
|
||||
}));
|
||||
#else
|
||||
vl_stop(filename, linenum, hier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_FATAL_MT (const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
}));
|
||||
#else
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Debug prints
|
||||
|
||||
/// sprintf but return as string (this isn't fast, for print messages only)
|
||||
std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
va_list aq;
|
||||
va_copy(aq, ap);
|
||||
int len = VL_VSNPRINTF(NULL, 0, formatp, aq);
|
||||
if (VL_UNLIKELY(len < 1)) return "";
|
||||
|
||||
char* bufp = new char[len+1];
|
||||
VL_VSNPRINTF(bufp, len+1, formatp, ap);
|
||||
std::string out = std::string(bufp, len);
|
||||
delete[] bufp;
|
||||
return out;
|
||||
}
|
||||
|
||||
vluint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
static std::atomic<vluint64_t> sequence;
|
||||
#else
|
||||
static vluint64_t sequence = 0;
|
||||
#endif
|
||||
return ++sequence;
|
||||
}
|
||||
|
||||
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
// Alternative is to use std::this_thread::get_id, but that returns a hard-to-read number and is very slow
|
||||
static std::atomic<vluint32_t> s_nextId (0);
|
||||
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
|
||||
return t_myId;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
||||
// We're still using c printf formats instead of operator<< so we can avoid the heavy
|
||||
// includes that otherwise would be required in every Verilated module
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
std::string out = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
// printf("-imm-V{t%d,%" VL_PRI64 "d}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), out.c_str());
|
||||
VL_PRINTF_MT("-V{t%d,%" VL_PRI64 "d}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), out.c_str());
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
std::string out = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
VL_PRINTF("%s", out.c_str());
|
||||
}));
|
||||
}
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
// Overall class init
|
||||
|
||||
@@ -96,8 +189,25 @@ Verilated::Serialized::Serialized() {
|
||||
//===========================================================================
|
||||
// Random reset -- Only called at init time, so don't inline.
|
||||
|
||||
IData VL_RAND32() {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
IData VL_RAND32() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
static VL_THREAD_LOCAL bool t_seeded = false;
|
||||
static VL_THREAD_LOCAL drand48_data t_buffer;
|
||||
static VerilatedMutex s_mutex;
|
||||
if (VL_UNLIKELY(!t_seeded)) {
|
||||
t_seeded = true;
|
||||
long seedval;
|
||||
{
|
||||
VerilatedLockGuard guard(s_mutex);
|
||||
seedval = lrand48()<<16 ^ lrand48();
|
||||
if (!seedval) seedval++;
|
||||
}
|
||||
srand48_r(seedval, &t_buffer);
|
||||
}
|
||||
long v0; lrand48_r(&t_buffer, &v0);
|
||||
long v1; lrand48_r(&t_buffer, &v1);
|
||||
return (v0<<16) ^ v1;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
// Windows doesn't have lrand48(), although Cygwin does.
|
||||
return (rand()<<16) ^ rand();
|
||||
#else
|
||||
@@ -105,16 +215,16 @@ IData VL_RAND32() {
|
||||
#endif
|
||||
}
|
||||
|
||||
IData VL_RANDOM_I(int obits) {
|
||||
IData VL_RANDOM_I(int obits) VL_MT_SAFE {
|
||||
return VL_RAND32() & VL_MASK_I(obits);
|
||||
}
|
||||
|
||||
QData VL_RANDOM_Q(int obits) {
|
||||
QData VL_RANDOM_Q(int obits) VL_MT_SAFE {
|
||||
QData data = ((QData)VL_RAND32()<<VL_ULL(32)) | (QData)VL_RAND32();
|
||||
return data & VL_MASK_Q(obits);
|
||||
}
|
||||
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) {
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
||||
if (i<(VL_WORDS_I(obits)-1)) {
|
||||
outwp[i] = VL_RAND32();
|
||||
@@ -125,7 +235,7 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) {
|
||||
return outwp;
|
||||
}
|
||||
|
||||
IData VL_RAND_RESET_I(int obits) {
|
||||
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
if (Verilated::randReset()==0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::randReset()!=1) { // if 2, randomize
|
||||
@@ -135,7 +245,7 @@ IData VL_RAND_RESET_I(int obits) {
|
||||
return data;
|
||||
}
|
||||
|
||||
QData VL_RAND_RESET_Q(int obits) {
|
||||
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::randReset()==0) return 0;
|
||||
QData data = VL_ULL(~0);
|
||||
if (Verilated::randReset()!=1) { // if 2, randomize
|
||||
@@ -145,7 +255,7 @@ QData VL_RAND_RESET_Q(int obits) {
|
||||
return data;
|
||||
}
|
||||
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) {
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
||||
if (i<(VL_WORDS_I(obits)-1)) {
|
||||
outwp[i] = VL_RAND_RESET_I(32);
|
||||
@@ -156,7 +266,7 @@ WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) {
|
||||
return outwp;
|
||||
}
|
||||
|
||||
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) {
|
||||
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i=0; i<VL_WORDS_I(obits); ++i) outwp[i] = 0;
|
||||
return outwp;
|
||||
}
|
||||
@@ -164,16 +274,16 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) {
|
||||
//===========================================================================
|
||||
// Debug
|
||||
|
||||
void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp) {
|
||||
VL_PRINTF(" Data: w%d: ", lbits);
|
||||
for (int i=VL_WORDS_I(lbits)-1; i>=0; --i) { VL_PRINTF("%08x ",iwp[i]); }
|
||||
VL_PRINTF("\n");
|
||||
void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp) VL_MT_SAFE {
|
||||
VL_PRINTF_MT(" Data: w%d: ", lbits);
|
||||
for (int i=VL_WORDS_I(lbits)-1; i>=0; --i) { VL_PRINTF_MT("%08x ",iwp[i]); }
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Slow math
|
||||
|
||||
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus) {
|
||||
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus) VL_MT_SAFE {
|
||||
// See Knuth Algorithm D. Computes u/v = q.r
|
||||
// This isn't massively tuned, as wide division is rare
|
||||
// for debug see V3Number version
|
||||
@@ -282,7 +392,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
|
||||
}
|
||||
}
|
||||
|
||||
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp) {
|
||||
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
|
||||
// obits==lbits, rbits can be different
|
||||
owp[0] = 1;
|
||||
for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
|
||||
@@ -304,11 +414,11 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDa
|
||||
}
|
||||
return owp;
|
||||
}
|
||||
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs) {
|
||||
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs) VL_MT_SAFE {
|
||||
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
|
||||
return VL_POW_WWW(obits,lbits,rbits,owp,lwp,rhsw);
|
||||
}
|
||||
QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) {
|
||||
QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) VL_MT_SAFE {
|
||||
// Skip check for rhs == 0, as short-circuit doesn't save time
|
||||
if (VL_UNLIKELY(lhs==0)) return 0;
|
||||
QData power = lhs;
|
||||
@@ -320,7 +430,7 @@ QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) {
|
||||
return out;
|
||||
}
|
||||
|
||||
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool lsign, bool rsign) {
|
||||
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
|
||||
// obits==lbits, rbits can be different
|
||||
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
||||
int words = VL_WORDS_I(obits);
|
||||
@@ -340,11 +450,11 @@ WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, W
|
||||
}
|
||||
return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp);
|
||||
}
|
||||
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs, bool lsign, bool rsign) {
|
||||
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs, bool lsign, bool rsign) VL_MT_SAFE {
|
||||
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
|
||||
return VL_POWSS_WWW(obits,lbits,rbits,owp,lwp,rhsw,lsign,rsign);
|
||||
}
|
||||
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsign, bool rsign) {
|
||||
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
|
||||
// Skip check for rhs == 0, as short-circuit doesn't save time
|
||||
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
||||
if (lhs==0) return 0; // "X"
|
||||
@@ -364,7 +474,7 @@ QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsig
|
||||
// Do a va_arg returning a quad, assuming input argument is anything less than wide
|
||||
#define _VL_VA_ARG_Q(ap, bits) (((bits) <= VL_WORDSIZE) ? va_arg(ap,IData) : va_arg(ap,QData))
|
||||
|
||||
void _vl_vsformat(std::string& output, const char* formatp, va_list ap) {
|
||||
void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
// Format a Verilog $write style format into the output list
|
||||
// The format must be pre-processed (and lower cased) by Verilator
|
||||
// Arguments are in "width, arg-value (or WDataIn* if wide)" form
|
||||
@@ -502,7 +612,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) {
|
||||
static_cast<QData>(ld/VL_TIME_MULTIPLIER),
|
||||
static_cast<QData>(ld%VL_TIME_MULTIPLIER));
|
||||
} else {
|
||||
vl_fatal(__FILE__,__LINE__,"","Unsupported VL_TIME_MULTIPLIER");
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"","Unsupported VL_TIME_MULTIPLIER");
|
||||
}
|
||||
int needmore = width-digits;
|
||||
if (needmore>0) output.append(needmore,' '); // Pre-pad spaces
|
||||
@@ -561,7 +671,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) {
|
||||
break;
|
||||
default:
|
||||
std::string msg = std::string("Unknown _vl_vsformat code: ")+pos[0];
|
||||
vl_fatal(__FILE__,__LINE__,"",msg.c_str());
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
@@ -570,15 +680,15 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool _vl_vsss_eof(FILE* fp, int& floc) {
|
||||
static inline bool _vl_vsss_eof(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
if (fp) return feof(fp) ? 1 : 0; // 1:0 to prevent MSVC++ warning
|
||||
else return (floc<0);
|
||||
}
|
||||
static inline void _vl_vsss_advance(FILE* fp, int& floc) {
|
||||
static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
if (fp) fgetc(fp);
|
||||
else floc -= 8;
|
||||
}
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) {
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
|
||||
// Get a character without advancing
|
||||
if (fp) {
|
||||
int data = fgetc(fp);
|
||||
@@ -595,7 +705,7 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std:
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) {
|
||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
|
||||
while (1) {
|
||||
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
if (c==EOF || !isspace(c)) return;
|
||||
@@ -603,7 +713,7 @@ static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp, const
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_read(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr,
|
||||
char* tmpp, const char* acceptp) {
|
||||
char* tmpp, const char* acceptp) VL_MT_SAFE {
|
||||
// Read into tmp, consisting of characters from acceptp list
|
||||
char* cp = tmpp;
|
||||
while (1) {
|
||||
@@ -616,14 +726,15 @@ static inline void _vl_vsss_read(FILE* fp, int& floc, WDataInP fromp, const std:
|
||||
_vl_vsss_advance(fp, floc);
|
||||
}
|
||||
*cp++ = '\0';
|
||||
//VL_PRINTF("\t_read got='%s'\n", tmpp);
|
||||
//VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
|
||||
}
|
||||
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) {
|
||||
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
|
||||
for (; nbits && lsb<obits; nbits--, lsb++, ld>>=1) {
|
||||
VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1);
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart, size_t posend) {
|
||||
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2,
|
||||
const char* strp, size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||
int lsb = 0;
|
||||
for (int i=0, pos=static_cast<int>(posend)-1; i<obits && pos>=static_cast<int>(posstart); --pos) {
|
||||
@@ -653,7 +764,7 @@ static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const
|
||||
IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
int fbits, WDataInP fromp, // Else if a sscanf
|
||||
const std::string& fstr, // if a sscanf to string
|
||||
const char* formatp, va_list ap) {
|
||||
const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
// Read a Verilog $sscanf/$fscanf style format into the output list
|
||||
// The format must be pre-processed (and lower cased) by Verilator
|
||||
// Arguments are in "width, arg-value (or WDataIn* if wide)" form
|
||||
@@ -663,7 +774,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
bool inPct = false;
|
||||
const char* pos = formatp;
|
||||
for (; *pos && !_vl_vsss_eof(fp,floc); ++pos) {
|
||||
//VL_PRINTF("_vlscan fmt='%c' floc=%d file='%c'\n", pos[0], floc, _vl_vsss_peek(fp,floc,fromp,fstr));
|
||||
//VL_DBG_MSGF("_vlscan fmt='"<<pos[0]<<"' floc="<<floc<<" file='"<<_vl_vsss_peek(fp,floc,fromp,fstr)<<"'"<<endl);
|
||||
if (!inPct && pos[0]=='%') {
|
||||
inPct = true;
|
||||
} else if (!inPct && isspace(pos[0])) { // Format spaces
|
||||
@@ -768,7 +879,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
}
|
||||
default:
|
||||
std::string msg = std::string("Unknown _vl_vsscanf code: ")+pos[0];
|
||||
vl_fatal(__FILE__,__LINE__,"",msg.c_str());
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
break;
|
||||
} // switch
|
||||
|
||||
@@ -794,11 +905,11 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
//===========================================================================
|
||||
// File I/O
|
||||
|
||||
FILE* VL_CVT_I_FP(IData lhs) {
|
||||
FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
|
||||
return VerilatedImp::fdToFp(lhs);
|
||||
}
|
||||
|
||||
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
|
||||
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE {
|
||||
// See also VL_DATA_TO_STRING_NW
|
||||
int lsb=obits-1;
|
||||
bool start=true;
|
||||
@@ -815,7 +926,7 @@ void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
|
||||
if (!start) while (isspace(*(destp-1)) && destp>destoutp) *--destp = '\0'; // Drop trailing spaces
|
||||
}
|
||||
|
||||
void _VL_STRING_TO_VINT(int obits, void* destp, size_t srclen, const char* srcp) {
|
||||
void _VL_STRING_TO_VINT(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE {
|
||||
// Convert C string to Verilog format
|
||||
size_t bytes = VL_BYTES_I(obits);
|
||||
char* op = reinterpret_cast<char*>(destp);
|
||||
@@ -825,7 +936,8 @@ void _VL_STRING_TO_VINT(int obits, void* destp, size_t srclen, const char* srcp)
|
||||
for (; i<bytes; ++i) { *op++ = 0; }
|
||||
}
|
||||
|
||||
IData VL_FGETS_IXI(int obits, void* destp, IData fpi) {
|
||||
IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
|
||||
@@ -836,14 +948,14 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) {
|
||||
char buffer[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
|
||||
if (VL_UNLIKELY(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
|
||||
vl_fatal(__FILE__,__LINE__,"","Internal: fgets buffer overrun");
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"","Internal: fgets buffer overrun");
|
||||
}
|
||||
|
||||
// We don't use fgets, as we must read \0s.
|
||||
IData got = 0;
|
||||
char* cp = buffer;
|
||||
while (got < bytes) {
|
||||
int c = getc(fp);
|
||||
int c = getc(fp); // getc() is threadsafe
|
||||
if (c==EOF) break;
|
||||
*cp++ = c; got++;
|
||||
if (c=='\n') break;
|
||||
@@ -853,34 +965,38 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) {
|
||||
return got;
|
||||
}
|
||||
|
||||
IData VL_FOPEN_NI(const std::string& filename, IData mode) {
|
||||
IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
char modez[5];
|
||||
_VL_VINT_TO_STRING(VL_WORDSIZE, modez, &mode);
|
||||
return VL_FOPEN_S(filename.c_str(), modez);
|
||||
}
|
||||
IData VL_FOPEN_QI(QData filename, IData mode) {
|
||||
IData VL_FOPEN_QI(QData filename, IData mode) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
WData fnw[2]; VL_SET_WQ(fnw, filename);
|
||||
return VL_FOPEN_WI(2, fnw, mode);
|
||||
}
|
||||
IData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) {
|
||||
IData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, filenamez, filenamep);
|
||||
char modez[5];
|
||||
_VL_VINT_TO_STRING(VL_WORDSIZE, modez, &mode);
|
||||
return VL_FOPEN_S(filenamez,modez);
|
||||
}
|
||||
IData VL_FOPEN_S(const char* filenamep, const char* modep) {
|
||||
IData VL_FOPEN_S(const char* filenamep, const char* modep) VL_MT_SAFE {
|
||||
return VerilatedImp::fdNew(fopen(filenamep,modep));
|
||||
}
|
||||
|
||||
void VL_FCLOSE_I(IData fdi) {
|
||||
void VL_FCLOSE_I(IData fdi) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fdi);
|
||||
if (VL_UNLIKELY(!fp)) return;
|
||||
fclose(fp);
|
||||
VerilatedImp::fdDelete(fdi);
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) {
|
||||
void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -891,7 +1007,7 @@ void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) {
|
||||
_VL_STRING_TO_VINT(obits, &destr, output.length(), output.c_str());
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...) {
|
||||
void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -902,7 +1018,7 @@ void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...) {
|
||||
_VL_STRING_TO_VINT(obits, &destr, output.length(), output.c_str());
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...) {
|
||||
void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -913,7 +1029,7 @@ void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...) {
|
||||
_VL_STRING_TO_VINT(obits, &destr, output.length(), output.c_str());
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...) {
|
||||
void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -924,7 +1040,7 @@ void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...) {
|
||||
_VL_STRING_TO_VINT(obits, &destr, output.length(), output.c_str());
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) {
|
||||
void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -935,7 +1051,7 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) {
|
||||
_VL_STRING_TO_VINT(obits, destp, output.length(), output.c_str());
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits_ignored, std::string &output, const char* formatp, ...) {
|
||||
void VL_SFORMAT_X(int obits_ignored, std::string &output, const char* formatp, ...) VL_MT_SAFE {
|
||||
if (obits_ignored) {}
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -944,7 +1060,7 @@ void VL_SFORMAT_X(int obits_ignored, std::string &output, const char* formatp, .
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
std::string VL_SFORMATF_NX(const char* formatp, ...) {
|
||||
std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -955,7 +1071,7 @@ std::string VL_SFORMATF_NX(const char* formatp, ...) {
|
||||
return output;
|
||||
}
|
||||
|
||||
void VL_WRITEF(const char* formatp, ...) {
|
||||
void VL_WRITEF(const char* formatp, ...) VL_MT_SAFE {
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
va_list ap;
|
||||
@@ -963,11 +1079,11 @@ void VL_WRITEF(const char* formatp, ...) {
|
||||
_vl_vsformat(output, formatp, ap);
|
||||
va_end(ap);
|
||||
|
||||
// Users can redefine VL_PRINTF if they wish.
|
||||
VL_PRINTF("%s", output.c_str());
|
||||
VL_PRINTF_MT("%s", output.c_str());
|
||||
}
|
||||
|
||||
void VL_FWRITEF(IData fpi, const char* formatp, ...) {
|
||||
void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
static VL_THREAD_LOCAL std::string output; // static only for speed
|
||||
output = "";
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
@@ -981,7 +1097,8 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) {
|
||||
fputs(output.c_str(), fp);
|
||||
}
|
||||
|
||||
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) {
|
||||
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
|
||||
@@ -992,7 +1109,7 @@ IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) {
|
||||
return got;
|
||||
}
|
||||
|
||||
IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) {
|
||||
IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) VL_MT_SAFE {
|
||||
WData fnw[2]; VL_SET_WI(fnw, ld);
|
||||
|
||||
va_list ap;
|
||||
@@ -1001,7 +1118,7 @@ IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) {
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) {
|
||||
IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) VL_MT_SAFE {
|
||||
WData fnw[2]; VL_SET_WQ(fnw, ld);
|
||||
|
||||
va_list ap;
|
||||
@@ -1010,14 +1127,14 @@ IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) {
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...) {
|
||||
IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
IData got = _vl_vsscanf(NULL, lbits, lwp, "", formatp, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) {
|
||||
IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
IData got = _vl_vsscanf(NULL, ld.length()*8, NULL, ld, formatp, ap);
|
||||
@@ -1026,13 +1143,13 @@ IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) {
|
||||
}
|
||||
|
||||
void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int,
|
||||
QData ofilename, void* memp, IData start, IData end) {
|
||||
QData ofilename, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
WData fnw[2]; VL_SET_WQ(fnw, ofilename);
|
||||
return VL_READMEM_W(hex,width,depth,array_lsb,2, fnw,memp,start,end);
|
||||
}
|
||||
|
||||
void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
WDataInP ofilenamep, void* memp, IData start, IData end) {
|
||||
WDataInP ofilenamep, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
char ofilenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, ofilenamez, ofilenamep);
|
||||
std::string ofilenames(ofilenamez);
|
||||
@@ -1040,12 +1157,12 @@ void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
}
|
||||
|
||||
void VL_READMEM_N(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
const std::string& ofilenamep, void* memp, IData start, IData end) {
|
||||
const std::string& ofilenamep, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
if (fnwords) {}
|
||||
FILE* fp = fopen(ofilenamep.c_str(), "r");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
// We don't report the Verilog source filename as it slow to have to pass it down
|
||||
vl_fatal (ofilenamep.c_str(), 0, "", "$readmem file not found");
|
||||
VL_FATAL_MT (ofilenamep.c_str(), 0, "", "$readmem file not found");
|
||||
return;
|
||||
}
|
||||
// Prep for reading
|
||||
@@ -1091,7 +1208,7 @@ void VL_READMEM_N(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
//printf(" Value width=%d @%x = %c\n", width, addr, c);
|
||||
if (VL_UNLIKELY(addr >= static_cast<IData>(depth+array_lsb)
|
||||
|| addr < static_cast<IData>(array_lsb))) {
|
||||
vl_fatal (ofilenamep.c_str(), linenum, "", "$readmem file address beyond bounds of array");
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "", "$readmem file address beyond bounds of array");
|
||||
} else {
|
||||
int entry = addr - array_lsb;
|
||||
QData shift = hex ? VL_ULL(4) : VL_ULL(1);
|
||||
@@ -1120,14 +1237,14 @@ void VL_READMEM_N(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
datap[0] |= value;
|
||||
}
|
||||
if (VL_UNLIKELY(value>=(1<<shift))) {
|
||||
vl_fatal (ofilenamep.c_str(), linenum, "", "$readmemb (binary) file contains hex characters");
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "", "$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
}
|
||||
}
|
||||
innum = true;
|
||||
}
|
||||
else {
|
||||
vl_fatal (ofilenamep.c_str(), linenum, "", "$readmem file syntax error");
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "", "$readmem file syntax error");
|
||||
}
|
||||
}
|
||||
lastc = c;
|
||||
@@ -1137,28 +1254,28 @@ void VL_READMEM_N(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
// Final checks
|
||||
fclose(fp);
|
||||
if (VL_UNLIKELY(end != VL_UL(0xffffffff) && addr != (end+1))) {
|
||||
vl_fatal (ofilenamep.c_str(), linenum, "", "$readmem file ended before specified ending-address");
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "", "$readmem file ended before specified ending-address");
|
||||
}
|
||||
}
|
||||
|
||||
IData VL_SYSTEM_IQ(QData lhs) {
|
||||
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
WData lhsw[2]; VL_SET_WQ(lhsw, lhs);
|
||||
return VL_SYSTEM_IW(2, lhsw);
|
||||
}
|
||||
IData VL_SYSTEM_IW(int lhswords, WDataInP filenamep) {
|
||||
IData VL_SYSTEM_IW(int lhswords, WDataInP filenamep) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
_VL_VINT_TO_STRING(lhswords*VL_WORDSIZE, filenamez, filenamep);
|
||||
int code = system(filenamez);
|
||||
int code = system(filenamez); // Yes, system() is threadsafe
|
||||
return code >> 8; // Want exit status
|
||||
}
|
||||
|
||||
IData VL_TESTPLUSARGS_I(const char* formatp) {
|
||||
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
|
||||
const std::string& match = VerilatedImp::argPlusMatch(formatp);
|
||||
if (match == "") return 0;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) {
|
||||
IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE {
|
||||
std::string prefix;
|
||||
bool inPct = false;
|
||||
bool done = false;
|
||||
@@ -1217,7 +1334,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) {
|
||||
_VL_CLEAN_INPLACE_W(rbits,rwp);
|
||||
return 1;
|
||||
}
|
||||
IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) {
|
||||
IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE {
|
||||
std::string prefix;
|
||||
bool inPct = false;
|
||||
bool done = false;
|
||||
@@ -1245,7 +1362,7 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* vl_mc_scan_plusargs(const char* prefixp) {
|
||||
const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE {
|
||||
const std::string& match = VerilatedImp::argPlusMatch(prefixp);
|
||||
static VL_THREAD_LOCAL char outstr[VL_VALUE_STRING_MAX_WIDTH];
|
||||
if (match == "") return NULL;
|
||||
@@ -1258,7 +1375,7 @@ const char* vl_mc_scan_plusargs(const char* prefixp) {
|
||||
//===========================================================================
|
||||
// Heavy functions
|
||||
|
||||
std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) {
|
||||
std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE {
|
||||
// See also _VL_VINT_TO_STRING
|
||||
char destout[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
int obits = lwords * VL_WORDSIZE;
|
||||
@@ -1281,11 +1398,53 @@ std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) {
|
||||
//===========================================================================
|
||||
// Verilated:: Methods
|
||||
|
||||
const char* Verilated::catName(const char* n1, const char* n2) {
|
||||
Verilated::ThreadLocal::ThreadLocal()
|
||||
:
|
||||
#ifdef VL_THREADED
|
||||
t_trainId(0),
|
||||
#endif
|
||||
t_dpiScopep(NULL), t_dpiFilename(0), t_dpiLineno(0) {
|
||||
}
|
||||
Verilated::ThreadLocal::~ThreadLocal() {
|
||||
}
|
||||
|
||||
void Verilated::debug(int val) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_debug = val;
|
||||
if (val) {
|
||||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on. Message prefix indicates {<thread>,<sequence_number>}.\n"););
|
||||
#else
|
||||
VL_PRINTF_MT("- Verilated::debug attempted, but compiled without VL_DEBUG, so messages suppressed.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void Verilated::randReset(int val) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_randReset = val;
|
||||
}
|
||||
void Verilated::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_calcUnusedSigs = flag;
|
||||
}
|
||||
void Verilated::gotFinish(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_gotFinish = flag;
|
||||
}
|
||||
void Verilated::assertOn(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_assertOn = flag;
|
||||
}
|
||||
void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_fatalOnVpiError = flag;
|
||||
}
|
||||
|
||||
const char* Verilated::catName(const char* n1, const char* n2) VL_MT_SAFE {
|
||||
// Returns new'ed data
|
||||
// Used by symbol table creation to make module names
|
||||
static char* strp = NULL;
|
||||
static size_t len = 0;
|
||||
static VL_THREAD_LOCAL char* strp = NULL;
|
||||
static VL_THREAD_LOCAL size_t len = 0;
|
||||
size_t newlen = strlen(n1)+strlen(n2)+2;
|
||||
if (!strp || newlen > len) {
|
||||
if (strp) delete [] strp;
|
||||
@@ -1298,22 +1457,29 @@ const char* Verilated::catName(const char* n1, const char* n2) {
|
||||
return strp;
|
||||
}
|
||||
|
||||
void Verilated::flushCb(VerilatedVoidCb cb) {
|
||||
void Verilated::flushCb(VerilatedVoidCb cb) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
if (s_flushCb == cb) {} // Ok - don't duplicate
|
||||
else if (!s_flushCb) { s_flushCb=cb; }
|
||||
else {
|
||||
// Someday we may allow multiple callbacks ala atexit(), but until then
|
||||
vl_fatal("unknown",0,"", "Verilated::flushCb called twice with different callbacks");
|
||||
VL_FATAL_MT("unknown",0,"", "Verilated::flushCb called twice with different callbacks");
|
||||
}
|
||||
}
|
||||
|
||||
void Verilated::commandArgs(int argc, const char** argv) {
|
||||
void Verilated::flushCall() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
if (s_flushCb) (*s_flushCb)();
|
||||
}
|
||||
|
||||
void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_args.argc = argc;
|
||||
s_args.argv = argv;
|
||||
VerilatedImp::commandArgs(argc,argv);
|
||||
}
|
||||
|
||||
const char* Verilated::commandArgsPlusMatch(const char* prefixp) {
|
||||
const char* Verilated::commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE {
|
||||
const std::string& match = VerilatedImp::argPlusMatch(prefixp);
|
||||
static VL_THREAD_LOCAL char outstr[VL_VALUE_STRING_MAX_WIDTH];
|
||||
if (match == "") return "";
|
||||
@@ -1322,26 +1488,77 @@ const char* Verilated::commandArgsPlusMatch(const char* prefixp) {
|
||||
return outstr;
|
||||
}
|
||||
|
||||
void Verilated::internalsDump() {
|
||||
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
||||
// Slowpath - Called only when signal sets too high of a bit
|
||||
std::string msg = (std::string("Testbench C set input '")
|
||||
+ signame
|
||||
+ "' to value that overflows what the signal's width can fit");
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
}
|
||||
|
||||
void Verilated::quiesce() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
// Wait until all threads under this evaluation are quiet
|
||||
// THREADED-TODO
|
||||
#endif
|
||||
}
|
||||
|
||||
void Verilated::internalsDump() VL_MT_SAFE {
|
||||
VerilatedImp::internalsDump();
|
||||
}
|
||||
|
||||
void Verilated::scopesDump() {
|
||||
void Verilated::scopesDump() VL_MT_SAFE {
|
||||
VerilatedImp::scopesDump();
|
||||
}
|
||||
|
||||
const VerilatedScope* Verilated::scopeFind(const char* namep) {
|
||||
void Verilated::numThreads(unsigned threads) VL_MT_SAFE {
|
||||
VerilatedImp::numThreads(threads);
|
||||
}
|
||||
unsigned Verilated::numThreads() VL_MT_SAFE {
|
||||
return VerilatedImp::numThreads();
|
||||
}
|
||||
void Verilated::spawnThreads() VL_MT_SAFE {
|
||||
VerilatedImp::spawnThreads();
|
||||
}
|
||||
|
||||
const VerilatedScope* Verilated::scopeFind(const char* namep) VL_MT_SAFE {
|
||||
return VerilatedImp::scopeFind(namep);
|
||||
}
|
||||
|
||||
int Verilated::exportFuncNum(const char* namep) {
|
||||
int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE {
|
||||
return VerilatedImp::exportFind(namep);
|
||||
}
|
||||
|
||||
const VerilatedScopeNameMap* Verilated::scopeNameMap() {
|
||||
const VerilatedScopeNameMap* Verilated::scopeNameMap() VL_MT_SAFE {
|
||||
return VerilatedImp::scopeNameMap();
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
void Verilated::endOfThreadTrainGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("End of thread train\n"););
|
||||
VerilatedThreadMsgQueue::flush(evalMsgQp);
|
||||
}
|
||||
|
||||
void Verilated::endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n"););
|
||||
evalMsgQp->process();
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VerilatedSyms:: Methods
|
||||
|
||||
VerilatedSyms::VerilatedSyms() {
|
||||
#ifdef VL_THREADED
|
||||
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
|
||||
#endif
|
||||
}
|
||||
VerilatedSyms::~VerilatedSyms() {
|
||||
#ifdef VL_THREADED
|
||||
delete __Vm_evalMsgQp;
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedModule:: Methods
|
||||
|
||||
@@ -1350,6 +1567,7 @@ VerilatedModule::VerilatedModule(const char* namep)
|
||||
}
|
||||
|
||||
VerilatedModule::~VerilatedModule() {
|
||||
// Memory cleanup - not called during normal operation
|
||||
if (m_namep) { free((void*)m_namep); m_namep=NULL; }
|
||||
}
|
||||
|
||||
@@ -1383,6 +1601,7 @@ VerilatedScope::VerilatedScope() {
|
||||
}
|
||||
|
||||
VerilatedScope::~VerilatedScope() {
|
||||
// Memory cleanup - not called during normal operation
|
||||
VerilatedImp::scopeErase(this);
|
||||
if (m_namep) { delete [] m_namep; m_namep = NULL; }
|
||||
if (m_callbacksp) { delete [] m_callbacksp; m_callbacksp = NULL; }
|
||||
@@ -1390,7 +1609,7 @@ VerilatedScope::~VerilatedScope() {
|
||||
m_funcnumMax = 0; // Force callback table to empty
|
||||
}
|
||||
|
||||
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) {
|
||||
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) VL_MT_UNSAFE {
|
||||
// Slowpath - called once/scope at construction
|
||||
// We don't want the space and reference-count access overhead of strings.
|
||||
m_symsp = symsp;
|
||||
@@ -1402,7 +1621,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const
|
||||
VerilatedImp::scopeInsert(this);
|
||||
}
|
||||
|
||||
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) {
|
||||
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {
|
||||
// Slowpath - called once/scope*export at construction
|
||||
// Insert a exported function into scope table
|
||||
int funcnum = VerilatedImp::exportInsert(namep);
|
||||
@@ -1412,7 +1631,7 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) {
|
||||
if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum+1; }
|
||||
} else {
|
||||
if (VL_UNLIKELY(funcnum >= m_funcnumMax)) {
|
||||
vl_fatal(__FILE__,__LINE__,"","Internal: Bad funcnum vs. pre-finalize maximum");
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"","Internal: Bad funcnum vs. pre-finalize maximum");
|
||||
}
|
||||
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
|
||||
m_callbacksp = new void* [m_funcnumMax];
|
||||
@@ -1423,7 +1642,7 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) {
|
||||
}
|
||||
|
||||
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
||||
VerilatedVarType vltype, int vlflags, int dims, ...) {
|
||||
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE {
|
||||
// Grab dimensions
|
||||
// In the future we may just create a large table at emit time and statically construct from that.
|
||||
if (!finalize) return;
|
||||
@@ -1445,7 +1664,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
||||
} else {
|
||||
// We could have a linked list of ranges, but really this whole thing needs
|
||||
// to be generalized to support structs and unions, etc.
|
||||
vl_fatal(__FILE__,__LINE__,"",(std::string("Unsupported multi-dimensional public varInsert: ")+namep).c_str());
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",(std::string("Unsupported multi-dimensional public varInsert: ")+namep).c_str());
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
@@ -1454,7 +1673,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
||||
}
|
||||
|
||||
// cppcheck-suppress unusedFunction // Used by applications
|
||||
VerilatedVar* VerilatedScope::varFind(const char* namep) const {
|
||||
VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTINIT {
|
||||
if (VL_LIKELY(m_varsp)) {
|
||||
VerilatedVarNameMap::iterator it = m_varsp->find(namep);
|
||||
if (VL_LIKELY(it != m_varsp->end())) {
|
||||
@@ -1464,12 +1683,12 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* VerilatedScope::exportFindNullError(int funcnum) {
|
||||
void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
std::string msg = (std::string("Testbench C called '")
|
||||
+VerilatedImp::exportName(funcnum)
|
||||
+"' but scope wasn't set, perhaps due to dpi import call without 'context'");
|
||||
vl_fatal("unknown",0,"", msg.c_str());
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1479,24 +1698,34 @@ void* VerilatedScope::exportFindError(int funcnum) const {
|
||||
+VerilatedImp::exportName(funcnum)
|
||||
+"' but this DPI export function exists only in other scopes, not scope '"
|
||||
+name()+"'");
|
||||
vl_fatal("unknown",0,"", msg.c_str());
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VerilatedScope::scopeDump() const {
|
||||
VL_PRINTF(" SCOPE %p: %s\n", this, name());
|
||||
VL_PRINTF_MT(" SCOPE %p: %s\n", this, name());
|
||||
for (int i=0; i<m_funcnumMax; ++i) {
|
||||
if (m_callbacksp && m_callbacksp[i]) {
|
||||
VL_PRINTF(" DPI-EXPORT %p: %s\n",
|
||||
m_callbacksp[i], VerilatedImp::exportName(i));
|
||||
VL_PRINTF_MT(" DPI-EXPORT %p: %s\n",
|
||||
m_callbacksp[i], VerilatedImp::exportName(i));
|
||||
}
|
||||
}
|
||||
if (VerilatedVarNameMap* varsp = this->varsp()) {
|
||||
for (VerilatedVarNameMap::const_iterator it = varsp->begin();
|
||||
it != varsp->end(); ++it) {
|
||||
VL_PRINTF(" VAR %p: %s\n", &(it->second), it->first);
|
||||
VL_PRINTF_MT(" VAR %p: %s\n", &(it->second), it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedOneThreaded:: Methods
|
||||
|
||||
#ifdef VL_THREADED
|
||||
void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Routine called that is single threaded, but called from"
|
||||
" a different thread then the expected constructing thread");
|
||||
}
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
|
||||
+400
-225
File diff suppressed because it is too large
Load Diff
@@ -109,6 +109,16 @@ ifeq ($(VM_SC),1)
|
||||
endif
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
##### Threaded builds
|
||||
|
||||
ifneq ($(VM_THREADS),0)
|
||||
ifneq ($(VM_THREADS),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
CPPFLAGS += -DVL_THREADED $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
endif
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
##### Stub
|
||||
|
||||
|
||||
+63
-47
@@ -96,13 +96,14 @@ private:
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
ValueIndexMap m_valueIndexes; ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues; ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items; ///< List of all items
|
||||
VerilatedMutex m_mutex; ///< Protects all members, when VL_THREADED. Wrapper deals with setting it.
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); ///< List of all items
|
||||
|
||||
VerilatedCovImpItem* m_insertp; ///< Item about to insert
|
||||
const char* m_insertFilenamep; ///< Filename about to insert
|
||||
int m_insertLineno; ///< Line number about to insert
|
||||
VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex); ///< Item about to insert
|
||||
const char* m_insertFilenamep VL_GUARDED_BY(m_mutex); ///< Filename about to insert
|
||||
int m_insertLineno VL_GUARDED_BY(m_mutex); ///< Line number about to insert
|
||||
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp() {
|
||||
@@ -110,16 +111,17 @@ private:
|
||||
m_insertFilenamep = NULL;
|
||||
m_insertLineno = 0;
|
||||
}
|
||||
VL_UNCOPYABLE(VerilatedCovImp);
|
||||
public:
|
||||
~VerilatedCovImp() { clear(); }
|
||||
static VerilatedCovImp& imp() {
|
||||
~VerilatedCovImp() { clearGuts(); }
|
||||
static VerilatedCovImp& imp() VL_MT_SAFE {
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
int valueIndex(const std::string& value) {
|
||||
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
|
||||
static int nextIndex = KEY_UNDEF+1;
|
||||
ValueIndexMap::iterator iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
@@ -128,7 +130,7 @@ private:
|
||||
m_indexValues.insert(std::make_pair(nextIndex, value));
|
||||
return nextIndex;
|
||||
}
|
||||
static std::string dequote(const std::string& text) {
|
||||
static std::string dequote(const std::string& text) VL_PURE {
|
||||
// Quote any special characters
|
||||
std::string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; ++pos) {
|
||||
@@ -141,7 +143,7 @@ private:
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
static bool legalKey(const std::string& key) {
|
||||
static bool legalKey(const std::string& key) VL_PURE {
|
||||
// Because we compress long keys to a single letter, and
|
||||
// don't want applications to either get confused if they use
|
||||
// a letter differently, nor want them to rely on our compression...
|
||||
@@ -150,7 +152,7 @@ private:
|
||||
if (key.length()==2 && isdigit(key[1])) return false;
|
||||
return true;
|
||||
}
|
||||
static std::string keyValueFormatter (const std::string& key, const std::string& value) {
|
||||
static std::string keyValueFormatter(const std::string& key, const std::string& value) VL_PURE {
|
||||
std::string name;
|
||||
if (key.length()==1 && isalpha(key[0])) {
|
||||
name += std::string("\001")+key;
|
||||
@@ -160,7 +162,7 @@ private:
|
||||
name += std::string("\002")+dequote(value);
|
||||
return name;
|
||||
}
|
||||
static std::string combineHier (const std::string& old, const std::string& add) {
|
||||
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
|
||||
// (foo.a.x, foo.b.x) => foo.*.x
|
||||
// (foo.a.x, foo.b.y) => foo.*
|
||||
// (foo.a.x, foo.b) => foo.*
|
||||
@@ -194,7 +196,7 @@ private:
|
||||
//cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add<<"\nch o="<<out<<endl;
|
||||
return out;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) {
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) VL_REQUIRES(m_mutex) {
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
@@ -206,21 +208,18 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void selftest() {
|
||||
static void selftest() VL_MT_SAFE {
|
||||
// Little selftest
|
||||
if (combineHier ("a.b.c","a.b.c") !="a.b.c") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.b.c","a.b") !="a.b*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.x.c","a.y.c") !="a.*.c") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.z.z.z.c","a.b.c") !="a.*.c") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("z","a") !="*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.a","q.b") !="q.*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.za","q.zb") !="q.z*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("1.2.3.a","9.8.7.a") !="*.a") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.b.c","a.b.c") !="a.b.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.b.c","a.b") !="a.b*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.x.c","a.y.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.z.z.z.c","a.b.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("z","a") !="*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.a","q.b") !="q.*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.za","q.zb") !="q.z*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("1.2.3.a","9.8.7.a") !="*.a") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
}
|
||||
|
||||
public:
|
||||
// PUBLIC METHODS
|
||||
void clear() {
|
||||
void clearGuts() VL_REQUIRES(m_mutex) {
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
delete itemp;
|
||||
@@ -229,7 +228,17 @@ public:
|
||||
m_indexValues.clear();
|
||||
m_valueIndexes.clear();
|
||||
}
|
||||
void clearNonMatch (const char* matchp) {
|
||||
|
||||
public:
|
||||
// PUBLIC METHODS
|
||||
void clear() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
clearGuts();
|
||||
}
|
||||
void clearNonMatch(const char* matchp) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
@@ -243,23 +252,28 @@ public:
|
||||
m_items = newlist;
|
||||
}
|
||||
}
|
||||
void zero() {
|
||||
void zero() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
(*it)->zero();
|
||||
}
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti (VerilatedCovImpItem* itemp) {
|
||||
void inserti (VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf (const char* filenamep, int lineno) {
|
||||
void insertf (const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp (const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) {
|
||||
const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0]="filename"; valps[0]=m_insertFilenamep;
|
||||
@@ -303,7 +317,7 @@ public:
|
||||
addKeynum++;
|
||||
if (!legalKey(key)) {
|
||||
std::string msg = "%Error: Coverage keys of one character, or letter+digit are illegal: "+key;
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,16 +326,18 @@ public:
|
||||
m_insertp = NULL;
|
||||
}
|
||||
|
||||
void write (const char* filename) {
|
||||
void write(const char* filename) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
#ifndef VM_COVERAGE
|
||||
vl_fatal("",0,"","%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
VL_FATAL_MT("",0,"","%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
std::ofstream os (filename);
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '")+filename+"'";
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
return;
|
||||
}
|
||||
os << "# SystemC::Coverage-3\n";
|
||||
@@ -385,25 +401,25 @@ public:
|
||||
//=============================================================================
|
||||
// VerilatedCov
|
||||
|
||||
void VerilatedCov::clear() {
|
||||
void VerilatedCov::clear() VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().clear();
|
||||
}
|
||||
void VerilatedCov::clearNonMatch (const char* matchp) {
|
||||
void VerilatedCov::clearNonMatch(const char* matchp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().clearNonMatch(matchp);
|
||||
}
|
||||
void VerilatedCov::zero() {
|
||||
void VerilatedCov::zero() VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().zero();
|
||||
}
|
||||
void VerilatedCov::write (const char* filenamep) {
|
||||
void VerilatedCov::write(const char* filenamep) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().write(filenamep);
|
||||
}
|
||||
void VerilatedCov::_inserti (vluint32_t* itemp) {
|
||||
void VerilatedCov::_inserti(vluint32_t* itemp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_inserti (vluint64_t* itemp) {
|
||||
void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_insertf (const char* filename, int lineno) {
|
||||
void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().insertf(filename,lineno);
|
||||
}
|
||||
|
||||
@@ -413,7 +429,7 @@ void VerilatedCov::_insertf (const char* filename, int lineno) {
|
||||
#define N(n) "","" // Null argument list
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19),
|
||||
A(20),A(21),A(22),A(23),A(24),A(25),A(26),A(27),A(28),A(29)) {
|
||||
A(20),A(21),A(22),A(23),A(24),A(25),A(26),A(27),A(28),A(29)) VL_MT_SAFE {
|
||||
const char* keyps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
key0,key1,key2,key3,key4,key5,key6,key7,key8,key9,
|
||||
@@ -428,20 +444,20 @@ void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
}
|
||||
|
||||
// And versions with fewer arguments (oh for a language with named parameters!)
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) {
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) {
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
// Backward compatibility for Verilator
|
||||
void VerilatedCov::_insertp (A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const std::string& val4, A(5),A(6)) {
|
||||
K(4),const std::string& val4, A(5),A(6)) VL_MT_SAFE {
|
||||
std::string val2str = vlCovCvtToStr(val2);
|
||||
std::string val3str = vlCovCvtToStr(val3);
|
||||
_insertp(C(0),C(1),
|
||||
|
||||
+11
-9
@@ -73,7 +73,7 @@
|
||||
//=============================================================================
|
||||
/// Convert VL_COVER_INSERT value arguments to strings
|
||||
|
||||
template< class T> std::string vlCovCvtToStr (const T& t) {
|
||||
template< class T> std::string vlCovCvtToStr (const T& t) VL_PURE {
|
||||
std::ostringstream os; os<<t; return os.str();
|
||||
}
|
||||
|
||||
@@ -82,23 +82,25 @@ template< class T> std::string vlCovCvtToStr (const T& t) {
|
||||
/// Verilator coverage global class
|
||||
////
|
||||
/// Global class with methods affecting all coverage data.
|
||||
/// All public methods in this class are thread safe.
|
||||
|
||||
class VerilatedCov {
|
||||
VL_UNCOPYABLE(VerilatedCov);
|
||||
public:
|
||||
// GLOBAL METHODS
|
||||
/// Return default filename
|
||||
static const char* defaultFilename() { return "coverage.dat"; }
|
||||
static const char* defaultFilename() VL_PURE { return "coverage.dat"; }
|
||||
/// Write all coverage data to a file
|
||||
static void write (const char* filenamep = defaultFilename());
|
||||
static void write (const char* filenamep = defaultFilename()) VL_MT_SAFE;
|
||||
/// Insert a coverage item
|
||||
/// We accept from 1-30 key/value pairs, all as strings.
|
||||
/// Call _insert1, followed by _insert2 and _insert3
|
||||
/// Do not call directly; use VL_COVER_INSERT or higher level macros instead
|
||||
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
|
||||
static void _inserti (vluint32_t* itemp);
|
||||
static void _inserti (vluint64_t* itemp);
|
||||
static void _inserti (vluint32_t* itemp) VL_MT_SAFE;
|
||||
static void _inserti (vluint64_t* itemp) VL_MT_SAFE;
|
||||
// _insert2: Set default filename and line number
|
||||
static void _insertf (const char* filename, int lineno);
|
||||
static void _insertf (const char* filename, int lineno) VL_MT_SAFE;
|
||||
// _insert3: Set parameters
|
||||
// We could have just the maximum argument version, but this compiles
|
||||
// much slower (nearly 2x) than having smaller versions also. However
|
||||
@@ -120,11 +122,11 @@ public:
|
||||
#undef A
|
||||
#undef D
|
||||
/// Clear coverage points (and call delete on all items)
|
||||
static void clear();
|
||||
static void clear() VL_MT_SAFE;
|
||||
/// Clear items not matching the provided string
|
||||
static void clearNonMatch (const char* matchp);
|
||||
static void clearNonMatch (const char* matchp) VL_MT_SAFE;
|
||||
/// Zero coverage points
|
||||
static void zero();
|
||||
static void zero() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -63,12 +63,6 @@ VLCOVGEN_ITEM("name=>'row2', short=>'r2', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'row3', short=>'r3', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'weight', short=>'w', group=>0, default=>undef, descr=>'For totaling items, weight of this item'")
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovKey
|
||||
/// Verilator coverage global class
|
||||
////
|
||||
/// Global class with methods affecting all coverage data.
|
||||
|
||||
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
|
||||
#define VL_CIK_COL0 "c0"
|
||||
#define VL_CIK_COL0_NAME "C0"
|
||||
@@ -102,9 +96,14 @@ VLCOVGEN_ITEM("name=>'weight', short=>'w', group=>0, default=>undef, descr
|
||||
#define VL_CIK_WEIGHT "w"
|
||||
// VLCOVGEN_CIK_AUTO_EDIT_END
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovKey
|
||||
/// Verilator coverage global class.
|
||||
/// This class is thread safe.
|
||||
|
||||
class VerilatedCovKey {
|
||||
public:
|
||||
static std::string shortKey(const std::string& key) {
|
||||
static std::string shortKey(const std::string& key) VL_PURE {
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
|
||||
if (key == "col0") return VL_CIK_COL0;
|
||||
if (key == "col0_name") return VL_CIK_COL0_NAME;
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
// Not supported yet
|
||||
#define _VL_SVDPI_UNIMP() \
|
||||
vl_fatal(__FILE__,__LINE__,"",(std::string("%%Error: Unsupported DPI function: ")+VL_FUNC).c_str())
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",(std::string("%%Error: Unsupported DPI function: ")+VL_FUNC).c_str())
|
||||
|
||||
// Function requires a "context" in the import declaration
|
||||
#define _VL_SVDPI_CONTEXT_WARN() \
|
||||
VL_PRINTF("%%Warning: DPI C Function called by Verilog DPI import with missing 'context' keyword.\n");
|
||||
VL_PRINTF_MT("%%Warning: DPI C Function called by Verilog DPI import with missing 'context' keyword.\n");
|
||||
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
|
||||
@@ -35,23 +35,23 @@
|
||||
// SETTING OPERATORS
|
||||
|
||||
/// Return svBitVecVal from WData
|
||||
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, svBitVecVal* lwp) {
|
||||
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, svBitVecVal* lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words-1; ++i) owp[i]=lwp[i];
|
||||
owp[words-1] = lwp[words-1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) {
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words-1; ++i) owp[i]=lwp[i];
|
||||
owp[words-1] = lwp[words-1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, svLogicVecVal* lwp) {
|
||||
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
// Note we ignore X/Z in svLogicVecVal
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words-1; ++i) owp[i]=lwp[i].aval;
|
||||
owp[words-1] = lwp[words-1].aval & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) {
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) VL_MT_SAFE {
|
||||
// Note we don't create X/Z in svLogicVecVal
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words; ++i) owp[i].bval=0;
|
||||
|
||||
+16
-16
@@ -36,49 +36,49 @@
|
||||
//======================================================================
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp);
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) {
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
WData lw[2]; VL_SET_WQ(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(2, lw);
|
||||
}
|
||||
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) {
|
||||
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE {
|
||||
return lhs;
|
||||
}
|
||||
inline std::string VL_CVT_PACK_STR_NI(IData lhs) {
|
||||
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
|
||||
WData lw[1]; lw[0] = lhs;
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) {
|
||||
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
|
||||
return lhs+rhs;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNQ(int,int,int, const std::string& lhs, IData rep) {
|
||||
inline std::string VL_REPLICATEN_NNQ(int,int,int, const std::string& lhs, IData rep) VL_PURE {
|
||||
std::string out; out.reserve(lhs.length() * rep);
|
||||
for (unsigned times=0; times<rep; ++times) out += lhs;
|
||||
return out;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNI(int obits,int lbits,int rbits, const std::string& lhs, IData rep) {
|
||||
inline std::string VL_REPLICATEN_NNI(int obits,int lbits,int rbits, const std::string& lhs, IData rep) VL_PURE {
|
||||
return VL_REPLICATEN_NNQ(obits,lbits,rbits,lhs,rep);
|
||||
}
|
||||
|
||||
extern IData VL_FOPEN_NI(const std::string& filename, IData mode);
|
||||
extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
const std::string& ofilename, void* memp, IData start, IData end);
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...);
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string &output, const char* formatp, ...);
|
||||
extern std::string VL_SFORMATF_NX(const char* formatp, ...);
|
||||
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rdp);
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) {
|
||||
const std::string& ofilename, void* memp, IData start, IData end) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string &output, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
|
||||
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rdp) VL_MT_SAFE;
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2]; // WData must always be at least 2
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits,ld,rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) {
|
||||
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2];
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits,ld,rwp);
|
||||
if (got) rdr = VL_SET_QW(rwp);
|
||||
return got;
|
||||
}
|
||||
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr);
|
||||
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+241
-49
@@ -34,14 +34,131 @@
|
||||
#include "verilated_syms.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#ifdef VL_THREADED
|
||||
# include <functional>
|
||||
# include <queue>
|
||||
#endif
|
||||
|
||||
class VerilatedScope;
|
||||
|
||||
//======================================================================
|
||||
// Types
|
||||
// Threaded message passing
|
||||
|
||||
#ifdef VL_THREADED
|
||||
/// Message, enqueued on a train, and consumed on the main eval thread
|
||||
class VerilatedMsg {
|
||||
public:
|
||||
// TYPES
|
||||
struct Cmp {
|
||||
bool operator() (const VerilatedMsg& a, const VerilatedMsg& b) {
|
||||
return a.trainId() < b.trainId(); }
|
||||
};
|
||||
private:
|
||||
// MEMBERS
|
||||
vluint32_t m_trainId; ///< Train that did enqueue
|
||||
std::function<void()> m_cb; ///< Lambda to execute when message received
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedMsg(const std::function<void()>& cb)
|
||||
: m_trainId(Verilated::trainId()), m_cb(cb) {}
|
||||
~VerilatedMsg() {}
|
||||
// METHODS
|
||||
vluint32_t trainId() const { return m_trainId; }
|
||||
/// Execute the lambda function
|
||||
void run() const { m_cb(); }
|
||||
};
|
||||
|
||||
/// Each thread has a queue it pushes to
|
||||
/// This assumes no thread starts pushing the next tick until the previous has drained.
|
||||
/// If more aggressiveness is needed, a double-buffered scheme might work well.
|
||||
class VerilatedEvalMsgQueue {
|
||||
typedef std::multiset<VerilatedMsg, VerilatedMsg::Cmp> VerilatedThreadQueue;
|
||||
|
||||
std::atomic<vluint64_t> m_depth; ///< Current depth of queue (see comments below)
|
||||
|
||||
VerilatedMutex m_mutex; ///< Mutex protecting queue
|
||||
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); ///< Message queue
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedEvalMsgQueue() : m_depth(0) { }
|
||||
~VerilatedEvalMsgQueue() { }
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedEvalMsgQueue);
|
||||
public:
|
||||
// METHODS
|
||||
//// Add message to queue (called by producer)
|
||||
void post(const VerilatedMsg& msg) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::endOfEvalReqdInc(); // No mutex, threadsafe
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
m_queue.insert(msg); // Pass by value to copy the message into queue
|
||||
++m_depth;
|
||||
}
|
||||
/// Service queue until completion (called by consumer)
|
||||
void process() VL_EXCLUDES(m_mutex) {
|
||||
// Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size,
|
||||
// but on the reader side it's 4x faster to test an atomic then getting a mutex
|
||||
while (m_depth) {
|
||||
// Wait for a message to be added to the queue
|
||||
// We don't use unique_lock as want to unlock with the message copy still in scope
|
||||
m_mutex.lock();
|
||||
assert(!m_queue.empty()); // Otherwise m_depth is wrong
|
||||
// Unfortunately to release the lock we need to copy the message
|
||||
// (Or have the message be a pointer, but then new/delete cost on each message)
|
||||
// We assume messages are small, so copy
|
||||
auto it = m_queue.begin();
|
||||
const VerilatedMsg msg = *(it);
|
||||
m_queue.erase(it);
|
||||
m_mutex.unlock();
|
||||
m_depth--; // Ok if outside critical section as only this code checks the value
|
||||
Verilated::endOfEvalReqdDec(); // No mutex, threadsafe
|
||||
{
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from trainId=%d\n", msg.trainId()););
|
||||
msg.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Each thread has a local queue to build up messages until the end of the eval() call
|
||||
class VerilatedThreadMsgQueue {
|
||||
std::queue<VerilatedMsg> m_queue;
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedThreadMsgQueue() { }
|
||||
~VerilatedThreadMsgQueue() {
|
||||
// The only call of this with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
}
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
|
||||
// METHODS
|
||||
static VerilatedThreadMsgQueue& threadton() {
|
||||
static VL_THREAD_LOCAL VerilatedThreadMsgQueue t_s;
|
||||
return t_s;
|
||||
}
|
||||
public:
|
||||
/// Add message to queue, called by producer
|
||||
static void post(const VerilatedMsg& msg) VL_MT_SAFE {
|
||||
Verilated::endOfEvalReqdInc();
|
||||
threadton().m_queue.push(msg); // Pass by value to copy the message into queue
|
||||
}
|
||||
/// Push all messages to the eval's queue
|
||||
static void flush(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
while (!threadton().m_queue.empty()) {
|
||||
evalMsgQp->post(threadton().m_queue.front());
|
||||
threadton().m_queue.pop();
|
||||
Verilated::endOfEvalReqdDec();
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif // VL_THREADED
|
||||
|
||||
//======================================================================
|
||||
// VerilatedImp
|
||||
|
||||
class VerilatedImp {
|
||||
// Whole class is internal use only - Global information shared between verilated*.cpp files.
|
||||
@@ -56,58 +173,77 @@ class VerilatedImp {
|
||||
|
||||
// Nothing here is save-restored; users expected to re-register appropriately
|
||||
|
||||
ArgVec m_argVec; ///< Argument list (NOT save-restored, may want different results)
|
||||
bool m_argVecLoaded; ///< Ever loaded argument list
|
||||
UserMap m_userMap; ///< Map of <(scope,userkey), userData>
|
||||
VerilatedScopeNameMap m_nameMap; ///< Map of <scope_name, scope pointer>
|
||||
VerilatedMutex m_argMutex; ///< Protect m_argVec, m_argVecLoaded
|
||||
ArgVec m_argVec VL_GUARDED_BY(m_argMutex); ///< Argument list (NOT save-restored, may want different results)
|
||||
bool m_argVecLoaded VL_GUARDED_BY(m_argMutex); ///< Ever loaded argument list
|
||||
|
||||
VerilatedMutex m_userMapMutex; ///< Protect m_userMap
|
||||
UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData>
|
||||
|
||||
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
|
||||
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); ///< Map of <scope_name, scope pointer>
|
||||
// Slow - somewhat static:
|
||||
ExportNameMap m_exportMap; ///< Map of <export_func_proto, func number>
|
||||
int m_exportNext; ///< Next export funcnum
|
||||
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
|
||||
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of <export_func_proto, func number>
|
||||
int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum
|
||||
|
||||
// File I/O
|
||||
std::vector<FILE*> m_fdps; ///< File descriptors
|
||||
std::deque<IData> m_fdFree; ///< List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree
|
||||
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors
|
||||
std::deque<IData> m_fdFree VL_GUARDED_BY(m_fdMutex); ///< List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
|
||||
// Threads
|
||||
VerilatedMutex m_threadMutex; ///< Protect m_numThreads, etc
|
||||
bool m_spawned VL_GUARDED_BY(m_threadMutex); ///< Already called spawnThreads()
|
||||
unsigned m_numThreads VL_GUARDED_BY(m_threadMutex); ///< Number of threads user requested, 0x0=all cpus
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS
|
||||
VerilatedImp() : m_argVecLoaded(false), m_exportNext(0) {
|
||||
VerilatedImp()
|
||||
: m_argVecLoaded(false), m_exportNext(0), m_spawned(false), m_numThreads(0) {
|
||||
m_fdps.resize(3);
|
||||
m_fdps[0] = stdin;
|
||||
m_fdps[1] = stdout;
|
||||
m_fdps[2] = stderr;
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
static void internalsDump() {
|
||||
VL_PRINTF("internalsDump:\n");
|
||||
VL_PRINTF(" Argv:");
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedImp);
|
||||
public:
|
||||
static void internalsDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
VL_PRINTF_MT("internalsDump:\n");
|
||||
VL_PRINTF_MT(" Argv:");
|
||||
for (ArgVec::const_iterator it=s_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
|
||||
VL_PRINTF(" %s",it->c_str());
|
||||
VL_PRINTF_MT(" %s",it->c_str());
|
||||
}
|
||||
VL_PRINTF("\n");
|
||||
VL_PRINTF(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
|
||||
VL_PRINTF_MT("\n");
|
||||
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
|
||||
scopesDump();
|
||||
exportsDump();
|
||||
userDump();
|
||||
}
|
||||
|
||||
// METHODS - arguments
|
||||
static void commandArgs(int argc, const char** argv) {
|
||||
public:
|
||||
static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
s_s.m_argVec.clear(); // Always clear
|
||||
commandArgsAdd(argc, argv);
|
||||
commandArgsAddGuts(argc, argv);
|
||||
}
|
||||
static void commandArgsAdd(int argc, const char** argv) {
|
||||
if (!s_s.m_argVecLoaded) s_s.m_argVec.clear();
|
||||
for (int i=0; i<argc; ++i) s_s.m_argVec.push_back(argv[i]);
|
||||
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
commandArgsAddGuts(argc, argv);
|
||||
}
|
||||
static std::string argPlusMatch(const char* prefixp) {
|
||||
static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
// Note prefixp does not include the leading "+"
|
||||
size_t len = strlen(prefixp);
|
||||
if (VL_UNLIKELY(!s_s.m_argVecLoaded)) {
|
||||
s_s.m_argVecLoaded = true; // Complain only once
|
||||
vl_fatal("unknown",0,"",
|
||||
"%Error: Verilog called $test$plusargs or $value$plusargs without"
|
||||
" testbench C first calling Verilated::commandArgs(argc,argv).");
|
||||
VL_FATAL_MT("unknown",0,"",
|
||||
"%Error: Verilog called $test$plusargs or $value$plusargs without"
|
||||
" testbench C first calling Verilated::commandArgs(argc,argv).");
|
||||
}
|
||||
for (ArgVec::const_iterator it=s_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
|
||||
if ((*it)[0]=='+') {
|
||||
@@ -116,26 +252,36 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private:
|
||||
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex) {
|
||||
if (!s_s.m_argVecLoaded) s_s.m_argVec.clear();
|
||||
for (int i=0; i<argc; ++i) s_s.m_argVec.push_back(argv[i]);
|
||||
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
// We implement this as a single large map instead of one map per scope
|
||||
// There's often many more scopes than userdata's and thus having a ~48byte
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) {
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex);
|
||||
UserMap::iterator it=s_s.m_userMap.find(std::make_pair(scopep,userKey));
|
||||
if (it != s_s.m_userMap.end()) it->second = userData;
|
||||
// When we support VL_THREADs, we need a lock around this insert, as it's runtime
|
||||
else s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep,userKey),userData));
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) {
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex);
|
||||
UserMap::const_iterator it=s_s.m_userMap.find(std::make_pair(scopep,userKey));
|
||||
if (VL_LIKELY(it != s_s.m_userMap.end())) return it->second;
|
||||
else return NULL;
|
||||
}
|
||||
private:
|
||||
/// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) {
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex);
|
||||
for (UserMap::iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ) {
|
||||
if (it->first.first == scopep) {
|
||||
s_s.m_userMap.erase(it++);
|
||||
@@ -144,44 +290,50 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
static void userDump() {
|
||||
static void userDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex); // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (UserMap::const_iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF(" userDump:\n"); first=false; }
|
||||
VL_PRINTF(" DPI_USER_DATA scope %p key %p: %p\n",
|
||||
if (first) { VL_PRINTF_MT(" userDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n",
|
||||
it->first.first, it->first.second, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - scope name
|
||||
static void scopeInsert(const VerilatedScope* scopep) {
|
||||
static void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at construction
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it == s_s.m_nameMap.end()) {
|
||||
s_s.m_nameMap.insert(it, std::make_pair(scopep->name(),scopep));
|
||||
}
|
||||
}
|
||||
static inline const VerilatedScope* scopeFind(const char* namep) {
|
||||
static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex); // If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
||||
VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_nameMap.end())) return it->second;
|
||||
else return NULL;
|
||||
}
|
||||
static void scopeErase(const VerilatedScope* scopep) {
|
||||
static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at destruction
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex);
|
||||
userEraseScope(scopep);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it);
|
||||
}
|
||||
static void scopesDump() {
|
||||
VL_PRINTF(" scopesDump:\n");
|
||||
static void scopesDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex);
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.begin(); it!=s_s.m_nameMap.end(); ++it) {
|
||||
const VerilatedScope* scopep = it->second;
|
||||
scopep->scopeDump();
|
||||
}
|
||||
VL_PRINTF("\n");
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
static const VerilatedScopeNameMap* scopeNameMap() {
|
||||
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE_POSTINIT {
|
||||
// Thread save only assuming this is called only after model construction completed
|
||||
return &s_s.m_nameMap;
|
||||
}
|
||||
|
||||
@@ -194,8 +346,9 @@ public: // But only for verilated*.cpp
|
||||
// in the design that also happen to have our same callback function.
|
||||
// Rather than a 2D map, the integer scheme saves 500ish ns on a likely
|
||||
// miss at the cost of a multiply, and all lookups move to slowpath.
|
||||
static int exportInsert(const char* namep) {
|
||||
static int exportInsert(const char* namep) VL_MT_SAFE {
|
||||
// Slow ok - called once/function at creation
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
ExportNameMap::iterator it=s_s.m_exportMap.find(namep);
|
||||
if (it == s_s.m_exportMap.end()) {
|
||||
s_s.m_exportMap.insert(it, std::make_pair(namep, s_s.m_exportNext++));
|
||||
@@ -204,26 +357,29 @@ public: // But only for verilated*.cpp
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
static int exportFind(const char* namep) {
|
||||
static int exportFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
ExportNameMap::const_iterator it=s_s.m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ")+namep
|
||||
+" but no such DPI export function name exists in ANY model");
|
||||
vl_fatal("unknown",0,"", msg.c_str());
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
static const char* exportName(int funcnum) {
|
||||
static const char* exportName(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath; find name for given export; errors only so no map to reverse-map it
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (it->second == funcnum) return it->first;
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
}
|
||||
static void exportsDump() {
|
||||
static void exportsDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
bool first = true;
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF(" exportDump:\n"); first=false; }
|
||||
VL_PRINTF(" DPI_EXPORT_NAME %05d: %s\n", it->second, it->first);
|
||||
if (first) { VL_PRINTF_MT(" exportDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_EXPORT_NAME %05d: %s\n", it->second, it->first);
|
||||
}
|
||||
}
|
||||
// We don't free up m_exportMap until the end, because we can't be sure
|
||||
@@ -231,9 +387,10 @@ public: // But only for verilated*.cpp
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
static IData fdNew(FILE* fp) {
|
||||
static IData fdNew(FILE* fp) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
VerilatedLockGuard guard(s_s.m_fdMutex);
|
||||
if (s_s.m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
size_t start = s_s.m_fdps.size();
|
||||
@@ -244,18 +401,53 @@ public: // But only for verilated*.cpp
|
||||
s_s.m_fdps[idx] = fp;
|
||||
return (idx | (1UL<<31)); // bit 31 indicates not MCD
|
||||
}
|
||||
static void fdDelete(IData fdi) {
|
||||
static void fdDelete(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard guard(s_s.m_fdMutex);
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free
|
||||
s_s.m_fdps[idx] = NULL;
|
||||
s_s.m_fdFree.push_back(idx);
|
||||
}
|
||||
static inline FILE* fdToFp(IData fdi) {
|
||||
static inline FILE* fdToFp(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard guard(s_s.m_fdMutex); // This might get slow, if it does we can cache it
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return NULL;
|
||||
return s_s.m_fdps[idx];
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - Threading
|
||||
static void numThreads(unsigned threads) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_threadMutex);
|
||||
if (!s_s.m_spawned) s_s.m_numThreads = threads;
|
||||
}
|
||||
static unsigned numThreads() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_threadMutex);
|
||||
#ifdef VL_THREADED
|
||||
unsigned threads = s_s.m_numThreads;
|
||||
if (threads == 0x0) {
|
||||
threads = std::thread::hardware_concurrency(); // Or 0=unknown, C++11
|
||||
}
|
||||
if (threads<1) threads = 1;
|
||||
return threads;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
static void spawnThreads() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_threadMutex);
|
||||
if (!s_s.m_spawned) {
|
||||
// Convert numThreads from 0 to the spawned number
|
||||
numThreads(numThreads());
|
||||
s_s.m_spawned = true;
|
||||
#ifdef VL_THREADED
|
||||
// THREADED-TODO startup threads
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+23
-19
@@ -48,7 +48,7 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; ///< Value of last by
|
||||
//=============================================================================
|
||||
// Searalization
|
||||
|
||||
bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t size) {
|
||||
bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
bufferCheck();
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
vluint8_t miss = 0;
|
||||
@@ -58,17 +58,17 @@ bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t siz
|
||||
return (miss!=0);
|
||||
}
|
||||
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) {
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap,size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize save-restore file as was made from different model");
|
||||
vl_fatal(fn.c_str(), 0, "", msg.c_str());
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
|
||||
void VerilatedSerialize::header() {
|
||||
void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedSerialize& os = *this; // So can cut and paste standard << code below
|
||||
assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned
|
||||
os.write(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR));
|
||||
@@ -78,29 +78,29 @@ void VerilatedSerialize::header() {
|
||||
os.write(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
}
|
||||
|
||||
void VerilatedDeserialize::header() {
|
||||
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong header signature");
|
||||
vl_fatal(fn.c_str(), 0, "", msg.c_str());
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
os.read(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
}
|
||||
|
||||
void VerilatedSerialize::trailer() {
|
||||
void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedSerialize& os = *this; // So can cut and paste standard << code below
|
||||
assert((strlen(VLTSAVE_TRAILER_STR) & 7) == 0); // Keep aligned
|
||||
os.write(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR));
|
||||
}
|
||||
|
||||
void VerilatedDeserialize::trailer() {
|
||||
void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature");
|
||||
vl_fatal(fn.c_str(), 0, "", msg.c_str());
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
}
|
||||
@@ -110,9 +110,10 @@ void VerilatedDeserialize::trailer() {
|
||||
//=============================================================================
|
||||
// Opening/Closing
|
||||
|
||||
void VerilatedSave::open (const char* filenamep) {
|
||||
void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_PRINTF("-vltSave: opening save file %s\n",filenamep););
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n",filenamep););
|
||||
|
||||
if (filenamep[0]=='|') {
|
||||
assert(0); // Not supported yet.
|
||||
@@ -132,9 +133,10 @@ void VerilatedSave::open (const char* filenamep) {
|
||||
header();
|
||||
}
|
||||
|
||||
void VerilatedRestore::open (const char* filenamep) {
|
||||
void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_PRINTF("-vltRestore: opening restore file %s\n",filenamep););
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n",filenamep););
|
||||
|
||||
if (filenamep[0]=='|') {
|
||||
assert(0); // Not supported yet.
|
||||
@@ -155,7 +157,7 @@ void VerilatedRestore::open (const char* filenamep) {
|
||||
header();
|
||||
}
|
||||
|
||||
void VerilatedSave::close () {
|
||||
void VerilatedSave::close() VL_MT_UNSAFE_ONE {
|
||||
if (!isOpen()) return;
|
||||
trailer();
|
||||
flush();
|
||||
@@ -163,7 +165,7 @@ void VerilatedSave::close () {
|
||||
::close(m_fd); // May get error, just ignore it
|
||||
}
|
||||
|
||||
void VerilatedRestore::close () {
|
||||
void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
|
||||
if (!isOpen()) return;
|
||||
trailer();
|
||||
flush();
|
||||
@@ -174,7 +176,8 @@ void VerilatedRestore::close () {
|
||||
//=============================================================================
|
||||
// Buffer management
|
||||
|
||||
void VerilatedSave::flush() {
|
||||
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
vluint8_t* wp = m_bufp;
|
||||
while (1) {
|
||||
@@ -188,7 +191,7 @@ void VerilatedSave::flush() {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
close();
|
||||
break;
|
||||
}
|
||||
@@ -197,7 +200,8 @@ void VerilatedSave::flush() {
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
}
|
||||
|
||||
void VerilatedRestore::fill() {
|
||||
void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
// Move remaining characters down to start of buffer. (No memcpy, overlaps allowed)
|
||||
vluint8_t* rp = m_bufp;
|
||||
@@ -216,7 +220,7 @@ void VerilatedRestore::fill() {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
close();
|
||||
break;
|
||||
}
|
||||
|
||||
+38
-30
@@ -28,6 +28,7 @@
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedSerialize - convert structures to a stream representation
|
||||
// This class is not thread safe, it must be called by a single thread
|
||||
|
||||
class VerilatedSerialize {
|
||||
protected:
|
||||
@@ -37,14 +38,17 @@ protected:
|
||||
vluint8_t* m_bufp; ///< Output buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
inline static size_t bufferInsertSize() { return 16*1024; }
|
||||
|
||||
void header();
|
||||
void trailer();
|
||||
void header() VL_MT_UNSAFE_ONE;
|
||||
void trailer() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedSerialize);
|
||||
public:
|
||||
// CREATORS
|
||||
VerilatedSerialize() {
|
||||
m_isOpen = false;
|
||||
m_bufp = new vluint8_t [bufferSize()];
|
||||
@@ -57,9 +61,9 @@ public:
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
std::string filename() const { return m_filename; }
|
||||
virtual void close() { flush(); }
|
||||
virtual void flush() {}
|
||||
inline VerilatedSerialize& write (const void* __restrict datap, size_t size) {
|
||||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
@@ -71,8 +75,7 @@ public:
|
||||
return *this; // For function chaining
|
||||
}
|
||||
private:
|
||||
VerilatedSerialize(const VerilatedSerialize& ); ///< N/A, no copy constructor
|
||||
VerilatedSerialize& bufferCheck() {
|
||||
VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_cp > (m_bufp+(bufferSize()-bufferInsertSize())))) {
|
||||
@@ -84,6 +87,7 @@ private:
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedDeserial - load structures from a stream representation
|
||||
// This class is not thread safe, it must be called by a single thread
|
||||
|
||||
class VerilatedDeserialize {
|
||||
protected:
|
||||
@@ -94,15 +98,18 @@ protected:
|
||||
vluint8_t* m_endp; ///< Last valid byte in m_bufp buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
inline static size_t bufferInsertSize() { return 16*1024; }
|
||||
|
||||
virtual void fill() = 0;
|
||||
void header();
|
||||
void trailer();
|
||||
void header() VL_MT_UNSAFE_ONE;
|
||||
void trailer() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedDeserialize);
|
||||
public:
|
||||
// CREATORS
|
||||
VerilatedDeserialize() {
|
||||
m_isOpen = false;
|
||||
m_bufp = new vluint8_t [bufferSize()];
|
||||
@@ -116,9 +123,9 @@ public:
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
std::string filename() const { return m_filename; }
|
||||
virtual void close() { flush(); }
|
||||
virtual void flush() {}
|
||||
inline VerilatedDeserialize& read (void* __restrict datap, size_t size) {
|
||||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
vluint8_t* __restrict dp = (vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
@@ -130,12 +137,11 @@ public:
|
||||
return *this; // For function chaining
|
||||
}
|
||||
// Read a datum and compare with expected value
|
||||
bool readDiffers (const void* __restrict datap, size_t size);
|
||||
VerilatedDeserialize& readAssert (const void* __restrict datap, size_t size);
|
||||
VerilatedDeserialize& readAssert (vluint64_t data) { return readAssert(&data, sizeof(data)); }
|
||||
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& readAssert(vluint64_t data) VL_MT_UNSAFE_ONE { return readAssert(&data, sizeof(data)); }
|
||||
private:
|
||||
VerilatedDeserialize(const VerilatedDeserialize& ); ///< N/A, no copy constructor
|
||||
VerilatedDeserialize& bufferCheck() {
|
||||
bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY((m_cp+bufferInsertSize()) > m_endp)) {
|
||||
@@ -147,40 +153,42 @@ private:
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedSave - serialize to a file
|
||||
// This class is not thread safe, it must be called by a single thread
|
||||
|
||||
class VerilatedSave : public VerilatedSerialize {
|
||||
private:
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
VerilatedSave() { m_fd=-1; }
|
||||
virtual ~VerilatedSave() { close(); }
|
||||
// METHODS
|
||||
void open(const char* filenamep); ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) { open(filename.c_str()); }
|
||||
virtual void close();
|
||||
virtual void flush();
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
virtual void close() VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedRestore - deserialize from a file
|
||||
// This class is not thread safe, it must be called by a single thread
|
||||
|
||||
class VerilatedRestore : public VerilatedDeserialize {
|
||||
private:
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
VerilatedRestore() { m_fd=-1; }
|
||||
virtual ~VerilatedRestore() { close(); }
|
||||
|
||||
// METHODS
|
||||
void open(const char* filenamep); ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) { open(filename.c_str()); }
|
||||
virtual void close();
|
||||
virtual void flush() {}
|
||||
virtual void fill();
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
virtual void close() VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
virtual void fill() VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@@ -35,11 +35,12 @@
|
||||
// We want to get a pointer to m_data in the sc_bv_base class,
|
||||
// but it is protected. So make an exposing class, then use
|
||||
// cast magic to get at it. Saves patching get_datap in SystemC.
|
||||
// This class is thread safe (though most of SystemC is not).
|
||||
|
||||
#define VL_SC_BV_DATAP(bv) (VlScBvExposer::sp_datap(bv))
|
||||
class VlScBvExposer : public sc_bv_base {
|
||||
public:
|
||||
static const vluint32_t* sp_datap(const sc_bv_base& base) {
|
||||
static const vluint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
|
||||
return static_cast<const VlScBvExposer*>(&base)->sp_datatp(); }
|
||||
const vluint32_t* sp_datatp() const { return reinterpret_cast<vluint32_t*>(m_data); }
|
||||
// Above reads this protected element in sc_bv_base:
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
/// the symbol table. It is not included in verilated.h
|
||||
/// as it requires some heavyweight C++ classes.
|
||||
///
|
||||
/// These classes are thread safe on read only. It is constructed
|
||||
/// only when a model is built (from the main thread).
|
||||
///
|
||||
/// Code available from: http://www.veripool.org/verilator
|
||||
///
|
||||
//*************************************************************************
|
||||
@@ -34,6 +37,7 @@
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator range
|
||||
/// Thread safety: Assume is constructed only with model, then any number of readers
|
||||
|
||||
// See also V3Ast::VNumRange
|
||||
class VerilatedRange {
|
||||
@@ -53,6 +57,7 @@ public:
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator variable
|
||||
/// Thread safety: Assume is constructed only with model, then any number of readers
|
||||
|
||||
class VerilatedVar {
|
||||
void* m_datap; // Location of data
|
||||
|
||||
+39
-24
@@ -55,18 +55,24 @@ class VerilatedVcdSingleton {
|
||||
private:
|
||||
typedef std::vector<VerilatedVcd*> VcdVec;
|
||||
struct Singleton {
|
||||
VcdVec s_vcdVecp; ///< List of all created traces
|
||||
VerilatedMutex s_vcdMutex; ///< Protect the singleton
|
||||
VcdVec s_vcdVecp VL_GUARDED_BY(s_vcdMutex); ///< List of all created traces
|
||||
};
|
||||
static Singleton& singleton() { static Singleton s; return s; }
|
||||
public:
|
||||
static void pushVcd(VerilatedVcd* vcdp) {
|
||||
static void pushVcd(VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard guard(singleton().s_vcdMutex);
|
||||
singleton().s_vcdVecp.push_back(vcdp);
|
||||
}
|
||||
static void removeVcd(const VerilatedVcd* vcdp) {
|
||||
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard guard(singleton().s_vcdMutex);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
|
||||
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||
}
|
||||
static void flush_all() {
|
||||
static void flush_all() VL_EXCLUDES(singleton().s_vcdMutex) VL_MT_UNSAFE_ONE {
|
||||
// Thread safety: Although this function is protected by a mutex so perhaps
|
||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
||||
VerilatedLockGuard guard(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
@@ -90,7 +96,7 @@ protected:
|
||||
VerilatedVcdCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
VerilatedVcdCallInfo (VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
@@ -103,16 +109,16 @@ protected:
|
||||
//=============================================================================
|
||||
// VerilatedVcdFile
|
||||
|
||||
bool VerilatedVcdFile::open(const std::string& name) {
|
||||
bool VerilatedVcdFile::open(const std::string& name) VL_MT_UNSAFE {
|
||||
m_fd = ::open(name.c_str(), O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK, 0666);
|
||||
return (m_fd>=0);
|
||||
}
|
||||
|
||||
void VerilatedVcdFile::close() {
|
||||
void VerilatedVcdFile::close() VL_MT_UNSAFE {
|
||||
::close(m_fd);
|
||||
}
|
||||
|
||||
ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) {
|
||||
ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) VL_MT_UNSAFE {
|
||||
return ::write(m_fd, bufp, len);
|
||||
}
|
||||
|
||||
@@ -141,6 +147,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
|
||||
}
|
||||
|
||||
void VerilatedVcd::open (const char* filename) {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
|
||||
// Set member variables
|
||||
@@ -171,6 +178,7 @@ void VerilatedVcd::open (const char* filename) {
|
||||
void VerilatedVcd::openNext (bool incFilename) {
|
||||
// Open next filename in concat sequence, mangle filename if
|
||||
// incFilename is true.
|
||||
m_assertOne.check();
|
||||
closePrev(); // Close existing
|
||||
if (incFilename) {
|
||||
// Find _0000.{ext} in filename
|
||||
@@ -219,7 +227,7 @@ void VerilatedVcd::makeNameMap() {
|
||||
m_namemapp = new NameMap;
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
cip->m_code = nextCode();
|
||||
cip->m_code = m_nextCode;
|
||||
(cip->m_initcb) (this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
@@ -265,6 +273,7 @@ VerilatedVcd::~VerilatedVcd() {
|
||||
}
|
||||
|
||||
void VerilatedVcd::closePrev () {
|
||||
// This function is on the flush() call path
|
||||
if (!isOpen()) return;
|
||||
|
||||
bufferFlush();
|
||||
@@ -273,6 +282,7 @@ void VerilatedVcd::closePrev () {
|
||||
}
|
||||
|
||||
void VerilatedVcd::closeErr () {
|
||||
// This function is on the flush() call path
|
||||
// Close due to an error. We might abort before even getting here,
|
||||
// depending on the definition of vl_fatal.
|
||||
if (!isOpen()) return;
|
||||
@@ -283,6 +293,8 @@ void VerilatedVcd::closeErr () {
|
||||
}
|
||||
|
||||
void VerilatedVcd::close() {
|
||||
// This function is on the flush() call path
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
@@ -311,10 +323,10 @@ void VerilatedVcd::printTime (vluint64_t timeui) {
|
||||
// Dinotrace doesn't mind, but Cadence vvision seems to choke
|
||||
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
|
||||
timeui = m_timeLastDump;
|
||||
static bool backTime = false;
|
||||
static VL_THREAD_LOCAL bool backTime = false;
|
||||
if (!backTime) {
|
||||
backTime = true;
|
||||
VL_PRINTF("VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
VL_PRINTF_MT("%%Warning: VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
}
|
||||
}
|
||||
m_timeLastDump = timeui;
|
||||
@@ -335,10 +347,12 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush () {
|
||||
void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
||||
// This function is on the flush() call path
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
// This is much faster than using buffered I/O
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
char* wp = m_wrBufp;
|
||||
while (1) {
|
||||
@@ -353,7 +367,7 @@ void VerilatedVcd::bufferFlush () {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string("VerilatedVcd::bufferFlush: ")+strerror(errno);
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
}
|
||||
@@ -499,19 +513,20 @@ void VerilatedVcd::dumpHeader () {
|
||||
}
|
||||
|
||||
void VerilatedVcd::module (const std::string& name) {
|
||||
m_assertOne.check();
|
||||
m_modName = name;
|
||||
}
|
||||
|
||||
void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
if (!code) { vl_fatal(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
|
||||
if (!code) { VL_FATAL_MT(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
|
||||
|
||||
int bits = ((msb>lsb)?(msb-lsb):(lsb-msb))+1;
|
||||
int codesNeeded = 1+int(bits/32);
|
||||
if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
|
||||
// Make sure array is large enough
|
||||
m_nextCode = std::max(nextCode(), code+codesNeeded);
|
||||
m_nextCode = std::max(m_nextCode, code+codesNeeded);
|
||||
if (m_sigs.capacity() <= m_nextCode) {
|
||||
m_sigs.reserve(m_nextCode*2); // Power-of-2 allocation speeds things up
|
||||
}
|
||||
@@ -617,15 +632,16 @@ void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedVcd::addCallback (
|
||||
void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
|
||||
VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb, VerilatedVcdCallback_t changecb,
|
||||
void* userthis)
|
||||
{
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
vl_fatal(__FILE__,__LINE__,"",msg.c_str());
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
}
|
||||
VerilatedVcdCallInfo* vci = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, nextCode());
|
||||
VerilatedVcdCallInfo* vci = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
|
||||
m_callbacks.push_back(vci);
|
||||
}
|
||||
|
||||
@@ -633,15 +649,17 @@ void VerilatedVcd::addCallback (
|
||||
// Dumping
|
||||
|
||||
void VerilatedVcd::dumpFull (vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
dumpPrep (timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_fullcb) (this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
dumpDone ();
|
||||
}
|
||||
|
||||
void VerilatedVcd::dump (vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
@@ -653,11 +671,11 @@ void VerilatedVcd::dump (vluint64_t timeui) {
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
dumpPrep (timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_changecb) (this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
dumpDone();
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpPrep (vluint64_t timeui) {
|
||||
@@ -666,13 +684,10 @@ void VerilatedVcd::dumpPrep (vluint64_t timeui) {
|
||||
printStr("\n");
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpDone () {
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Static members
|
||||
|
||||
void VerilatedVcd::flush_all() {
|
||||
void VerilatedVcd::flush_all() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVcdSingleton::flush_all();
|
||||
}
|
||||
|
||||
|
||||
+31
-24
@@ -24,6 +24,7 @@
|
||||
#define _VERILATED_VCD_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -44,9 +45,9 @@ public:
|
||||
// METHODS
|
||||
VerilatedVcdFile() : m_fd(0) {}
|
||||
virtual ~VerilatedVcdFile() {}
|
||||
virtual bool open(const std::string& name);
|
||||
virtual void close();
|
||||
virtual ssize_t write(const char* bufp, ssize_t len);
|
||||
virtual bool open(const std::string& name) VL_MT_UNSAFE;
|
||||
virtual void close() VL_MT_UNSAFE;
|
||||
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -104,8 +105,10 @@ private:
|
||||
typedef std::map<std::string,std::string> NameMap;
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
void bufferResize(vluint64_t minsize);
|
||||
void bufferFlush();
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
@@ -136,7 +139,7 @@ private:
|
||||
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
|
||||
*m_writep++ = static_cast<char>((code)%94+33);
|
||||
}
|
||||
static std::string stringCode (vluint32_t code) {
|
||||
static std::string stringCode (vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
|
||||
@@ -144,20 +147,13 @@ private:
|
||||
return out + static_cast<char>((code)%94+33);
|
||||
}
|
||||
|
||||
VerilatedVcd(const VerilatedVcd& ); ///< N/A, no copy constructor
|
||||
|
||||
protected:
|
||||
// METHODS
|
||||
void evcd(bool flag) { m_evcd = flag; }
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
public:
|
||||
// CREATORS
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep=NULL);
|
||||
~VerilatedVcd();
|
||||
|
||||
// ACCESSORS
|
||||
/// Inside dumping routines, return next VCD signal code
|
||||
vluint32_t nextCode() const {return m_nextCode;}
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB=rolloverMB; };
|
||||
/// Is file open?
|
||||
@@ -168,11 +164,13 @@ public:
|
||||
inline bool isScopeEscape(char c) { return isspace(c) || c==m_scopeEscape; }
|
||||
|
||||
// METHODS
|
||||
void open (const char* filename); ///< Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void openNext (bool incFilename); ///< Open next data-only file
|
||||
void flush() { bufferFlush(); } ///< Flush any remaining data
|
||||
static void flush_all(); ///< Flush any remaining data from all files
|
||||
void close (); ///< Close the file
|
||||
void close() VL_MT_UNSAFE_ONE; ///< Close the file
|
||||
/// Flush any remaining data to this file
|
||||
void flush() VL_MT_UNSAFE_ONE { bufferFlush(); }
|
||||
/// Flush any remaining data from all files
|
||||
static void flush_all() VL_MT_UNSAFE_ONE;
|
||||
|
||||
void set_time_unit (const char* unit); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit (const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
@@ -191,7 +189,7 @@ public:
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback (VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
|
||||
VerilatedVcdCallback_t change,
|
||||
void* userthis);
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module (const std::string& name);
|
||||
@@ -393,18 +391,27 @@ public:
|
||||
fullFloat (code, newval);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// METHODS
|
||||
void evcd(bool flag) { m_evcd = flag; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdC
|
||||
/// Create a VCD dump file in C standalone (no SystemC) simulations.
|
||||
/// Also derived for use in SystemC simulations.
|
||||
/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE
|
||||
|
||||
class VerilatedVcdC {
|
||||
VerilatedVcd m_sptrace; ///< Trace file being created
|
||||
public:
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdC);
|
||||
public:
|
||||
explicit VerilatedVcdC(VerilatedVcdFile* filep=NULL) : m_sptrace(filep) {}
|
||||
~VerilatedVcdC() {}
|
||||
public:
|
||||
// ACCESSORS
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_sptrace.isOpen(); }
|
||||
@@ -412,17 +419,17 @@ public:
|
||||
/// Open a new VCD file
|
||||
/// This includes a complete header dump each time it is called,
|
||||
/// just as if this object was deleted and reconstructed.
|
||||
void open (const char* filename) { m_sptrace.open(filename); }
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); }
|
||||
/// Continue a VCD dump by rotating to a new file name
|
||||
/// The header is only in the first file created, this allows
|
||||
/// "cat" to be used to combine the header plus any number of data files.
|
||||
void openNext (bool incFilename=true) { m_sptrace.openNext(incFilename); }
|
||||
void openNext(bool incFilename=true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); }
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); };
|
||||
/// Close dump
|
||||
void close() { m_sptrace.close(); }
|
||||
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
void flush() { m_sptrace.flush(); }
|
||||
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
void dump (vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
/// \file
|
||||
/// \brief Verilator tracing in VCD format
|
||||
///
|
||||
/// This class is not threadsafe, as the SystemC kernel is not threadsafe.
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
@@ -37,6 +39,8 @@ class VerilatedVcdSc
|
||||
: sc_trace_file
|
||||
, public VerilatedVcdC
|
||||
{
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdSc);
|
||||
public:
|
||||
VerilatedVcdSc() {
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
@@ -56,8 +60,9 @@ public:
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
# endif
|
||||
}
|
||||
|
||||
virtual ~VerilatedVcdSc() {}
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle (bool delta_cycle) {
|
||||
# if (SYSTEMC_VERSION>20011000)
|
||||
|
||||
+543
-42
@@ -32,18 +32,489 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h"
|
||||
|
||||
//======================================================================
|
||||
|
||||
VerilatedVpi VerilatedVpi::s_s; // Singleton
|
||||
vluint8_t* VerilatedVpio::s_freeHead = NULL;
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpi Methods
|
||||
// Internal constants
|
||||
|
||||
#define VL_DEBUG_IF_PLI VL_DEBUG_IF
|
||||
#define VL_VPI_LINE_SIZE 8192
|
||||
|
||||
VerilatedVpiError* VerilatedVpi::error_info() {
|
||||
if (s_s.m_errorInfop == NULL) {
|
||||
s_s.m_errorInfop = new VerilatedVpiError();
|
||||
//======================================================================
|
||||
// Internal macros
|
||||
|
||||
#define _VL_VPI_INTERNAL VerilatedVpiImp::error_info()->setMessage(vpiInternal)->setMessage
|
||||
#define _VL_VPI_SYSTEM VerilatedVpiImp::error_info()->setMessage(vpiSystem )->setMessage
|
||||
#define _VL_VPI_ERROR VerilatedVpiImp::error_info()->setMessage(vpiError )->setMessage
|
||||
#define _VL_VPI_WARNING VerilatedVpiImp::error_info()->setMessage(vpiWarning )->setMessage
|
||||
#define _VL_VPI_NOTICE VerilatedVpiImp::error_info()->setMessage(vpiNotice )->setMessage
|
||||
#define _VL_VPI_ERROR_RESET VerilatedVpiImp::error_info()->resetError
|
||||
|
||||
// Not supported yet
|
||||
#define _VL_VPI_UNIMP() \
|
||||
_VL_VPI_ERROR(__FILE__,__LINE__,Verilated::catName("Unsupported VPI function: ",VL_FUNC));
|
||||
|
||||
//======================================================================
|
||||
// Implementation
|
||||
|
||||
// Base VPI handled object
|
||||
class VerilatedVpio {
|
||||
// MEM MANGLEMENT
|
||||
static VL_THREAD_LOCAL vluint8_t* t_freeHead;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedVpio() {}
|
||||
virtual ~VerilatedVpio() {}
|
||||
inline static void* operator new(size_t size) VL_MT_SAFE {
|
||||
// We new and delete tons of vpi structures, so keep them around
|
||||
// To simplify our free list, we use a size large enough for all derived types
|
||||
// We reserve word zero for the next pointer, as that's safer in case a
|
||||
// dangling reference to the original remains around.
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__,__LINE__,"", "increase chunk");
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
vluint8_t* newp = t_freeHead;
|
||||
t_freeHead = *((vluint8_t**)newp);
|
||||
return newp+8;
|
||||
} else {
|
||||
// +8: 8 bytes for next
|
||||
vluint8_t* newp = reinterpret_cast<vluint8_t*>(::operator new(chunk+8));
|
||||
return newp+8;
|
||||
}
|
||||
}
|
||||
inline static void operator delete(void* obj, size_t size) VL_MT_SAFE {
|
||||
vluint8_t* oldp = ((vluint8_t*)obj)-8;
|
||||
*((void**)oldp) = t_freeHead;
|
||||
t_freeHead = oldp;
|
||||
}
|
||||
// MEMBERS
|
||||
static inline VerilatedVpio* castp(vpiHandle h) { return dynamic_cast<VerilatedVpio*>((VerilatedVpio*)h); }
|
||||
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
// ACCESSORS
|
||||
virtual const char* name() const { return "<null>"; }
|
||||
virtual const char* fullname() const { return "<null>"; }
|
||||
virtual const char* defname() const { return "<null>"; }
|
||||
virtual vluint32_t type() const { return 0; }
|
||||
virtual vluint32_t size() const { return 0; }
|
||||
virtual const VerilatedRange* rangep() const { return NULL; }
|
||||
virtual vpiHandle dovpi_scan() { return 0; }
|
||||
};
|
||||
|
||||
typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data *);
|
||||
|
||||
class VerilatedVpioCb : public VerilatedVpio {
|
||||
t_cb_data m_cbData;
|
||||
s_vpi_value m_value;
|
||||
QData m_time;
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpioCb(const t_cb_data* cbDatap, QData time)
|
||||
: m_cbData(*cbDatap), m_time(time) {
|
||||
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
||||
m_cbData.value = &m_value;
|
||||
}
|
||||
virtual ~VerilatedVpioCb() {}
|
||||
static inline VerilatedVpioCb* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioCb*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiCallback; }
|
||||
vluint32_t reason() const { return m_cbData.reason; }
|
||||
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
|
||||
t_cb_data* cb_datap() { return &(m_cbData); }
|
||||
QData time() const { return m_time; }
|
||||
};
|
||||
|
||||
class VerilatedVpioConst : public VerilatedVpio {
|
||||
vlsint32_t m_num;
|
||||
public:
|
||||
explicit VerilatedVpioConst(vlsint32_t num) : m_num(num) {}
|
||||
virtual ~VerilatedVpioConst() {}
|
||||
static inline VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioConst*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiUndefined; }
|
||||
vlsint32_t num() const { return m_num; }
|
||||
};
|
||||
|
||||
class VerilatedVpioRange : public VerilatedVpio {
|
||||
const VerilatedRange* m_range;
|
||||
vlsint32_t m_iteration;
|
||||
public:
|
||||
explicit VerilatedVpioRange(const VerilatedRange* range) : m_range(range), m_iteration(0) {}
|
||||
virtual ~VerilatedVpioRange() {}
|
||||
static inline VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioRange*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiRange; }
|
||||
virtual vluint32_t size() const { return m_range->elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return m_range; }
|
||||
int iteration() const { return m_iteration; }
|
||||
void iterationInc() { ++m_iteration; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
if (!iteration()) {
|
||||
VerilatedVpioRange* nextp = new VerilatedVpioRange(*this);
|
||||
nextp->iterationInc();
|
||||
return ((nextp)->castVpiHandle());
|
||||
} else {
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioScope : public VerilatedVpio {
|
||||
const VerilatedScope* m_scopep;
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep(scopep) {}
|
||||
virtual ~VerilatedVpioScope() {}
|
||||
static inline VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioScope*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual const char* name() const { return m_scopep->name(); }
|
||||
virtual const char* fullname() const { return m_scopep->name(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioVar : public VerilatedVpio {
|
||||
const VerilatedVar* m_varp;
|
||||
const VerilatedScope* m_scopep;
|
||||
vluint8_t* m_prevDatap; // Previous value of data, for cbValueChange
|
||||
union {
|
||||
vluint8_t u8[4];
|
||||
vluint32_t u32;
|
||||
} m_mask; // memoized variable mask
|
||||
vluint32_t m_entSize; // memoized variable size
|
||||
protected:
|
||||
void* m_varDatap; // varp()->datap() adjusted for array entries
|
||||
vlsint32_t m_index;
|
||||
const VerilatedRange& get_range() const {
|
||||
// Determine number of dimensions and return outermost
|
||||
return (m_varp->dims()>1) ? m_varp->array() : m_varp->range();
|
||||
}
|
||||
public:
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: m_varp(varp), m_scopep(scopep), m_index(0) {
|
||||
m_prevDatap = NULL;
|
||||
m_mask.u32 = VL_MASK_I(varp->range().elements());
|
||||
m_entSize = varp->entSize();
|
||||
m_varDatap = varp->datap();
|
||||
}
|
||||
virtual ~VerilatedVpioVar() {
|
||||
if (m_prevDatap) { delete [] m_prevDatap; m_prevDatap = NULL; }
|
||||
}
|
||||
static inline VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioVar*>((VerilatedVpio*)h); }
|
||||
const VerilatedVar* varp() const { return m_varp; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
vluint32_t mask() const { return m_mask.u32; }
|
||||
vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; }
|
||||
vluint32_t entSize() const { return m_entSize; }
|
||||
vluint32_t index() { return m_index; }
|
||||
virtual vluint32_t type() const {
|
||||
if (varp()->vldir() != vpiNoDirection) return vpiPort;
|
||||
return (varp()->dims()>1) ? vpiMemory : vpiReg; // but might be wire, logic
|
||||
}
|
||||
virtual vluint32_t size() const { return get_range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return &get_range(); }
|
||||
virtual const char* name() const { return m_varp->name(); }
|
||||
virtual const char* fullname() const {
|
||||
static VL_THREAD_LOCAL std::string out;
|
||||
out = std::string(m_scopep->name())+"."+name();
|
||||
return out.c_str();
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
m_prevDatap = new vluint8_t [entSize()];
|
||||
memcpy(prevDatap(), varp()->datap(), entSize());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioMemoryWord : public VerilatedVpioVar {
|
||||
public:
|
||||
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep,
|
||||
vlsint32_t index, int offset)
|
||||
: VerilatedVpioVar(varp, scopep) {
|
||||
m_index = index;
|
||||
m_varDatap = ((vluint8_t*)varp->datap()) + entSize()*offset;
|
||||
}
|
||||
virtual ~VerilatedVpioMemoryWord() {}
|
||||
static inline VerilatedVpioMemoryWord* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioMemoryWord*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiMemoryWord; }
|
||||
virtual vluint32_t size() const { return varp()->range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return &(varp()->range()); }
|
||||
virtual const char* fullname() const {
|
||||
static VL_THREAD_LOCAL std::string out;
|
||||
char num[20]; sprintf(num,"%d",m_index);
|
||||
out = std::string(scopep()->name())+"."+name()+"["+num+"]";
|
||||
return out.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioVarIter : public VerilatedVpio {
|
||||
const VerilatedScope* m_scopep;
|
||||
VerilatedVarNameMap::const_iterator m_it;
|
||||
bool m_started;
|
||||
public:
|
||||
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
|
||||
: m_scopep(scopep), m_started(false) { }
|
||||
virtual ~VerilatedVpioVarIter() {}
|
||||
static inline VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioVarIter*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
if (VL_LIKELY(m_scopep->varsp())) {
|
||||
VerilatedVarNameMap* varsp = m_scopep->varsp();
|
||||
if (VL_UNLIKELY(!m_started)) { m_it = varsp->begin(); m_started=true; }
|
||||
else if (VL_UNLIKELY(m_it == varsp->end())) return 0;
|
||||
else ++m_it;
|
||||
if (m_it == varsp->end()) return 0;
|
||||
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))
|
||||
->castVpiHandle());
|
||||
} else {
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioMemoryWordIter : public VerilatedVpio {
|
||||
const vpiHandle m_handle;
|
||||
const VerilatedVar* m_varp;
|
||||
vlsint32_t m_iteration;
|
||||
vlsint32_t m_direction;
|
||||
bool m_done;
|
||||
public:
|
||||
VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp)
|
||||
: m_handle(handle), m_varp(varp), m_iteration(varp->array().right()), m_direction(VL_LIKELY(varp->array().left()>varp->array().right())?1:-1), m_done(false) { }
|
||||
virtual ~VerilatedVpioMemoryWordIter() {}
|
||||
static inline VerilatedVpioMemoryWordIter* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioMemoryWordIter*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiIterator; }
|
||||
void iterationInc() {
|
||||
if (!(m_done = (m_iteration == m_varp->array().left()))) m_iteration+=m_direction;
|
||||
}
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
vpiHandle result;
|
||||
if (m_done) return 0;
|
||||
result = vpi_handle_by_index(m_handle, m_iteration);
|
||||
iterationInc();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
struct VerilatedVpiTimedCbsCmp {
|
||||
/// Ordering sets keyed by time, then callback descriptor
|
||||
bool operator() (const std::pair<QData,VerilatedVpioCb*>& a,
|
||||
const std::pair<QData,VerilatedVpioCb*>& b) const {
|
||||
if (a.first < b.first) return 1;
|
||||
if (a.first > b.first) return 0;
|
||||
return a.second < b.second;
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpiError;
|
||||
|
||||
class VerilatedVpiImp {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason
|
||||
typedef std::list<VerilatedVpioCb*> VpioCbList;
|
||||
typedef std::set<std::pair<QData,VerilatedVpioCb*>,VerilatedVpiTimedCbsCmp > VpioTimedCbs;
|
||||
|
||||
struct product_info {
|
||||
PLI_BYTE8* product;
|
||||
};
|
||||
|
||||
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||
VerilatedVpiError* m_errorInfop; // Container for vpi error info
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
static VerilatedVpiImp s_s; // Singleton
|
||||
|
||||
public:
|
||||
VerilatedVpiImp() { m_errorInfop=NULL; }
|
||||
~VerilatedVpiImp() {}
|
||||
static void assertOneCheck() { s_s.m_assertOne.check(); }
|
||||
static void cbReasonAdd(VerilatedVpioCb* vop) {
|
||||
if (vop->reason() == cbValueChange) {
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
||||
varop->createPrevDatap();
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) VL_FATAL_MT(__FILE__,__LINE__,"", "vpi bb reason too large");
|
||||
s_s.m_cbObjLists[vop->reason()].push_back(vop);
|
||||
}
|
||||
static void cbTimedAdd(VerilatedVpioCb* vop) {
|
||||
s_s.m_timedCbs.insert(std::make_pair(vop->time(), vop));
|
||||
}
|
||||
static void cbReasonRemove(VerilatedVpioCb* cbp) {
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()];
|
||||
// We do not remove it now as we may be iterating the list,
|
||||
// instead set to NULL and will cleanup later
|
||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end(); ++it) {
|
||||
if (*it == cbp) *it = NULL;
|
||||
}
|
||||
}
|
||||
static void cbTimedRemove(VerilatedVpioCb* cbp) {
|
||||
VpioTimedCbs::iterator it=s_s.m_timedCbs.find(std::make_pair(cbp->time(),cbp));
|
||||
if (VL_LIKELY(it != s_s.m_timedCbs.end())) {
|
||||
s_s.m_timedCbs.erase(it);
|
||||
}
|
||||
}
|
||||
static void callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
QData time = VL_TIME_Q();
|
||||
for (VpioTimedCbs::iterator it=s_s.m_timedCbs.begin(); it!=s_s.m_timedCbs.end(); ) {
|
||||
if (VL_UNLIKELY(it->first <= time)) {
|
||||
VerilatedVpioCb* vop = it->second;
|
||||
++it; // iterator may be deleted by callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: timed_callback %p\n",vop););
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
else { ++it; }
|
||||
}
|
||||
}
|
||||
static QData cbNextDeadline() {
|
||||
VpioTimedCbs::const_iterator it=s_s.m_timedCbs.begin();
|
||||
if (VL_LIKELY(it!=s_s.m_timedCbs.end())) {
|
||||
return it->first;
|
||||
} else {
|
||||
return ~VL_ULL(0); // maxquad
|
||||
}
|
||||
}
|
||||
static void callCbs(vluint32_t reason) {
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[reason];
|
||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) {
|
||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
continue;
|
||||
}
|
||||
VerilatedVpioCb* vop = *it++;
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback %d %p\n",reason,vop););
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
}
|
||||
static void callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange];
|
||||
typedef std::set<VerilatedVpioVar*> VpioVarSet;
|
||||
VpioVarSet update; // set of objects to update after callbacks
|
||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) {
|
||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
continue;
|
||||
}
|
||||
VerilatedVpioCb* vop = *it++;
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
||||
void* newDatap = varop->varDatap();
|
||||
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap),
|
||||
newDatap, prevDatap););
|
||||
if (memcmp(prevDatap, newDatap, varop->entSize())) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %p %s v[0]=%d\n",
|
||||
vop,varop->fullname(), *((CData*)newDatap)););
|
||||
update.insert(varop);
|
||||
vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value);
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (VpioVarSet::const_iterator it=update.begin(); it!=update.end(); ++it) {
|
||||
memcpy((*it)->prevDatap(), (*it)->varDatap(), (*it)->entSize());
|
||||
}
|
||||
}
|
||||
|
||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
||||
};
|
||||
|
||||
class VerilatedVpiError {
|
||||
//// Container for vpi error info
|
||||
|
||||
t_vpi_error_info m_errorInfo;
|
||||
bool m_flag;
|
||||
char m_buff[VL_VPI_LINE_SIZE];
|
||||
void setError(PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) {
|
||||
m_errorInfo.message = message;
|
||||
m_errorInfo.file = file;
|
||||
m_errorInfo.line = line;
|
||||
m_errorInfo.code = code;
|
||||
do_callbacks();
|
||||
}
|
||||
void do_callbacks() {
|
||||
if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) {
|
||||
// Stop on vpi error/unsupported
|
||||
vpi_unsupported();
|
||||
}
|
||||
// We need to run above code first because in the case that the callback executes further vpi
|
||||
// functions we will loose the error as it will be overwritten.
|
||||
VerilatedVpiImp::callCbs(cbPLIError);
|
||||
}
|
||||
public:
|
||||
|
||||
VerilatedVpiError() : m_flag(false) {
|
||||
m_buff[0] = '\0';
|
||||
m_errorInfo.product = (PLI_BYTE8*)Verilated::productName();
|
||||
}
|
||||
~VerilatedVpiError() {}
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
VerilatedVpiError* setMessage(PLI_INT32 level) {
|
||||
m_flag=true;
|
||||
m_errorInfo.level = level;
|
||||
return this;
|
||||
}
|
||||
void setMessage(std::string file, PLI_INT32 line, std::string message, ...) {
|
||||
static VL_THREAD_LOCAL std::string filehold;
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
VL_VSNPRINTF(m_buff, sizeof(m_buff), message.c_str(), args);
|
||||
va_end(args);
|
||||
m_errorInfo.state = vpiPLI;
|
||||
filehold = file;
|
||||
setError((PLI_BYTE8*)m_buff, NULL, (PLI_BYTE8*)filehold.c_str(), line);
|
||||
}
|
||||
p_vpi_error_info getError() {
|
||||
if (m_flag) return &m_errorInfo;
|
||||
return NULL;
|
||||
}
|
||||
void resetError() {
|
||||
m_flag=false;
|
||||
}
|
||||
static void vpi_unsupported() {
|
||||
// Not supported yet
|
||||
p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
if (error_info_p) {
|
||||
VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message);
|
||||
return;
|
||||
}
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_unsupported called without error info set");
|
||||
}
|
||||
static const char* strFromVpiVal(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiObjType(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiMethod(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
VerilatedVpiImp VerilatedVpiImp::s_s; // Singleton
|
||||
VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = NULL;
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpi implementation
|
||||
|
||||
void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::callTimedCbs();
|
||||
}
|
||||
|
||||
void VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::callValueCbs();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpiImp implementation
|
||||
|
||||
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
if (VL_UNLIKELY(!s_s.m_errorInfop)) {
|
||||
s_s.m_errorInfop = new VerilatedVpiError();
|
||||
}
|
||||
return s_s.m_errorInfop;
|
||||
}
|
||||
@@ -51,7 +522,7 @@ VerilatedVpiError* VerilatedVpi::error_info() {
|
||||
//======================================================================
|
||||
// VerilatedVpiError Methods
|
||||
|
||||
const char* VerilatedVpiError::strFromVpiVal(PLI_INT32 vpiVal) {
|
||||
const char* VerilatedVpiError::strFromVpiVal(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
static const char* const names[] = {
|
||||
"*undefined*",
|
||||
"vpiBinStrVal",
|
||||
@@ -76,7 +547,7 @@ const char* VerilatedVpiError::strFromVpiVal(PLI_INT32 vpiVal) {
|
||||
if (vpiVal < 0) return names[0];
|
||||
return names[(vpiVal<=vpiRawFourStateVal)?vpiVal:0];
|
||||
}
|
||||
const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) {
|
||||
const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
static const char* const names[] = {
|
||||
"*undefined*",
|
||||
"vpiAlways",
|
||||
@@ -218,7 +689,7 @@ const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) {
|
||||
if (vpiVal < 0) return names[0];
|
||||
return names[(vpiVal<=vpiGenVar)?vpiVal:0];
|
||||
}
|
||||
const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) {
|
||||
const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
static const char* const names[] = {
|
||||
"vpiCondition",
|
||||
"vpiDelay",
|
||||
@@ -261,7 +732,7 @@ const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) {
|
||||
return names[vpiVal-vpiCondition];
|
||||
}
|
||||
|
||||
const char* VerilatedVpiError::strFromVpiCallbackReason(PLI_INT32 vpiVal) {
|
||||
const char* VerilatedVpiError::strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
static const char* const names[] = {
|
||||
"*undefined*",
|
||||
"cbValueChange",
|
||||
@@ -300,7 +771,7 @@ const char* VerilatedVpiError::strFromVpiCallbackReason(PLI_INT32 vpiVal) {
|
||||
return names[(vpiVal<=cbAtEndOfSimTime)?vpiVal:0];
|
||||
}
|
||||
|
||||
const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) {
|
||||
const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
static const char* const names[] = {
|
||||
"*undefined or other*",
|
||||
"vpiType",
|
||||
@@ -389,7 +860,7 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) {
|
||||
std::string msg = std::string("%Error: ") \
|
||||
+ "GOT = '"+((got)?(got):"<null>")+"'" \
|
||||
+ " EXP = '"+((exp)?(exp):"<null>")+"'"; \
|
||||
vl_fatal(__FILE__,__LINE__,"",msg.c_str()); \
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str()); \
|
||||
}
|
||||
|
||||
#define CHECK_ENUM_STR(fn, enum) \
|
||||
@@ -398,7 +869,12 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) {
|
||||
CHECK_RESULT_CSTR(strVal, #enum); \
|
||||
} while (0)
|
||||
|
||||
void VerilatedVpiError::selfTest() {
|
||||
void VerilatedVpi::selfTest() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiError::selfTest();
|
||||
}
|
||||
void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
|
||||
CHECK_ENUM_STR(strFromVpiVal, vpiBinStrVal);
|
||||
CHECK_ENUM_STR(strFromVpiVal, vpiRawFourStateVal);
|
||||
|
||||
@@ -433,6 +909,7 @@ void VerilatedVpiError::selfTest() {
|
||||
// callback related
|
||||
|
||||
vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
// cppcheck-suppress nullPointer
|
||||
if (VL_UNLIKELY(!cb_data_p)) {
|
||||
@@ -444,8 +921,8 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
QData time = 0;
|
||||
if (cb_data_p->time) time = _VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, VL_TIME_Q()+time);
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_register_cb %d %p delay=%" VL_PRI64 "u\n",cb_data_p->reason,vop,time););
|
||||
VerilatedVpi::cbTimedAdd(vop);
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p delay=%" VL_PRI64 "u\n",cb_data_p->reason,vop,time););
|
||||
VerilatedVpiImp::cbTimedAdd(vop);
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
|
||||
@@ -459,8 +936,8 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
|
||||
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0);
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_register_cb %d %p\n",cb_data_p->reason,vop););
|
||||
VerilatedVpi::cbReasonAdd(vop);
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p\n",cb_data_p->reason,vop););
|
||||
VerilatedVpiImp::cbReasonAdd(vop);
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
@@ -471,14 +948,15 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_remove_cb(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_remove_cb %p\n",object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpioCb* vop = VerilatedVpioCb::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
if (vop->cb_datap()->reason == cbAfterDelay) {
|
||||
VerilatedVpi::cbTimedRemove(vop);
|
||||
VerilatedVpiImp::cbTimedRemove(vop);
|
||||
} else {
|
||||
VerilatedVpi::cbReasonRemove(vop);
|
||||
VerilatedVpiImp::cbReasonRemove(vop);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -496,9 +974,10 @@ void vpi_get_systf_info(vpiHandle object, p_vpi_systf_data systf_data_p) {
|
||||
// for obtaining handles
|
||||
|
||||
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!namep)) return NULL;
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle_by_name %s %p\n",namep,scope););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n",namep,scope););
|
||||
VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope);
|
||||
const VerilatedVar* varp;
|
||||
const VerilatedScope* scopep;
|
||||
@@ -530,7 +1009,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
|
||||
vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
// Used to get array entries
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle_by_index %p %d\n",object, indx););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n",object, indx););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpioVar* varop = VerilatedVpioVar::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_LIKELY(varop)) {
|
||||
@@ -555,7 +1035,8 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
// for traversing relationships
|
||||
|
||||
vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle %d %p\n",type,object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle %d %p\n",type,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (type) {
|
||||
case vpiLeftRange: {
|
||||
@@ -597,7 +1078,8 @@ vpiHandle vpi_handle_multi(PLI_INT32 type, vpiHandle refHandle1, vpiHandle refHa
|
||||
}
|
||||
|
||||
vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_iterate %d %p\n",type,object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_iterate %d %p\n",type,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (type) {
|
||||
case vpiMemoryWord: {
|
||||
@@ -634,7 +1116,8 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
}
|
||||
}
|
||||
vpiHandle vpi_scan(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_scan %p\n",object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return NULL;
|
||||
@@ -645,7 +1128,8 @@ vpiHandle vpi_scan(vpiHandle object) {
|
||||
|
||||
PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||
// Leave this in the header file - in many cases the compiler can constant propagate "object"
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_get %d %p\n",property,object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get %d %p\n",property,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (property) {
|
||||
case vpiTimePrecision: {
|
||||
@@ -686,7 +1170,8 @@ PLI_INT64 vpi_get64(PLI_INT32 property, vpiHandle object) {
|
||||
}
|
||||
|
||||
PLI_BYTE8 *vpi_get_str(PLI_INT32 property, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_get_str %d %p\n",property,object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n",property,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!vop)) return NULL;
|
||||
@@ -724,7 +1209,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
static VL_THREAD_LOCAL char outStr[1+VL_MULS_MAX_WORDS*32]; // Maximum required size is for binary string, one byte per bit plus null termination
|
||||
// cppcheck-suppress variableScope
|
||||
static VL_THREAD_LOCAL int outStrSz = sizeof(outStr)-1;
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_get_value %p\n",object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!value_p)) return;
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
@@ -751,7 +1237,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
case VLVT_WDATA: {
|
||||
int words = VL_WORDS_I(vop->varp()->range().elements());
|
||||
if (VL_UNLIKELY(words >= VL_MULS_MAX_WORDS)) {
|
||||
vl_fatal(__FILE__,__LINE__,"", "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"", "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||
}
|
||||
WDataInP datap = (reinterpret_cast<IData*>(vop->varDatap()));
|
||||
for (int i=0; i<words; ++i) {
|
||||
@@ -979,17 +1465,18 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
|
||||
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
p_vpi_time time_p, PLI_INT32 flags) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_put_value %p %p\n",object, value_p););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n",object, value_p););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!value_p)) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value with NULL value pointer");
|
||||
return 0;
|
||||
}
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_put_value name=%s fmt=%d vali=%d\n",
|
||||
vop->fullname(), value_p->format, value_p->value.integer);
|
||||
VL_PRINTF("-vltVpi: varp=%p putatp=%p\n",
|
||||
vop->varp()->datap(), vop->varDatap()););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n",
|
||||
vop->fullname(), value_p->format, value_p->value.integer);
|
||||
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
|
||||
vop->varp()->datap(), vop->varDatap()););
|
||||
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value to signal marked read-only, use public_flat_rw instead: ", vop->fullname());
|
||||
return 0;
|
||||
@@ -1251,6 +1738,7 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p,
|
||||
// time processing
|
||||
|
||||
void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
// cppcheck-suppress nullPointer
|
||||
if (VL_UNLIKELY(!time_p)) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_get_time with NULL value pointer");
|
||||
@@ -1272,11 +1760,13 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
// I/O routines
|
||||
|
||||
PLI_UINT32 vpi_mcd_open(PLI_BYTE8 *filenamep) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
return VL_FOPEN_S(filenamep,"wb");
|
||||
}
|
||||
|
||||
PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
VL_FCLOSE_I(mcd); return 0;
|
||||
}
|
||||
@@ -1286,6 +1776,7 @@ PLI_BYTE8 *vpi_mcd_name(PLI_UINT32 mcd) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8 *formatp, ...) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
@@ -1295,6 +1786,7 @@ PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8 *formatp, ...) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_printf(PLI_BYTE8 *formatp, ...) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
@@ -1304,11 +1796,13 @@ PLI_INT32 vpi_printf(PLI_BYTE8 *formatp, ...) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
return VL_VPRINTF(formatp, ap);
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8 *format, va_list ap) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
FILE* fp = VL_CVT_I_FP(mcd);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
// cppcheck-suppress nullPointer
|
||||
@@ -1318,12 +1812,14 @@ PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8 *format, va_list ap) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_flush(void) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
Verilated::flushCall();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
FILE* fp = VL_CVT_I_FP(mcd);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!fp)) return 1;
|
||||
@@ -1339,7 +1835,8 @@ PLI_INT32 vpi_compare_objects(vpiHandle object1, vpiHandle object2) {
|
||||
PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
|
||||
// executing vpi_chk_error does not reset error
|
||||
// error_info_p can be NULL, so only return level in that case
|
||||
p_vpi_error_info _error_info_p = VerilatedVpi::error_info()->getError();
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
p_vpi_error_info _error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
if (error_info_p && _error_info_p) {
|
||||
*error_info_p = *_error_info_p;
|
||||
}
|
||||
@@ -1348,12 +1845,14 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
|
||||
};
|
||||
|
||||
PLI_INT32 vpi_free_object(vpiHandle object) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
return vpi_release_handle(object); // Deprecated
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_release_handle (vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_release_handle %p\n",object););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
@@ -1362,7 +1861,8 @@ PLI_INT32 vpi_release_handle (vpiHandle object) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) {
|
||||
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
vlog_info_p->argc = Verilated::getCommandArgs()->argc;
|
||||
vlog_info_p->argv = (PLI_BYTE8**)Verilated::getCommandArgs()->argv;
|
||||
@@ -1387,15 +1887,16 @@ PLI_INT32 vpi_put_userdata(vpiHandle obj, void *userdata) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_control(PLI_INT32 operation, ...) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_control %d\n",operation););
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_control %d\n",operation););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (operation) {
|
||||
case vpiFinish: {
|
||||
vl_finish(__FILE__,__LINE__,"*VPI*");
|
||||
VL_FINISH_MT(__FILE__,__LINE__,"*VPI*");
|
||||
return 1;
|
||||
}
|
||||
case vpiStop: {
|
||||
vl_stop(__FILE__,__LINE__,"*VPI*");
|
||||
VL_STOP_MT(__FILE__,__LINE__,"*VPI*");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-458
@@ -16,8 +16,8 @@
|
||||
/// \file
|
||||
/// \brief Verilator: VPI implementation code
|
||||
///
|
||||
/// This file must be compiled and linked against all objects
|
||||
/// created from Verilator or called by Verilator that use the VPI.
|
||||
/// This file must be compiled and linked against all objects
|
||||
/// created from Verilator or called by Verilator that use the VPI.
|
||||
///
|
||||
/// Code available from: http://www.veripool.org/verilator
|
||||
///
|
||||
@@ -29,472 +29,24 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
//======================================================================
|
||||
// From IEEE 1800-2009 annex K
|
||||
|
||||
#include "vltstd/vpi_user.h"
|
||||
|
||||
//======================================================================
|
||||
// Internal constants
|
||||
|
||||
#define VL_DEBUG_IF_PLI VL_DEBUG_IF
|
||||
#define VL_VPI_LINE_SIZE 8192
|
||||
|
||||
//======================================================================
|
||||
// Internal macros
|
||||
|
||||
#define _VL_VPI_INTERNAL VerilatedVpi::error_info()->setMessage(vpiInternal)->setMessage
|
||||
#define _VL_VPI_SYSTEM VerilatedVpi::error_info()->setMessage(vpiSystem )->setMessage
|
||||
#define _VL_VPI_ERROR VerilatedVpi::error_info()->setMessage(vpiError )->setMessage
|
||||
#define _VL_VPI_WARNING VerilatedVpi::error_info()->setMessage(vpiWarning )->setMessage
|
||||
#define _VL_VPI_NOTICE VerilatedVpi::error_info()->setMessage(vpiNotice )->setMessage
|
||||
#define _VL_VPI_ERROR_RESET VerilatedVpi::error_info()->resetError
|
||||
|
||||
// Not supported yet
|
||||
#define _VL_VPI_UNIMP() \
|
||||
_VL_VPI_ERROR(__FILE__,__LINE__,Verilated::catName("Unsupported VPI function: ",VL_FUNC));
|
||||
|
||||
//======================================================================
|
||||
// Implementation
|
||||
|
||||
// Base VPI handled object
|
||||
class VerilatedVpio {
|
||||
// MEM MANGLEMENT
|
||||
static vluint8_t* s_freeHead;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedVpio() {}
|
||||
virtual ~VerilatedVpio() {}
|
||||
inline static void* operator new(size_t size) {
|
||||
// We new and delete tons of vpi structures, so keep them around
|
||||
// To simplify our free list, we use a size large enough for all derived types
|
||||
// We reserve word zero for the next pointer, as that's safer in case a
|
||||
// dangling reference to the original remains around.
|
||||
static size_t chunk = 96;
|
||||
if (VL_UNLIKELY(size>chunk)) vl_fatal(__FILE__,__LINE__,"", "increase chunk");
|
||||
if (VL_LIKELY(s_freeHead)) {
|
||||
vluint8_t* newp = s_freeHead;
|
||||
s_freeHead = *((vluint8_t**)newp);
|
||||
return newp+8;
|
||||
} else {
|
||||
// +8: 8 bytes for next
|
||||
vluint8_t* newp = reinterpret_cast<vluint8_t*>(::operator new(chunk+8));
|
||||
return newp+8;
|
||||
}
|
||||
}
|
||||
inline static void operator delete(void* obj, size_t size) {
|
||||
vluint8_t* oldp = ((vluint8_t*)obj)-8;
|
||||
*((void**)oldp) = s_freeHead;
|
||||
s_freeHead = oldp;
|
||||
}
|
||||
// MEMBERS
|
||||
static inline VerilatedVpio* castp(vpiHandle h) { return dynamic_cast<VerilatedVpio*>((VerilatedVpio*)h); }
|
||||
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
// ACCESSORS
|
||||
virtual const char* name() const { return "<null>"; }
|
||||
virtual const char* fullname() const { return "<null>"; }
|
||||
virtual const char* defname() const { return "<null>"; }
|
||||
virtual vluint32_t type() const { return 0; }
|
||||
virtual vluint32_t size() const { return 0; }
|
||||
virtual const VerilatedRange* rangep() const { return NULL; }
|
||||
virtual vpiHandle dovpi_scan() { return 0; }
|
||||
};
|
||||
|
||||
typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data *);
|
||||
|
||||
class VerilatedVpioCb : public VerilatedVpio {
|
||||
t_cb_data m_cbData;
|
||||
s_vpi_value m_value;
|
||||
QData m_time;
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpioCb(const t_cb_data* cbDatap, QData time)
|
||||
: m_cbData(*cbDatap), m_time(time) {
|
||||
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
||||
m_cbData.value = &m_value;
|
||||
}
|
||||
virtual ~VerilatedVpioCb() {}
|
||||
static inline VerilatedVpioCb* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioCb*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiCallback; }
|
||||
vluint32_t reason() const { return m_cbData.reason; }
|
||||
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
|
||||
t_cb_data* cb_datap() { return &(m_cbData); }
|
||||
QData time() const { return m_time; }
|
||||
};
|
||||
|
||||
class VerilatedVpioConst : public VerilatedVpio {
|
||||
vlsint32_t m_num;
|
||||
public:
|
||||
explicit VerilatedVpioConst(vlsint32_t num) : m_num(num) {}
|
||||
virtual ~VerilatedVpioConst() {}
|
||||
static inline VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioConst*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiUndefined; }
|
||||
vlsint32_t num() const { return m_num; }
|
||||
};
|
||||
|
||||
class VerilatedVpioRange : public VerilatedVpio {
|
||||
const VerilatedRange* m_range;
|
||||
vlsint32_t m_iteration;
|
||||
public:
|
||||
explicit VerilatedVpioRange(const VerilatedRange* range) : m_range(range), m_iteration(0) {}
|
||||
virtual ~VerilatedVpioRange() {}
|
||||
static inline VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioRange*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiRange; }
|
||||
virtual vluint32_t size() const { return m_range->elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return m_range; }
|
||||
int iteration() const { return m_iteration; }
|
||||
void iterationInc() { ++m_iteration; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
if (!iteration()) {
|
||||
VerilatedVpioRange* nextp = new VerilatedVpioRange(*this);
|
||||
nextp->iterationInc();
|
||||
return ((nextp)->castVpiHandle());
|
||||
} else {
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioScope : public VerilatedVpio {
|
||||
const VerilatedScope* m_scopep;
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep(scopep) {}
|
||||
virtual ~VerilatedVpioScope() {}
|
||||
static inline VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioScope*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual const char* name() const { return m_scopep->name(); }
|
||||
virtual const char* fullname() const { return m_scopep->name(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioVar : public VerilatedVpio {
|
||||
const VerilatedVar* m_varp;
|
||||
const VerilatedScope* m_scopep;
|
||||
vluint8_t* m_prevDatap; // Previous value of data, for cbValueChange
|
||||
union {
|
||||
vluint8_t u8[4];
|
||||
vluint32_t u32;
|
||||
} m_mask; // memoized variable mask
|
||||
vluint32_t m_entSize; // memoized variable size
|
||||
protected:
|
||||
void* m_varDatap; // varp()->datap() adjusted for array entries
|
||||
vlsint32_t m_index;
|
||||
const VerilatedRange& get_range() const {
|
||||
// Determine number of dimensions and return outermost
|
||||
return (m_varp->dims()>1) ? m_varp->array() : m_varp->range();
|
||||
}
|
||||
public:
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: m_varp(varp), m_scopep(scopep), m_index(0) {
|
||||
m_prevDatap = NULL;
|
||||
m_mask.u32 = VL_MASK_I(varp->range().elements());
|
||||
m_entSize = varp->entSize();
|
||||
m_varDatap = varp->datap();
|
||||
}
|
||||
virtual ~VerilatedVpioVar() {
|
||||
if (m_prevDatap) { delete [] m_prevDatap; m_prevDatap = NULL; }
|
||||
}
|
||||
static inline VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioVar*>((VerilatedVpio*)h); }
|
||||
const VerilatedVar* varp() const { return m_varp; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
vluint32_t mask() const { return m_mask.u32; }
|
||||
vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; }
|
||||
vluint32_t entSize() const { return m_entSize; }
|
||||
vluint32_t index() { return m_index; }
|
||||
virtual vluint32_t type() const {
|
||||
if (varp()->vldir() != vpiNoDirection) return vpiPort;
|
||||
return (varp()->dims()>1) ? vpiMemory : vpiReg; /* but might be wire, logic */
|
||||
}
|
||||
virtual vluint32_t size() const { return get_range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return &get_range(); }
|
||||
virtual const char* name() const { return m_varp->name(); }
|
||||
virtual const char* fullname() const {
|
||||
static VL_THREAD_LOCAL std::string out;
|
||||
out = std::string(m_scopep->name())+"."+name();
|
||||
return out.c_str();
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
m_prevDatap = new vluint8_t [entSize()];
|
||||
memcpy(prevDatap(), varp()->datap(), entSize());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioMemoryWord : public VerilatedVpioVar {
|
||||
public:
|
||||
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep,
|
||||
vlsint32_t index, int offset)
|
||||
: VerilatedVpioVar(varp, scopep) {
|
||||
m_index = index;
|
||||
m_varDatap = ((vluint8_t*)varp->datap()) + entSize()*offset;
|
||||
}
|
||||
virtual ~VerilatedVpioMemoryWord() {}
|
||||
static inline VerilatedVpioMemoryWord* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioMemoryWord*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiMemoryWord; }
|
||||
virtual vluint32_t size() const { return varp()->range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return &(varp()->range()); }
|
||||
virtual const char* fullname() const {
|
||||
static VL_THREAD_LOCAL std::string out;
|
||||
char num[20]; sprintf(num,"%d",m_index);
|
||||
out = std::string(scopep()->name())+"."+name()+"["+num+"]";
|
||||
return out.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioVarIter : public VerilatedVpio {
|
||||
const VerilatedScope* m_scopep;
|
||||
VerilatedVarNameMap::const_iterator m_it;
|
||||
bool m_started;
|
||||
public:
|
||||
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
|
||||
: m_scopep(scopep), m_started(false) { }
|
||||
virtual ~VerilatedVpioVarIter() {}
|
||||
static inline VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioVarIter*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
if (VL_LIKELY(m_scopep->varsp())) {
|
||||
VerilatedVarNameMap* varsp = m_scopep->varsp();
|
||||
if (VL_UNLIKELY(!m_started)) { m_it = varsp->begin(); m_started=true; }
|
||||
else if (VL_UNLIKELY(m_it == varsp->end())) return 0;
|
||||
else ++m_it;
|
||||
if (m_it == varsp->end()) return 0;
|
||||
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))
|
||||
->castVpiHandle());
|
||||
} else {
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioMemoryWordIter : public VerilatedVpio {
|
||||
const vpiHandle m_handle;
|
||||
const VerilatedVar* m_varp;
|
||||
vlsint32_t m_iteration;
|
||||
vlsint32_t m_direction;
|
||||
bool m_done;
|
||||
public:
|
||||
VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp)
|
||||
: m_handle(handle), m_varp(varp), m_iteration(varp->array().right()), m_direction(VL_LIKELY(varp->array().left()>varp->array().right())?1:-1), m_done(false) { }
|
||||
virtual ~VerilatedVpioMemoryWordIter() {}
|
||||
static inline VerilatedVpioMemoryWordIter* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioMemoryWordIter*>((VerilatedVpio*)h); }
|
||||
virtual vluint32_t type() const { return vpiIterator; }
|
||||
void iterationInc() {
|
||||
if (!(m_done = (m_iteration == m_varp->array().left()))) m_iteration+=m_direction;
|
||||
}
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
vpiHandle result;
|
||||
if (m_done) return 0;
|
||||
result = vpi_handle_by_index(m_handle, m_iteration);
|
||||
iterationInc();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
struct VerilatedVpiTimedCbsCmp {
|
||||
/// Ordering sets keyed by time, then callback descriptor
|
||||
bool operator() (const std::pair<QData,VerilatedVpioCb*>& a,
|
||||
const std::pair<QData,VerilatedVpioCb*>& b) const {
|
||||
if (a.first < b.first) return 1;
|
||||
if (a.first > b.first) return 0;
|
||||
return a.second < b.second;
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpiError;
|
||||
|
||||
class VerilatedVpi {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason
|
||||
typedef std::list<VerilatedVpioCb*> VpioCbList;
|
||||
typedef std::set<std::pair<QData,VerilatedVpioCb*>,VerilatedVpiTimedCbsCmp > VpioTimedCbs;
|
||||
|
||||
struct product_info {
|
||||
PLI_BYTE8* product;
|
||||
};
|
||||
|
||||
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||
VerilatedVpiError* m_errorInfop; // Container for vpi error info
|
||||
|
||||
static VerilatedVpi s_s; // Singleton
|
||||
|
||||
public:
|
||||
VerilatedVpi() { m_errorInfop=NULL; }
|
||||
~VerilatedVpi() {}
|
||||
static void cbReasonAdd(VerilatedVpioCb* vop) {
|
||||
if (vop->reason() == cbValueChange) {
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
||||
varop->createPrevDatap();
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) vl_fatal(__FILE__,__LINE__,"", "vpi bb reason too large");
|
||||
s_s.m_cbObjLists[vop->reason()].push_back(vop);
|
||||
}
|
||||
static void cbTimedAdd(VerilatedVpioCb* vop) {
|
||||
s_s.m_timedCbs.insert(std::make_pair(vop->time(), vop));
|
||||
}
|
||||
static void cbReasonRemove(VerilatedVpioCb* cbp) {
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()];
|
||||
// We do not remove it now as we may be iterating the list,
|
||||
// instead set to NULL and will cleanup later
|
||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end(); ++it) {
|
||||
if (*it == cbp) *it = NULL;
|
||||
}
|
||||
}
|
||||
static void cbTimedRemove(VerilatedVpioCb* cbp) {
|
||||
VpioTimedCbs::iterator it=s_s.m_timedCbs.find(std::make_pair(cbp->time(),cbp));
|
||||
if (VL_LIKELY(it != s_s.m_timedCbs.end())) {
|
||||
s_s.m_timedCbs.erase(it);
|
||||
}
|
||||
}
|
||||
static void callTimedCbs() {
|
||||
QData time = VL_TIME_Q();
|
||||
for (VpioTimedCbs::iterator it=s_s.m_timedCbs.begin(); it!=s_s.m_timedCbs.end(); ) {
|
||||
if (VL_UNLIKELY(it->first <= time)) {
|
||||
VerilatedVpioCb* vop = it->second;
|
||||
++it; // iterator may be deleted by callback
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: timed_callback %p\n",vop););
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
else { ++it; }
|
||||
}
|
||||
}
|
||||
static QData cbNextDeadline() {
|
||||
VpioTimedCbs::const_iterator it=s_s.m_timedCbs.begin();
|
||||
if (VL_LIKELY(it!=s_s.m_timedCbs.end())) {
|
||||
return it->first;
|
||||
} else {
|
||||
return ~VL_ULL(0); // maxquad
|
||||
}
|
||||
}
|
||||
static void callCbs(vluint32_t reason) {
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[reason];
|
||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) {
|
||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
continue;
|
||||
}
|
||||
VerilatedVpioCb* vop = *it++;
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: reason_callback %d %p\n",reason,vop););
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
}
|
||||
static void callValueCbs() {
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange];
|
||||
typedef std::set<VerilatedVpioVar*> VpioVarSet;
|
||||
VpioVarSet update; // set of objects to update after callbacks
|
||||
for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) {
|
||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
continue;
|
||||
}
|
||||
VerilatedVpioCb* vop = *it++;
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
||||
void* newDatap = varop->varDatap();
|
||||
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap),
|
||||
newDatap, prevDatap););
|
||||
if (memcmp(prevDatap, newDatap, varop->entSize())) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: value_callback %p %s v[0]=%d\n",
|
||||
vop,varop->fullname(), *((CData*)newDatap)););
|
||||
update.insert(varop);
|
||||
vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value);
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (VpioVarSet::const_iterator it=update.begin(); it!=update.end(); ++it) {
|
||||
memcpy((*it)->prevDatap(), (*it)->varDatap(), (*it)->entSize());
|
||||
}
|
||||
}
|
||||
|
||||
static VerilatedVpiError* error_info(); // getter for vpi error info
|
||||
/// Call timed callbacks
|
||||
/// Users should call this from their main loops
|
||||
static void callTimedCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Call value based callbacks
|
||||
/// Users should call this from their main loops
|
||||
static void callValueCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Self test, for internal use only
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
#define _VL_VPI_ERROR_SET \
|
||||
do { \
|
||||
va_list args; \
|
||||
va_start(args, message); \
|
||||
VL_VSNPRINTF(m_buff, sizeof(m_buff), message.c_str(), args); \
|
||||
va_end(args); \
|
||||
} while (0)
|
||||
|
||||
class VerilatedVpiError {
|
||||
//// Container for vpi error info
|
||||
|
||||
t_vpi_error_info m_errorInfo;
|
||||
bool m_flag;
|
||||
char m_buff[VL_VPI_LINE_SIZE];
|
||||
void setError(PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) {
|
||||
m_errorInfo.message = message;
|
||||
m_errorInfo.file = file;
|
||||
m_errorInfo.line = line;
|
||||
m_errorInfo.code = code;
|
||||
do_callbacks();
|
||||
}
|
||||
void do_callbacks() {
|
||||
if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) {
|
||||
// Stop on vpi error/unsupported
|
||||
vpi_unsupported();
|
||||
}
|
||||
// We need to run above code first because in the case that the callback executes further vpi
|
||||
// functions we will loose the error as it will be overwritten.
|
||||
VerilatedVpi::callCbs(cbPLIError);
|
||||
}
|
||||
public:
|
||||
|
||||
VerilatedVpiError() : m_flag(false) {
|
||||
m_buff[0] = '\0';
|
||||
m_errorInfo.product = (PLI_BYTE8*)Verilated::productName();
|
||||
}
|
||||
~VerilatedVpiError() {}
|
||||
static void selfTest();
|
||||
VerilatedVpiError* setMessage(PLI_INT32 level) {
|
||||
m_flag=true;
|
||||
m_errorInfo.level = level;
|
||||
return this;
|
||||
}
|
||||
void setMessage(std::string file, PLI_INT32 line, std::string message, ...) {
|
||||
static VL_THREAD_LOCAL std::string filehold;
|
||||
_VL_VPI_ERROR_SET;
|
||||
m_errorInfo.state = vpiPLI;
|
||||
filehold = file;
|
||||
setError((PLI_BYTE8*)m_buff, NULL, (PLI_BYTE8*)filehold.c_str(), line);
|
||||
}
|
||||
p_vpi_error_info getError() {
|
||||
if (m_flag) return &m_errorInfo;
|
||||
return NULL;
|
||||
}
|
||||
void resetError() {
|
||||
m_flag=false;
|
||||
}
|
||||
static void vpi_unsupported() {
|
||||
// Not supported yet
|
||||
p_vpi_error_info error_info_p = VerilatedVpi::error_info()->getError();
|
||||
if (error_info_p) {
|
||||
vl_fatal(error_info_p->file, error_info_p->line, "", error_info_p->message);
|
||||
return;
|
||||
}
|
||||
vl_fatal(__FILE__, __LINE__, "", "vpi_unsupported called without error info set");
|
||||
}
|
||||
static const char* strFromVpiVal(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiObjType(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiMethod(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiProp(PLI_INT32 vpiVal);
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+78
-16
@@ -42,43 +42,84 @@
|
||||
# else
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, fmtArgNum, fmtArgNum+1)))
|
||||
# endif
|
||||
# define VL_ATTR_PURE __attribute__ ((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__ ((unused))
|
||||
# define VL_FUNC __func__
|
||||
# if defined(__clang__) && defined(VL_THREADED)
|
||||
# define VL_ACQUIRE(...) __attribute__ ((acquire_capability(__VA_ARGS__)))
|
||||
# define VL_ACQUIRE_SHARED(...) __attribute__ ((acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE(...) __attribute__ ((release_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE_SHARED(...) __attribute__ ((release_shared_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE(...) __attribute__ ((try_acquire_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__ ((try_acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_CAPABILITY(x) __attribute__ ((capability(x)))
|
||||
# define VL_REQUIRES(x) __attribute__ ((requires_capability(x)))
|
||||
# define VL_GUARDED_BY(x) __attribute__ ((guarded_by(x)))
|
||||
# define VL_EXCLUDES(x) __attribute__ ((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__ ((scoped_lockable))
|
||||
# endif
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define VL_UNREACHABLE __builtin_unreachable();
|
||||
# define VL_PREFETCH_RD(p) __builtin_prefetch((p),0)
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p),1)
|
||||
#elif defined(_MSC_VER)
|
||||
# define VL_ATTR_ALIGNED(alignment)
|
||||
# define VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_NORETURN
|
||||
# define VL_ATTR_PRINTF(fmtArgNum)
|
||||
# define VL_ATTR_UNUSED
|
||||
# define VL_FUNC __FUNCTION__
|
||||
# define VL_LIKELY(x) (!!(x))
|
||||
# define VL_UNLIKELY(x) (!!(x))
|
||||
# define VL_UNREACHABLE
|
||||
# define VL_PREFETCH_RD(p)
|
||||
# define VL_PREFETCH_RW(p)
|
||||
#else
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALIGNED
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
|
||||
#endif
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||
#endif
|
||||
#ifndef VL_ATTR_NORETURN
|
||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||
#endif
|
||||
#ifndef VL_ATTR_PRINTF
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
|
||||
#endif
|
||||
#ifndef VL_ATTR_PURE
|
||||
# define VL_ATTR_PURE ///< Function is pure (and thus also VL_MT_SAFE)
|
||||
#endif
|
||||
#ifndef VL_ATTR_UNUSED
|
||||
# define VL_ATTR_UNUSED ///< Function that may be never used
|
||||
#endif
|
||||
#ifndef VL_FUNC
|
||||
# define VL_FUNC "__func__" ///< Name of current function for error macros
|
||||
#endif
|
||||
#ifndef VL_CAPABILITY
|
||||
# define VL_ACQUIRE(...) ///< Function requires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function aquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if aquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if aquired a shared capability (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
#endif
|
||||
#ifndef VL_LIKELY
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
#endif
|
||||
#ifndef VL_UNREACHABLE
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RD
|
||||
# define VL_PREFETCH_RD(p) ///< Prefetch data with read intent
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RW
|
||||
# define VL_PREFETCH_RW(p) ///< Prefetch data with read/write intent
|
||||
#endif
|
||||
|
||||
#ifdef VL_THREADED
|
||||
# ifdef __GNUC__
|
||||
# if (__cplusplus < 201103L) && !defined(VL_THREADED_NO_C11_WARNING)
|
||||
# error "VL_THREADED support plans to move to C++-11 and later only; use newer --std to be ready"
|
||||
# error "VL_THREADED/--threads support requires C++-11 or newer only; use newer compiler"
|
||||
# endif
|
||||
# else
|
||||
# error "Unsupported compiler for VL_THREADED: No thread-local declarator"
|
||||
@@ -90,6 +131,12 @@
|
||||
#define VL_THREAD ///< Deprecated
|
||||
#define VL_STATIC_OR_THREAD static ///< Deprecated
|
||||
|
||||
#define VL_PURE ///< Comment tag that Function is pure (and thus also VL_MT_SAFE)
|
||||
#define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED
|
||||
#define VL_MT_SAFE_POSTINIT ///< Comment tag that function is threadsafe when VL_THREADED, only during normal operation (post-init)
|
||||
#define VL_MT_UNSAFE ///< Comment tag that function is not threadsafe when VL_THREADED
|
||||
#define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, protected to make sure single-caller
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define VL_ULL(c) (c##ui64) ///< Add appropriate suffix to 64-bit constant
|
||||
#else
|
||||
@@ -109,15 +156,22 @@
|
||||
// C++-2011
|
||||
|
||||
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
# define VL_EQ_DELETE = delete
|
||||
# define VL_HAS_UNIQUE_PTR
|
||||
# define VL_HAS_UNORDERED_MAP
|
||||
# define VL_UNIQUE_PTR std::unique_ptr
|
||||
# define VL_UNORDERED_MAP std::unordered_map
|
||||
# define VL_HAS_UNORDERED_SET
|
||||
# define vl_unique_ptr std::unique_ptr
|
||||
# define vl_unordered_map std::unordered_map
|
||||
# define vl_unordered_set std::unordered_set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
|
||||
#else
|
||||
# define VL_UNIQUE_PTR std::auto_ptr
|
||||
# define VL_UNORDERED_MAP std::map
|
||||
# define VL_EQ_DELETE
|
||||
# define vl_unique_ptr std::auto_ptr
|
||||
# define vl_unordered_map std::map
|
||||
# define vl_unordered_set std::set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <set>
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
@@ -257,6 +311,14 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
/// Words this number of bits needs (1 bit=1 word)
|
||||
#define VL_WORDS_I(nbits) (((nbits)+(VL_WORDSIZE-1))/VL_WORDSIZE)
|
||||
|
||||
//=========================================================================
|
||||
// Class definition helpers
|
||||
|
||||
// Used to declare a class as uncopyable; put after a private:
|
||||
#define VL_UNCOPYABLE(Type) \
|
||||
Type(const Type& other) VL_EQ_DELETE; \
|
||||
Type& operator= (const Type&) VL_EQ_DELETE
|
||||
|
||||
//=========================================================================
|
||||
// Verilated function size macros
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
|
||||
else
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_SRC)
|
||||
endif
|
||||
|
||||
CPPFLAGSWALL = $(CPPFLAGS)
|
||||
CPPFLAGSPARSER = $(CPPFLAGS) $(CFG_CXXFLAGS_PARSER)
|
||||
|
||||
|
||||
+7
-1
@@ -67,7 +67,7 @@ private:
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
}
|
||||
nodep->fmtp()->exprsp(timenewp);
|
||||
nodep->fmtp()->addExprsp(timenewp);
|
||||
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
@@ -202,6 +202,9 @@ private:
|
||||
AstNode *newifp = nodep->cloneTree(false);
|
||||
bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||
|
||||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
@@ -259,6 +262,9 @@ private:
|
||||
else propp = onep;
|
||||
}
|
||||
}
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
||||
|
||||
bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* ohot = (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
|
||||
+1
-1
@@ -1037,7 +1037,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
|
||||
if (doDump) {
|
||||
{ // Write log & close
|
||||
UINFO(2,"Dumping "<<filename<<endl);
|
||||
const VL_UNIQUE_PTR<ofstream> logsp (V3File::new_ofstream(filename, append));
|
||||
const vl_unique_ptr<ofstream> logsp (V3File::new_ofstream(filename, append));
|
||||
if (logsp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
*logsp<<"Verilator Tree Dump (format 0x3900) from <e"<<dec<<editCountLast()<<">";
|
||||
*logsp<<" to <e"<<dec<<editCountGbl()<<">"<<endl;
|
||||
|
||||
+62
-26
@@ -208,14 +208,37 @@ public:
|
||||
"COMBO","INITIAL","SETTLE","NEVER"
|
||||
};
|
||||
return names[m_e];
|
||||
};
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[] = {
|
||||
"%E-edge", "[any]", "edge", "posedge", "negedge", "[high]","[low]",
|
||||
"*","[initial]","[settle]","[never]"
|
||||
};
|
||||
return names[m_e];
|
||||
};
|
||||
}
|
||||
// Return true iff this and the other have mutually exclusive transitions
|
||||
bool exclusiveEdge(const AstEdgeType& other) const {
|
||||
switch (m_e) {
|
||||
case AstEdgeType::ET_POSEDGE:
|
||||
switch (other.m_e) {
|
||||
case AstEdgeType::ET_NEGEDGE: // FALLTHRU
|
||||
case AstEdgeType::ET_LOWEDGE:
|
||||
return true;
|
||||
default: {}
|
||||
}
|
||||
break;
|
||||
case AstEdgeType::ET_NEGEDGE:
|
||||
switch (other.m_e) {
|
||||
case AstEdgeType::ET_POSEDGE: // FALLTHRU
|
||||
case AstEdgeType::ET_HIGHEDGE:
|
||||
return true;
|
||||
default: {}
|
||||
}
|
||||
break;
|
||||
default: {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline AstEdgeType () : m_e(ET_ILLEGAL) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstEdgeType (en _e) : m_e(_e) {}
|
||||
@@ -340,7 +363,7 @@ public:
|
||||
};
|
||||
return names[m_e];
|
||||
};
|
||||
static void test() {
|
||||
static void selfTest() {
|
||||
UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).ascii()," MAX"),"Enum array mismatch");
|
||||
UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).dpiType()," MAX"),"Enum array mismatch");
|
||||
}
|
||||
@@ -658,10 +681,14 @@ struct VNumRange {
|
||||
return false;
|
||||
}
|
||||
//
|
||||
class LeftRight {};
|
||||
VNumRange() : m_hi(0), m_lo(0), mu_flags(0) {}
|
||||
VNumRange(int hi, int lo, bool littleEndian)
|
||||
: m_hi(0), m_lo(0), mu_flags(0)
|
||||
{ init(hi,lo,littleEndian); }
|
||||
VNumRange(LeftRight, int left, int right)
|
||||
: m_hi(0), m_lo(0), mu_flags(0)
|
||||
{ init((right>left)?right:left, (right>left)?left:right, (right>left)); }
|
||||
~VNumRange() {}
|
||||
// MEMBERS
|
||||
void init(int hi, int lo, bool littleEndian) {
|
||||
@@ -919,7 +946,7 @@ public:
|
||||
inline bool operator== (const V3Hash& rh) const { return m_both==rh.m_both; }
|
||||
inline bool operator!= (const V3Hash& rh) const { return m_both!=rh.m_both; }
|
||||
inline bool operator< (const V3Hash& rh) const { return m_both<rh.m_both; }
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
class Illegal {}; // for creator type-overload selection
|
||||
class FullValue {}; // for creator type-overload selection
|
||||
explicit V3Hash(Illegal) { m_both=0; }
|
||||
@@ -1287,7 +1314,7 @@ public:
|
||||
virtual bool isUnlikely() const { return false; } // Else $stop or similar statement which means an above IF statement is unlikely to be taken
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash::Illegal()); } // Not a node that supports it
|
||||
virtual bool same(AstNode* otherp) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
virtual bool hasDType() const { return false; } // Iff has a data type; dtype() must be non null
|
||||
virtual AstNodeDType* getChildDTypep() const { return NULL; } // Iff has a non-null childDTypep(), as generic node function
|
||||
virtual bool maybePointedTo() const { return false; } // Another AstNode* may have a pointer into this node, other then normal front/back/etc.
|
||||
@@ -1369,7 +1396,7 @@ public:
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
class AstNodeBiop : public AstNodeMath {
|
||||
@@ -1396,7 +1423,7 @@ public:
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
class AstNodeTriop : public AstNodeMath {
|
||||
@@ -1422,7 +1449,7 @@ public:
|
||||
virtual bool sizeMattersThs() = 0; // True if output result depends on ths size
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
class AstNodeBiCom : public AstNodeBiop {
|
||||
@@ -1482,7 +1509,7 @@ public:
|
||||
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
|
||||
// METHODS
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
class AstNodeStmt : public AstNode {
|
||||
@@ -1514,7 +1541,7 @@ public:
|
||||
virtual bool cleanRhs() { return true; }
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
virtual string verilogKwd() const { return "="; }
|
||||
virtual bool brokeLhsMustBeLvalue() const = 0;
|
||||
};
|
||||
@@ -1534,7 +1561,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstNodeIf : public AstNodeStmt {
|
||||
@@ -1556,7 +1583,7 @@ public:
|
||||
virtual bool isGateDedupable() const { return true; }
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
void branchPred(AstBranchPred flag) { m_branchPred = flag; }
|
||||
AstBranchPred branchPred() const { return m_branchPred; }
|
||||
};
|
||||
@@ -1648,8 +1675,9 @@ public:
|
||||
ASTNODE_BASE_FUNCS(NodeText)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return text()==samep->castNodeText()->text(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
|
||||
return text() == asamep->text(); }
|
||||
const string& text() const { return m_text; }
|
||||
};
|
||||
|
||||
@@ -1684,6 +1712,7 @@ public:
|
||||
virtual AstNodeDType* virtRefDTypep() const { return NULL; } // Iff has a non-null refDTypep(), as generic node function
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { } // Iff has refDTypep(), set as generic node function
|
||||
virtual bool similarDType(AstNodeDType* samep) const = 0; // Assignable equivalence. Call skipRefp() on this and samep before calling
|
||||
virtual AstNodeDType* subDTypep() const { return NULL; } // Iff has a non-null subDTypep(), as generic node function
|
||||
//
|
||||
// Changing the width may confuse the data type resolution, so must clear TypeTable cache after use.
|
||||
void widthForce(int width, int sized) { m_width=width; m_widthMin=sized; }
|
||||
@@ -1770,24 +1799,24 @@ public:
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep();
|
||||
}}
|
||||
virtual bool same(AstNode* samep) const {
|
||||
AstNodeArrayDType* sp = samep->castNodeArrayDType();
|
||||
return (msb()==sp->msb()
|
||||
&& subDTypep()==sp->subDTypep()
|
||||
&& rangenp()->sameTree(sp->rangenp())); } // HashedDT doesn't recurse, so need to check children
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return (msb()==asamep->msb()
|
||||
&& subDTypep()==asamep->subDTypep()
|
||||
&& rangenp()->sameTree(asamep->rangenp())); } // HashedDT doesn't recurse, so need to check children
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
AstNodeArrayDType* sp = samep->castNodeArrayDType();
|
||||
return (sp
|
||||
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return (asamep
|
||||
&& type() == samep->type()
|
||||
&& msb() == sp->msb()
|
||||
&& rangenp()->sameTree(sp->rangenp())
|
||||
&& subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp()));
|
||||
&& msb() == asamep->msb()
|
||||
&& rangenp()->sameTree(asamep->rangenp())
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(msb()),V3Hash(lsb())); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
@@ -1952,6 +1981,8 @@ private:
|
||||
bool m_inLibrary:1; // From a library, no error if not used, never top level
|
||||
bool m_dead:1; // LinkDot believes is dead; will remove in Dead visitors
|
||||
bool m_internal:1; // Internally created
|
||||
bool m_recursive:1; // Recursive module
|
||||
bool m_recursiveClone:1; // If recursive, what module it clones, otherwise NULL
|
||||
int m_level; // 1=top module, 2=cell off top module, ...
|
||||
int m_varNum; // Incrementing variable number
|
||||
int m_typeNum; // Incrementing implicit type number
|
||||
@@ -1959,7 +1990,8 @@ public:
|
||||
AstNodeModule(FileLine* fl, const string& name)
|
||||
: AstNode (fl)
|
||||
,m_name(name), m_origName(name)
|
||||
,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false), m_internal(false)
|
||||
,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false)
|
||||
,m_internal(false), m_recursive(false), m_recursiveClone(false)
|
||||
,m_level(0), m_varNum(0), m_typeNum(0) { }
|
||||
ASTNODE_BASE_FUNCS(NodeModule)
|
||||
virtual void dump(ostream& str);
|
||||
@@ -1989,6 +2021,10 @@ public:
|
||||
bool dead() const { return m_dead; }
|
||||
void internal(bool flag) { m_internal = flag; }
|
||||
bool internal() const { return m_internal; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
void recursiveClone(bool flag) { m_recursiveClone = flag; }
|
||||
bool recursiveClone() const { return m_recursiveClone; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
+15
-5
@@ -26,6 +26,7 @@
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
@@ -69,7 +70,7 @@ void AstNodeClassDType::repairMemberCache() {
|
||||
}
|
||||
|
||||
const char* AstNodeClassDType::broken() const {
|
||||
set<AstMemberDType*> exists;
|
||||
vl_unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
@@ -548,28 +549,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
return out;
|
||||
}
|
||||
|
||||
bool AstSenTree::hasClocked() {
|
||||
bool AstSenTree::hasClocked() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
if (senp->isClocked()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() {
|
||||
bool AstSenTree::hasSettle() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
if (senp->isSettle()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasInitial() {
|
||||
bool AstSenTree::hasInitial() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
if (senp->isInitial()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() {
|
||||
bool AstSenTree::hasCombo() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
if (senp->isCombo()) return true;
|
||||
@@ -778,6 +779,7 @@ void AstCCast::dump(ostream& str) {
|
||||
}
|
||||
void AstCell::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (recursive()) str<<" [RECURSIVE]";
|
||||
if (modp()) { str<<" -> "; modp()->dump(str); }
|
||||
else { str<<" ->UNLINKED:"<<modName(); }
|
||||
}
|
||||
@@ -886,6 +888,8 @@ void AstNodeModule::dump(ostream& str) {
|
||||
if (modPublic()) str<<" [P]";
|
||||
if (inLibrary()) str<<" [LIB]";
|
||||
if (dead()) str<<" [DEAD]";
|
||||
if (recursiveClone()) str<<" [RECURSIVE-CLONE]";
|
||||
else if (recursive()) str<<" [RECURSIVE]";
|
||||
}
|
||||
void AstPackageExport::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
@@ -902,6 +906,12 @@ void AstSel::dump(ostream& str) {
|
||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||
}
|
||||
}
|
||||
void AstSliceSel::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl"<<declRange();
|
||||
}
|
||||
}
|
||||
void AstTypeTable::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
|
||||
+184
-119
@@ -100,8 +100,9 @@ public:
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(num().toHash()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return num().isCaseEq(samep->castConst()->num()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstConst* sp = static_cast<const AstConst*>(samep);
|
||||
return num().isCaseEq(sp->num()); }
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
|
||||
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
|
||||
@@ -142,7 +143,7 @@ public:
|
||||
virtual void dump(ostream& str);
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstGatePin : public AstNodeMath {
|
||||
@@ -178,7 +179,7 @@ public:
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
|
||||
virtual AstNodeDType* skipRefp() const { return subDTypep()->skipRefp(); }
|
||||
virtual AstNodeDType* skipRefToConstp() const { return subDTypep()->skipRefToConstp(); }
|
||||
@@ -203,6 +204,7 @@ class AstTypedef : public AstNode {
|
||||
private:
|
||||
string m_name;
|
||||
bool m_attrPublic;
|
||||
string m_tag; // Holds the string of the verilator tag -- used in XML output.
|
||||
public:
|
||||
AstTypedef(FileLine* fl, const string& name, AstNode* attrsp, VFlagChildDType, AstNodeDType* dtp)
|
||||
: AstNode(fl), m_name(name) {
|
||||
@@ -216,7 +218,7 @@ public:
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
void addAttrsp(AstNode* nodep) { addNOp4p(nodep); }
|
||||
AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse
|
||||
// METHODS
|
||||
@@ -226,6 +228,8 @@ public:
|
||||
void name(const string& flag) { m_name = flag; }
|
||||
bool attrPublic() const { return m_attrPublic; }
|
||||
void attrPublic(bool flag) { m_attrPublic = flag; }
|
||||
virtual void tag(const string& text) { m_tag = text;}
|
||||
virtual string tag() const { return m_tag; }
|
||||
};
|
||||
|
||||
class AstTypedefFwd : public AstNode {
|
||||
@@ -257,14 +261,16 @@ public:
|
||||
m_uniqueNum = uniqueNumInc();
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(DefImplicitDType)
|
||||
virtual bool same(AstNode* samep) const { return m_uniqueNum==samep->castDefImplicitDType()->m_uniqueNum; }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstDefImplicitDType* sp = static_cast<const AstDefImplicitDType*>(samep);
|
||||
return m_uniqueNum == sp->m_uniqueNum; }
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return type()==samep->type() && same(samep); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
void* containerp() const { return m_containerp; }
|
||||
// METHODS
|
||||
AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable
|
||||
@@ -399,8 +405,9 @@ public:
|
||||
ASTNODE_NODE_FUNCS(BasicDType)
|
||||
virtual void dump(ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m.m_keyword), V3Hash(m.m_nrange.hi())); }
|
||||
virtual bool same(AstNode* samep) const { // width/widthMin/numeric compared elsewhere
|
||||
return samep->castBasicDType()->m == m; }
|
||||
virtual bool same(const AstNode* samep) const { // width/widthMin/numeric compared elsewhere
|
||||
const AstBasicDType* sp = static_cast<const AstBasicDType*>(samep);
|
||||
return m == sp->m; }
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return type()==samep->type() && same(samep); }
|
||||
virtual string name() const { return m.m_keyword.ascii(); }
|
||||
@@ -465,15 +472,16 @@ public:
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep();
|
||||
}}
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (m_refDTypep==samep->castConstDType()->m_refDTypep); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstConstDType* sp = static_cast<const AstConstDType*>(samep);
|
||||
return (m_refDTypep == sp->m_refDTypep); }
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return skipRefp()->similarDType(samep->skipRefp()); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); } // node's type() included elsewhere
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
@@ -550,10 +558,11 @@ public:
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep();
|
||||
}}
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (m_refDTypep==samep->castRefDType()->m_refDTypep
|
||||
&& m_name==samep->castRefDType()->m_name
|
||||
&& m_packagep==samep->castRefDType()->m_packagep); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstRefDType* asamep = static_cast<const AstRefDType*>(samep);
|
||||
return (m_refDTypep == asamep->m_refDTypep
|
||||
&& m_name == asamep->m_name
|
||||
&& m_packagep == asamep->m_packagep); }
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return skipRefp()->similarDType(samep->skipRefp()); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(m_packagep)); }
|
||||
@@ -582,7 +591,7 @@ public:
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep=nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return refDTypep(); }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
AstNodeDType* subDTypep() const { return m_refDTypep; }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep; }
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
void packagep(AstPackage* nodep) { m_packagep=nodep; }
|
||||
};
|
||||
@@ -636,7 +645,7 @@ public:
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
@@ -691,8 +700,9 @@ public:
|
||||
virtual const char* broken() const { BROKEN_RTN(!itemp()); return NULL; }
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = m_itemp->clonep()->castEnumItem(); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return itemp()==samep->castEnumItemRef()->itemp(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstEnumItemRef* sp = static_cast<const AstEnumItemRef*>(samep);
|
||||
return itemp() == sp->itemp(); }
|
||||
AstEnumItem* itemp() const { return m_itemp; }
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
@@ -723,13 +733,15 @@ public:
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep();
|
||||
}}
|
||||
virtual bool same(AstNode* samep) const { return m_uniqueNum==samep->castEnumDType()->m_uniqueNum; }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstEnumDType* sp = static_cast<const AstEnumDType*>(samep);
|
||||
return m_uniqueNum == sp->m_uniqueNum; }
|
||||
virtual bool similarDType(AstNodeDType* samep) const { return this==samep; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Data type
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
@@ -796,7 +808,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return true; } // esp for V3Const::ifSameAssign
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
// Special operators
|
||||
static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences?
|
||||
@@ -818,7 +830,7 @@ public:
|
||||
virtual bool cleanLhs() { return true; } virtual bool cleanRhs() { return true; }
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstSelExtract : public AstNodePreSel {
|
||||
@@ -905,7 +917,7 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual bool sizeMattersThs() {return false;}
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
virtual int instrCount() const { return widthInstrs()*(lsbp()->castConst()?3:10); }
|
||||
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
|
||||
AstNode* lsbp() const { return op2p(); } // op2 = Msb selection expression
|
||||
@@ -919,6 +931,40 @@ public:
|
||||
void declElWidth(int flag) { m_declElWidth = flag; }
|
||||
};
|
||||
|
||||
class AstSliceSel : public AstNodeTriop {
|
||||
// Multiple array element extraction
|
||||
// Parents: math|stmt
|
||||
// Children: varref|arraysel, math, constant math
|
||||
private:
|
||||
VNumRange m_declRange; // Range of the 'from' array if isRanged() is set, else invalid
|
||||
public:
|
||||
AstSliceSel(FileLine* fl, AstNode* fromp, const VNumRange& declRange)
|
||||
: AstNodeTriop(fl, fromp,
|
||||
new AstConst(fl, declRange.lo()),
|
||||
new AstConst(fl, declRange.elements()))
|
||||
, m_declRange(declRange) { }
|
||||
ASTNODE_NODE_FUNCS(SliceSel)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& lo, const V3Number& width) {
|
||||
V3ERROR_NA; }
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
virtual string emitC() { V3ERROR_NA; return ""; } // Removed before EmitC
|
||||
virtual bool cleanOut() { return false; }
|
||||
virtual bool cleanLhs() { return false; }
|
||||
virtual bool cleanRhs() { return true; }
|
||||
virtual bool cleanThs() { return true; }
|
||||
virtual bool sizeMattersLhs() { return false; }
|
||||
virtual bool sizeMattersRhs() { return false; }
|
||||
virtual bool sizeMattersThs() { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
virtual int instrCount() const { return 10; } // Removed before matters
|
||||
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
|
||||
// For widthConst()/loConst etc, see declRange().elements() and other VNumRange methods
|
||||
VNumRange& declRange() { return m_declRange; }
|
||||
void declRange(const VNumRange& flag) { m_declRange = flag; }
|
||||
};
|
||||
|
||||
class AstMemberSel : public AstNodeMath {
|
||||
// Parents: math|stmt
|
||||
// Children: varref|arraysel, math
|
||||
@@ -944,7 +990,7 @@ public:
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { return false; }
|
||||
virtual bool same(AstNode* samep) const { return true; } // dtype comparison does it all for us
|
||||
virtual bool same(const AstNode* samep) const { return true; } // dtype comparison does it all for us
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
|
||||
void fromp(AstNode* nodep) { setOp1p(nodep); }
|
||||
@@ -1094,7 +1140,7 @@ public:
|
||||
void addAttrsp(AstNode* nodep) { addNOp4p(nodep); }
|
||||
AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
void attrClockEn(bool flag) { m_attrClockEn = flag; }
|
||||
void attrClocker(AstVarAttrClocker flag) { m_attrClocker = flag; }
|
||||
void attrFileDescr(bool flag) { m_fileDescr = flag; }
|
||||
@@ -1227,7 +1273,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(DefParam)
|
||||
virtual bool cleanRhs() { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
AstNode* rhsp() const { return op1p(); } // op1 = Assign from
|
||||
string path() const { return m_path; }
|
||||
};
|
||||
@@ -1348,8 +1394,9 @@ public:
|
||||
ASTNODE_NODE_FUNCS(VarRef)
|
||||
virtual void dump(ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()->name()),V3Hash(hiername())); }
|
||||
virtual bool same(AstNode* samep) const { return same(samep->castVarRef()); }
|
||||
inline bool same(AstVarRef* samep) const {
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep)); }
|
||||
inline bool same(const AstVarRef* samep) const {
|
||||
if (varScopep()) return (varScopep()==samep->varScopep()
|
||||
&& lvalue()==samep->lvalue());
|
||||
else return (hiername()==samep->hiername()
|
||||
@@ -1392,11 +1439,12 @@ public:
|
||||
virtual bool cleanOut() { return true; }
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()),V3Hash(dotted())); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (hiername()==samep->castVarXRef()->hiername()
|
||||
&& varp()==samep->castVarXRef()->varp()
|
||||
&& name()==samep->castVarXRef()->name()
|
||||
&& dotted()==samep->castVarXRef()->dotted()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstVarXRef* asamep = static_cast<const AstVarXRef*>(samep);
|
||||
return (hiername() == asamep->hiername()
|
||||
&& varp() == asamep->varp()
|
||||
&& name() == asamep->name()
|
||||
&& dotted() == asamep->dotted()); }
|
||||
};
|
||||
|
||||
class AstPin : public AstNode {
|
||||
@@ -1625,13 +1673,14 @@ private:
|
||||
string m_modName; // Module the cell instances
|
||||
AstNodeModule* m_modp; // [AfterLink] Pointer to module instanced
|
||||
bool m_hasIfaceVar:1; // True if a Var has been created for this cell
|
||||
bool m_recursive:1; // Self-recursive module
|
||||
bool m_trace:1; // Trace this cell
|
||||
public:
|
||||
AstCell(FileLine* fl, const string& instName, const string& modName,
|
||||
AstPin* pinsp, AstPin* paramsp, AstRange* rangep)
|
||||
: AstNode(fl)
|
||||
, m_name(instName), m_origName(instName), m_modName(modName)
|
||||
, m_modp(NULL), m_hasIfaceVar(false), m_trace(true) {
|
||||
, m_modp(NULL), m_hasIfaceVar(false), m_recursive(false), m_trace(true) {
|
||||
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
|
||||
ASTNODE_NODE_FUNCS(Cell)
|
||||
// No cloneRelink, we presume cloneee's want the same module linkages
|
||||
@@ -1656,6 +1705,8 @@ public:
|
||||
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
|
||||
void trace(bool flag) { m_trace=flag; }
|
||||
bool isTrace() const { return m_trace; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
};
|
||||
|
||||
class AstCellInline : public AstNode {
|
||||
@@ -1794,9 +1845,10 @@ public:
|
||||
virtual void dump(ostream& str);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect),V3Hash(m_name)); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (expect() == samep->castParseRef()->expect()
|
||||
&& m_name==samep->castParseRef()->m_name); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstParseRef* asamep = static_cast<const AstParseRef*>(samep);
|
||||
return (expect() == asamep->expect()
|
||||
&& m_name == asamep->m_name); }
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
@@ -1820,8 +1872,8 @@ public:
|
||||
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) {
|
||||
m_packagep = m_packagep->clonep();
|
||||
}}
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (m_packagep==samep->castPackageRef()->m_packagep); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return (m_packagep==static_cast<const AstPackageRef*>(samep)->m_packagep); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_packagep); }
|
||||
virtual void dump(ostream& str=cout);
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
@@ -1945,8 +1997,8 @@ public:
|
||||
ASTNODE_NODE_FUNCS(SenItem)
|
||||
virtual void dump(ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(edgeType()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return edgeType()==samep->castSenItem()->edgeType(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return edgeType()==static_cast<const AstSenItem*>(samep)->edgeType(); }
|
||||
AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge
|
||||
void edgeType(AstEdgeType type) { m_edgeType=type; editCountInc(); }// * = Posedge/negedge
|
||||
AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized
|
||||
@@ -2003,10 +2055,10 @@ public:
|
||||
void addSensesp(AstNodeSenItem* nodep) { addOp1p(nodep); }
|
||||
void multi(bool flag) { m_multi = true; }
|
||||
// METHODS
|
||||
bool hasClocked(); // Includes a clocked statement
|
||||
bool hasSettle(); // Includes a SETTLE SenItem
|
||||
bool hasInitial(); // Includes a INITIAL SenItem
|
||||
bool hasCombo(); // Includes a COMBO SenItem
|
||||
bool hasClocked() const; // Includes a clocked statement
|
||||
bool hasSettle() const; // Includes a SETTLE SenItem
|
||||
bool hasInitial() const; // Includes a INITIAL SenItem
|
||||
bool hasCombo() const; // Includes a COMBO SenItem
|
||||
};
|
||||
|
||||
class AstAlways : public AstNode {
|
||||
@@ -2037,7 +2089,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(AlwaysPublic)
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
//
|
||||
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
|
||||
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
|
||||
@@ -2132,8 +2184,8 @@ public:
|
||||
m_direction = direction;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Pull)
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return direction()==samep->castPull()->direction(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return direction()==static_cast<const AstPull*>(samep)->direction(); }
|
||||
void lhsp(AstNode* np) { setOp1p(np); }
|
||||
AstNode* lhsp() const { return op1p(); } // op1 = Assign to
|
||||
uint32_t direction() const { return (uint32_t) m_direction; }
|
||||
@@ -2172,7 +2224,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(Comment)
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual V3Hash sameHash() const { return V3Hash(); } // Ignore name in comments
|
||||
virtual bool same(AstNode* samep) const { return true; } // Ignore name in comments
|
||||
virtual bool same(const AstNode* samep) const { return true; } // Ignore name in comments
|
||||
};
|
||||
|
||||
class AstCond : public AstNodeCond {
|
||||
@@ -2235,11 +2287,12 @@ public:
|
||||
void hier(const string& flag) { m_hier=flag; }
|
||||
void comment(const string& flag) { m_text=flag; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (fileline() == samep->castCoverDecl()->fileline()
|
||||
&& hier()==samep->castCoverDecl()->hier()
|
||||
&& comment()==samep->castCoverDecl()->comment()
|
||||
&& column()==samep->castCoverDecl()->column()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstCoverDecl* asamep = static_cast<const AstCoverDecl*>(samep);
|
||||
return (fileline() == asamep->fileline()
|
||||
&& hier() == asamep->hier()
|
||||
&& comment() == asamep->comment()
|
||||
&& column() == asamep->column()); }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp=nodep; }
|
||||
// dataDecl NULL means "use this one", but often you want "this" to indicate to get data from here
|
||||
@@ -2264,8 +2317,8 @@ public:
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return 1+2*instrCountLd(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(declp()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return declp()==samep->castCoverInc()->declp(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return declp() == static_cast<const AstCoverInc*>(samep)->declp(); }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool isOutputter() const { return true; }
|
||||
@@ -2287,7 +2340,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(CoverToggle)
|
||||
virtual int instrCount() const { return 3+instrCountBranch()+instrCountLd(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return true; }
|
||||
virtual bool isOutputter() const { return false; } // Though the AstCoverInc under this is an outputter
|
||||
@@ -2331,8 +2384,8 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Case)
|
||||
virtual string verilogKwd() const { return casez()?"casez":casex()?"casex":"case"; }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return m_casex==samep->castCase()->m_casex; }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return m_casex==static_cast<const AstCase*>(samep)->m_casex; }
|
||||
bool casex() const { return m_casex==VCaseType::CT_CASEX; }
|
||||
bool casez() const { return m_casex==VCaseType::CT_CASEZ; }
|
||||
bool caseInside() const { return m_casex==VCaseType::CT_CASEINSIDE; }
|
||||
@@ -2395,9 +2448,10 @@ public:
|
||||
virtual int instrCount() const { return instrCountPli(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual bool same(AstNode* samep) const { return text()==samep->castSFormatF()->text(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return text()==static_cast<const AstSFormatF*>(samep)->text(); }
|
||||
virtual string verilogKwd() const { return "$sformatf"; }
|
||||
void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output
|
||||
void addExprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output
|
||||
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text=text; }
|
||||
@@ -2440,7 +2494,8 @@ public:
|
||||
virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(displayType()); }
|
||||
virtual bool same(AstNode* samep) const { return displayType()==samep->castDisplay()->displayType(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return displayType()==static_cast<const AstDisplay*>(samep)->displayType(); }
|
||||
virtual int instrCount() const { return instrCountPli(); }
|
||||
AstDisplayType displayType() const { return m_displayType; }
|
||||
void displayType(AstDisplayType type) { m_displayType = type; }
|
||||
@@ -2473,7 +2528,7 @@ public:
|
||||
virtual bool cleanOut() { return false; }
|
||||
virtual int instrCount() const { return instrCountPli(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
|
||||
AstSFormatF* fmtp() const { return op1p()->castSFormatF(); }
|
||||
AstNode* lhsp() const { return op3p(); }
|
||||
@@ -2513,7 +2568,7 @@ public:
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
|
||||
};
|
||||
@@ -2535,7 +2590,7 @@ public:
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* filep() const { return op1p(); }
|
||||
AstNode* filenamep() const { return op2p(); }
|
||||
AstNode* modep() const { return op3p(); }
|
||||
@@ -2557,7 +2612,7 @@ public:
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
|
||||
};
|
||||
@@ -2585,8 +2640,8 @@ public:
|
||||
virtual bool isOutputter() const { return true; } // SPECIAL: makes output
|
||||
virtual bool cleanOut() { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return text()==samep->castFScanF()->text(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return text()==static_cast<const AstFScanF*>(samep)->text(); }
|
||||
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
|
||||
void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
@@ -2618,8 +2673,8 @@ public:
|
||||
virtual bool isOutputter() const { return true; } // SPECIAL: makes output
|
||||
virtual bool cleanOut() { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return text()==samep->castSScanF()->text(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return text()==static_cast<const AstSScanF*>(samep)->text(); }
|
||||
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
|
||||
void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
@@ -2645,7 +2700,8 @@ public:
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return isHex()==samep->castReadMem()->isHex(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return isHex()==static_cast<const AstReadMem*>(samep)->isHex(); }
|
||||
bool isHex() const { return m_isHex; }
|
||||
AstNode* filenamep() const { return op1p(); }
|
||||
AstNode* memp() const { return op2p(); }
|
||||
@@ -2668,7 +2724,7 @@ public:
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
};
|
||||
|
||||
@@ -2690,7 +2746,7 @@ public:
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
};
|
||||
|
||||
@@ -2710,7 +2766,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* searchp() const { return op1p(); } // op1 = Search expression
|
||||
void searchp(AstNode* nodep) { setOp1p(nodep); }
|
||||
AstNode* outp() const { return op2p(); } // op2 = Expressions to output
|
||||
@@ -2734,8 +2790,8 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return text()==samep->castTestPlusArgs()->text(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return text() == static_cast<const AstTestPlusArgs*>(samep)->text(); }
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text=text; }
|
||||
};
|
||||
@@ -2763,7 +2819,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstRepeat : public AstNodeStmt {
|
||||
@@ -2778,7 +2834,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return false; } // Not releavant - converted to FOR
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstWhile : public AstNodeStmt {
|
||||
@@ -2798,7 +2854,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here
|
||||
virtual void addNextStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here
|
||||
};
|
||||
@@ -2862,7 +2918,7 @@ private:
|
||||
bool m_unique0Pragma; // unique0 case
|
||||
bool m_priorityPragma; // priority case
|
||||
public:
|
||||
AstIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
|
||||
AstIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp=NULL)
|
||||
: AstNodeIf(fileline, condp, ifsp, elsesp) {
|
||||
m_uniquePragma=false; m_unique0Pragma=false; m_priorityPragma=false;
|
||||
}
|
||||
@@ -2891,7 +2947,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(JumpLabel)
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
// op1 = Statements
|
||||
AstNode* stmtsp() const { return op1p(); } // op1 = List of statements
|
||||
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
@@ -2915,8 +2971,8 @@ public:
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(labelp()); }
|
||||
virtual bool same(AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect
|
||||
return labelp()==samep->castJumpGo()->labelp(); }
|
||||
virtual bool same(const AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect
|
||||
return labelp() == static_cast<const AstJumpGo*>(samep)->labelp(); }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks
|
||||
AstJumpLabel* labelp() const { return m_labelp; }
|
||||
@@ -2940,7 +2996,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; } // Not relevant
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstChangeXor : public AstNodeBiComAsv {
|
||||
@@ -2983,7 +3039,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstBegin : public AstNode {
|
||||
@@ -3103,7 +3159,8 @@ public:
|
||||
return m_indices[listPos]; }
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return m_indices==samep->castInitArray()->m_indices; }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return m_indices == static_cast<const AstInitArray*>(samep)->m_indices; }
|
||||
};
|
||||
|
||||
class AstPragma : public AstNode {
|
||||
@@ -3120,8 +3177,8 @@ public:
|
||||
AstPragmaType pragType() const { return m_pragType; } // *=type of the pragma
|
||||
virtual V3Hash sameHash() const { return V3Hash(pragType()); }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return pragType()==samep->castPragma()->pragType(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return pragType() == static_cast<const AstPragma*>(samep)->pragType(); }
|
||||
};
|
||||
|
||||
class AstStop : public AstNodeStmt {
|
||||
@@ -3136,7 +3193,7 @@ public:
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual int instrCount() const { return 0; } // Rarely executes
|
||||
virtual V3Hash sameHash() const { return V3Hash(fileline()->lineno()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return fileline() == samep->fileline(); }
|
||||
};
|
||||
|
||||
@@ -3152,7 +3209,7 @@ public:
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual int instrCount() const { return 0; } // Rarely executes
|
||||
virtual V3Hash sameHash() const { return V3Hash(fileline()->lineno()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return fileline() == samep->fileline(); }
|
||||
};
|
||||
|
||||
@@ -3182,7 +3239,7 @@ public:
|
||||
virtual string name() const { return m_showname; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual bool same(AstNode* samep) const { return false; }
|
||||
virtual bool same(const AstNode* samep) const { return false; }
|
||||
string showname() const { return m_showname; } // * = Var name
|
||||
// Details on what we're tracing
|
||||
uint32_t code() const { return m_code; }
|
||||
@@ -3212,8 +3269,8 @@ public:
|
||||
virtual int instrCount() const { return 10+2*instrCountLd(); }
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(declp()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return declp()==samep->castTraceInc()->declp(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return declp() == static_cast<const AstTraceInc*>(samep)->declp(); }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool isOutputter() const { return true; }
|
||||
@@ -3295,7 +3352,8 @@ public:
|
||||
dtypeSetUInt64(); }
|
||||
ASTNODE_NODE_FUNCS(ScopeName)
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return m_dpiExport==samep->castScopeName()->m_dpiExport; }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return m_dpiExport == static_cast<const AstScopeName*>(samep)->m_dpiExport; }
|
||||
virtual string emitVerilog() { return ""; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
@@ -3353,7 +3411,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountPli(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstTime : public AstNodeTermop {
|
||||
@@ -3368,7 +3426,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountTime(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstTimeD : public AstNodeTermop {
|
||||
@@ -3383,7 +3441,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountTime(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstUCFunc : public AstNodeMath {
|
||||
@@ -3406,7 +3464,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual int instrCount() const { return instrCountPli(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -3767,7 +3825,8 @@ public:
|
||||
virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;} // Special cased in V3Cast
|
||||
virtual V3Hash sameHash() const { return V3Hash(size()); }
|
||||
virtual bool same(AstNode* samep) const { return size()==samep->castCCast()->size(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return size() == static_cast<const AstCCast*>(samep)->size(); }
|
||||
virtual void dump(ostream& str=cout);
|
||||
//
|
||||
int size() const { return m_size; }
|
||||
@@ -3785,7 +3844,7 @@ public:
|
||||
virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstFEof : public AstNodeUniop {
|
||||
@@ -4892,7 +4951,7 @@ public:
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc
|
||||
};
|
||||
class AstPatMember : public AstNodeMath {
|
||||
@@ -4934,7 +4993,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(VAssert)
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstNode* propp() const { return op1p(); } // op1 = property
|
||||
AstNode* passsp() const { return op2p(); } // op2 = if passes
|
||||
AstNode* failsp() const { return op3p(); } // op3 = if fails
|
||||
@@ -4995,7 +5054,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(PslCover)
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual V3Hash sameHash() const { return V3Hash(name()); }
|
||||
virtual bool same(AstNode* samep) const { return samep->name() == name(); }
|
||||
virtual bool same(const AstNode* samep) const { return samep->name() == name(); }
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
AstNode* propp() const { return op1p(); } // op1 = property
|
||||
AstSenTree* sentreep() const { return op2p()->castSenTree(); } // op2 = clock domain
|
||||
@@ -5087,7 +5146,7 @@ public:
|
||||
virtual bool isPure() const { return false; }
|
||||
virtual bool isOutputter() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -5113,7 +5172,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(CFile)
|
||||
virtual string name() const { return m_name; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
virtual void dump(ostream& str=cout);
|
||||
bool slow() const { return m_slow; }
|
||||
void slow(bool flag) { m_slow = flag; }
|
||||
@@ -5134,6 +5193,7 @@ private:
|
||||
string m_cname; // C name, for dpiExports
|
||||
string m_rtnType; // void, bool, or other return type
|
||||
string m_argTypes;
|
||||
string m_ifdef; // #ifdef symbol around this function
|
||||
bool m_dontCombine:1; // V3Combine shouldn't compare this func tree, it's special
|
||||
bool m_skipDecl:1; // Don't declare it
|
||||
bool m_declPrivate:1; // Declare it private
|
||||
@@ -5176,11 +5236,13 @@ public:
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return ((funcType()==samep->castCFunc()->funcType())
|
||||
&& (rtnTypeVoid()==samep->castCFunc()->rtnTypeVoid())
|
||||
&& (argTypes()==samep->castCFunc()->argTypes())
|
||||
&& (!(dpiImport() || dpiExport())
|
||||
|| name()==samep->castCFunc()->name())); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
|
||||
return ((funcType() == asamep->funcType())
|
||||
&& (rtnTypeVoid() == asamep->rtnTypeVoid())
|
||||
&& (argTypes() == asamep->argTypes())
|
||||
&& (!(dpiImport() || dpiExport())
|
||||
|| name() == asamep->name())); }
|
||||
//
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
virtual int instrCount() const { return dpiImport() ? instrCountDpi() : 0; }
|
||||
@@ -5204,6 +5266,8 @@ public:
|
||||
void funcPublic(bool flag) { m_funcPublic = flag; }
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
void ifdef(const string& str) { m_ifdef = str; }
|
||||
string ifdef() const { return m_ifdef; }
|
||||
void funcType(AstCFuncType flag) { m_funcType = flag; }
|
||||
AstCFuncType funcType() const { return m_funcType; }
|
||||
bool isInline() const { return m_isInline; }
|
||||
@@ -5266,9 +5330,10 @@ public:
|
||||
virtual const char* broken() const { BROKEN_RTN(m_funcp && !m_funcp->brokeExists()); return NULL; }
|
||||
virtual int instrCount() const { return instrCountCall(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(funcp()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return (funcp()==samep->castCCall()->funcp()
|
||||
&& argTypes()==samep->castCCall()->argTypes()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstCCall* asamep = static_cast<const AstCCall*>(samep);
|
||||
return (funcp() == asamep->funcp()
|
||||
&& argTypes() == asamep->argTypes()); }
|
||||
AstNode* exprsp() const { return op1p(); } // op1= expressions to print
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
@@ -5296,7 +5361,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(CReturn)
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
//
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
};
|
||||
@@ -5323,7 +5388,7 @@ public:
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
AstNode* bodysp() const { return op1p(); } // op1= expressions to print
|
||||
};
|
||||
@@ -5340,7 +5405,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstVarRef* varrefp() const { return op1p()->castVarRef(); } // op1= varref to reset
|
||||
};
|
||||
|
||||
@@ -5359,7 +5424,7 @@ public:
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
AstNode* bodysp() const { return op1p(); } // op1= expressions to print
|
||||
};
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
class V3Branch {
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
static void branchAll(AstNetlist* rootp);
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class BrokenTable : public AstNVisitor {
|
||||
private:
|
||||
// MEMBERS
|
||||
// For each node, we keep if it exists or not.
|
||||
typedef VL_UNORDERED_MAP<const AstNode*,int> NodeMap; // Performance matters (when --debug)
|
||||
typedef vl_unordered_map<const AstNode*,int> NodeMap; // Performance matters (when --debug)
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
// BITMASK
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
|
||||
+41
-1
@@ -23,6 +23,8 @@
|
||||
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
|
||||
// For each variable that needs reset, add a AstCReset node.
|
||||
//
|
||||
// For primary inputs, add _eval_debug_assertions.
|
||||
//
|
||||
// This transformation honors outputSplitCFuncs.
|
||||
//*************************************************************************
|
||||
#include "config_build.h"
|
||||
@@ -96,13 +98,51 @@ public:
|
||||
}
|
||||
~V3CCtorsVisitor() {}
|
||||
private:
|
||||
explicit V3CCtorsVisitor(const V3CCtorsVisitor&); ///< N/A, no copy constructor
|
||||
VL_UNCOPYABLE(V3CCtorsVisitor);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
void V3CCtors::evalAsserts() {
|
||||
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", NULL, "void");
|
||||
funcp->declPrivate(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->slow(false);
|
||||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = np->castVar()) {
|
||||
if (varp->isPrimaryIn() && !varp->isSc()) {
|
||||
if (AstBasicDType* basicp = varp->dtypeSkipRefp()->castBasicDType()) {
|
||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel(varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), varp->widthWords()-1));
|
||||
}
|
||||
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
V3Number num (varp->fileline(), storedWidth, value);
|
||||
newp = new AstAnd(varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), num));
|
||||
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
|
||||
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""+varp->prettyName()+"\");"));
|
||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
newp = ifp;
|
||||
funcp->addStmtsp(newp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V3CCtors::cctorsAll() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
evalAsserts();
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
|
||||
// Process each module in turn
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
class V3CCtors {
|
||||
public:
|
||||
static void cctorsAll();
|
||||
private:
|
||||
static void evalAsserts();
|
||||
};
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -496,7 +496,7 @@ private:
|
||||
}
|
||||
|
||||
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc_edges.txt";
|
||||
const VL_UNIQUE_PTR<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
*ofp<<"Edge Report for "<<v3Global.opt.prefix()<<endl;
|
||||
|
||||
|
||||
+1
-1
@@ -621,7 +621,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
}
|
||||
// Top level we could choose to make multiple gaters, or ORs under the gater
|
||||
// Right now we'll put OR lower down and let other optimizations deal
|
||||
if (nodep) nodep = new AstOr(eqnp->fileline(),nodep,eqnp);
|
||||
if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp);
|
||||
else nodep = eqnp;
|
||||
//if (debug()>=9) nodep->dumpTree(cout," followExpr: ");
|
||||
}
|
||||
|
||||
+10
-8
@@ -1943,6 +1943,8 @@ private:
|
||||
|| !prevp->filep()->sameTree(nodep->filep())) return false;
|
||||
if (!prevp->fmtp() || prevp->fmtp()->nextp()
|
||||
|| !nodep->fmtp() || nodep->fmtp()->nextp()) return false;
|
||||
// We don't merge scopeNames as might be different scopes (late in process)
|
||||
// We don't merge arguments as might need to later print warnings with right line numbers
|
||||
AstSFormatF* pformatp = prevp->fmtp();
|
||||
if (!pformatp || pformatp->exprsp() || pformatp->scopeNamep()) return false;
|
||||
AstSFormatF* nformatp = nodep->fmtp();
|
||||
@@ -2202,7 +2204,7 @@ private:
|
||||
// This visit function here must allow for short-circuiting.
|
||||
TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)");
|
||||
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
||||
TREEOP ("AstCond{$condp->castNot(), $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}");
|
||||
TREEOP ("AstCond{$condp.castNot, $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}");
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstLogOr {$condp, $expr2p}"); // a?1:b == a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstLogAnd{$condp, $expr1p}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstLogOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a||b
|
||||
@@ -2342,12 +2344,12 @@ private:
|
||||
TREEOPV("AstRedAnd{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstRedOr {$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstRedXor{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstRedAnd{$lhsp->castConcat()}", "AstAnd{AstRedAnd{$lhsp->castConcat()->lhsp()}, AstRedAnd{$lhsp->castConcat()->rhsp()}}"); // &{a,b} => {&a}&{&b}
|
||||
TREEOPV("AstRedOr {$lhsp->castConcat()}", "AstOr {AstRedOr {$lhsp->castConcat()->lhsp()}, AstRedOr {$lhsp->castConcat()->rhsp()}}"); // |{a,b} => {|a}|{|b}
|
||||
TREEOPV("AstRedXor{$lhsp->castConcat()}", "AstXor{AstRedXor{$lhsp->castConcat()->lhsp()}, AstRedXor{$lhsp->castConcat()->rhsp()}}"); // ^{a,b} => {^a}^{^b}
|
||||
TREEOPV("AstRedAnd{$lhsp->castExtend(), $lhsp->width()>$lhsp->castExtend()->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error
|
||||
TREEOPV("AstRedOr {$lhsp->castExtend()}", "AstRedOr {$lhsp->castExtend()->lhsp()}");
|
||||
TREEOPV("AstRedXor{$lhsp->castExtend()}", "AstRedXor{$lhsp->castExtend()->lhsp()}");
|
||||
TREEOPV("AstRedAnd{$lhsp.castConcat}", "AstAnd{AstRedAnd{$lhsp->castConcat()->lhsp()}, AstRedAnd{$lhsp->castConcat()->rhsp()}}"); // &{a,b} => {&a}&{&b}
|
||||
TREEOPV("AstRedOr {$lhsp.castConcat}", "AstOr {AstRedOr {$lhsp->castConcat()->lhsp()}, AstRedOr {$lhsp->castConcat()->rhsp()}}"); // |{a,b} => {|a}|{|b}
|
||||
TREEOPV("AstRedXor{$lhsp.castConcat}", "AstXor{AstRedXor{$lhsp->castConcat()->lhsp()}, AstRedXor{$lhsp->castConcat()->rhsp()}}"); // ^{a,b} => {^a}^{^b}
|
||||
TREEOPV("AstRedAnd{$lhsp.castExtend, $lhsp->width() > $lhsp->castExtend()->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error
|
||||
TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}");
|
||||
TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}");
|
||||
TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)");
|
||||
// Binary AND/OR is faster than logical and/or (usually)
|
||||
@@ -2359,7 +2361,7 @@ private:
|
||||
TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)");
|
||||
TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())");
|
||||
// CONCAT(a[1],a[0]) -> a[1:0]
|
||||
TREEOPV("AstConcat{$lhsp->castSel(), $rhsp->castSel(), ifAdjacentSel($lhsp->castSel(),,$rhsp->castSel())}", "replaceConcatSel(nodep)");
|
||||
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel($lhsp->castSel(),,$rhsp->castSel())}", "replaceConcatSel(nodep)");
|
||||
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
|
||||
// Common two-level operations that can be simplified
|
||||
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
class V3Coverage {
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
static void coverage(AstNetlist* rootp);
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
class V3CoverageJoin {
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
static void coverageJoin(AstNetlist* rootp);
|
||||
};
|
||||
|
||||
|
||||
+8
-4
@@ -137,8 +137,10 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
checkAll(nodep);
|
||||
if (!nodep->dead()) {
|
||||
nodep->iterateChildren(*this);
|
||||
checkAll(nodep);
|
||||
}
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
@@ -280,12 +282,14 @@ private:
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
|
||||
nextmodp = modp->nextp()->castNodeModule();
|
||||
if (modp->level()>2 && modp->user1()==0 && !modp->internal()) {
|
||||
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4," Dead module "<<modp<<endl);
|
||||
// And its children may now be killable too; correct counts
|
||||
// Recurse, as cells may not be directly under the module but in a generate
|
||||
DeadModVisitor visitor(modp);
|
||||
if (!modp->dead()) { // If was dead didn't increment user1's
|
||||
DeadModVisitor visitor(modp);
|
||||
}
|
||||
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
|
||||
retry = true;
|
||||
}
|
||||
|
||||
+45
-14
@@ -27,6 +27,7 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
@@ -433,14 +434,14 @@ public:
|
||||
puts("}\n");
|
||||
}
|
||||
virtual void visit(AstStop* nodep) {
|
||||
puts("vl_stop(");
|
||||
puts("VL_STOP_MT(");
|
||||
putsQuoted(nodep->fileline()->filename());
|
||||
puts(",");
|
||||
puts(cvtToStr(nodep->fileline()->lineno()));
|
||||
puts(",\"\");\n");
|
||||
}
|
||||
virtual void visit(AstFinish* nodep) {
|
||||
puts("vl_finish(");
|
||||
puts("VL_FINISH_MT(");
|
||||
putsQuoted(nodep->fileline()->filename());
|
||||
puts(",");
|
||||
puts(cvtToStr(nodep->fileline()->lineno()));
|
||||
@@ -871,12 +872,14 @@ class EmitCImp : EmitCStmts {
|
||||
splitSizeInc(nodep);
|
||||
|
||||
puts("\n");
|
||||
if (nodep->ifdef()!="") puts("#ifdef "+nodep->ifdef()+"\n");
|
||||
if (nodep->isInline()) puts("VL_INLINE_OPT ");
|
||||
puts(nodep->rtnTypeVoid()); puts(" ");
|
||||
puts(modClassName(m_modp)+"::"+nodep->name()
|
||||
+"("+cFuncArgs(nodep)+") {\n");
|
||||
|
||||
puts("VL_DEBUG_IF(VL_PRINTF(\" ");
|
||||
// "+" in the debug indicates a print from the model
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
|
||||
for (int i=0;i<m_modp->level();i++) { puts(" "); }
|
||||
puts(modClassName(m_modp)+"::"+nodep->name()
|
||||
+"\\n\"); );\n");
|
||||
@@ -906,6 +909,7 @@ class EmitCImp : EmitCStmts {
|
||||
|
||||
//puts("__Vm_activity = true;\n");
|
||||
puts("}\n");
|
||||
if (nodep->ifdef()!="") puts("#endif // "+nodep->ifdef()+"\n");
|
||||
}
|
||||
|
||||
void emitChangeDet() {
|
||||
@@ -937,7 +941,7 @@ class EmitCImp : EmitCStmts {
|
||||
if (nodep->lhsp()->castVarRef()) {
|
||||
varname = ": "+nodep->lhsp()->castVarRef()->varp()->prettyName();
|
||||
}
|
||||
puts(")) VL_PRINTF(\"\tCHANGE: "+nodep->fileline()->ascii()
|
||||
puts(")) VL_DBG_MSGF(\" CHANGE: "+nodep->fileline()->ascii()
|
||||
+varname+"\\n\"); );\n");
|
||||
}
|
||||
}
|
||||
@@ -999,7 +1003,7 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
if (nodep->isSc()) {
|
||||
m_ctorVarsVec.push_back(nodep);
|
||||
if (nodep->attrScClocked() && nodep->isInput()) {
|
||||
puts("sc_in_clk\t");
|
||||
puts("sc_in_clk ");
|
||||
} else {
|
||||
if (nodep->isInout()) puts("sc_inout<");
|
||||
else if (nodep->isInput()) puts("sc_in<");
|
||||
@@ -1007,7 +1011,7 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
else nodep->v3fatalSrc("Unknown type");
|
||||
|
||||
puts(nodep->scType());
|
||||
puts(">\t");
|
||||
puts("> ");
|
||||
}
|
||||
puts(nodep->name());
|
||||
emitDeclArrayBrackets(nodep);
|
||||
@@ -1729,27 +1733,45 @@ void EmitCImp::emitSensitives() {
|
||||
|
||||
void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts("\nvoid "+modClassName(modp)+"::eval() {\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n");
|
||||
puts(EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp; // Setup global symbol table\n");
|
||||
puts(EmitCBaseVisitor::symTopAssign()+"\n");
|
||||
puts("#ifdef VL_DEBUG\n");
|
||||
putsDecoration("// Debug assertions\n");
|
||||
puts("_eval_debug_assertions();\n");
|
||||
puts("#endif // VL_DEBUG\n");
|
||||
putsDecoration("// Initialize\n");
|
||||
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);\n");
|
||||
if (v3Global.opt.inhibitSim()) {
|
||||
puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.threads()) { // THREADED-TODO move to per-train
|
||||
uint32_t trainId = 0;
|
||||
putsDecoration("// Train "+cvtToStr(trainId)+" start\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"Train starting, trainId="+cvtToStr(trainId)+"\\n\"););\n");
|
||||
puts("Verilated::trainId("+cvtToStr(trainId)+");\n");
|
||||
}
|
||||
|
||||
putsDecoration("// Evaluate till stable\n");
|
||||
puts("VL_DEBUG_IF(VL_PRINTF(\"\\n----TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n");
|
||||
puts("int __VclockLoop = 0;\n");
|
||||
puts("QData __Vchange = 1;\n");
|
||||
puts("while (VL_LIKELY(__Vchange)) {\n");
|
||||
puts( "VL_DEBUG_IF(VL_PRINTF(\" Clock loop\\n\"););\n");
|
||||
puts( "VL_DEBUG_IF(VL_DBG_MSGF(\"+ Clock loop\\n\"););\n");
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("vlSymsp->__Vm_activity = true;\n");
|
||||
}
|
||||
puts( "_eval(vlSymsp);\n");
|
||||
puts( "__Vchange = _change_request(vlSymsp);\n");
|
||||
puts( "if (VL_UNLIKELY(++__VclockLoop > "+cvtToStr(v3Global.opt.convergeLimit())
|
||||
+")) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n");
|
||||
+")) VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n");
|
||||
puts("}\n");
|
||||
if (v3Global.opt.threads()) { // THREADED-TODO move to end of all trains on thread
|
||||
puts("Verilated::endOfThreadTrain(vlSymsp->__Vm_evalMsgQp);\n");
|
||||
}
|
||||
if (v3Global.opt.threads()) {
|
||||
puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
|
||||
}
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
@@ -1767,7 +1789,7 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts( "_eval(vlSymsp);\n");
|
||||
puts( "__Vchange = _change_request(vlSymsp);\n");
|
||||
puts( "if (VL_UNLIKELY(++__VclockLoop > "+cvtToStr(v3Global.opt.convergeLimit())
|
||||
+")) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't DC converge\");\n");
|
||||
+")) VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Verilated model didn't DC converge\");\n");
|
||||
puts( "}\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
@@ -1843,9 +1865,11 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) {
|
||||
AstCFunc* funcp = *it;
|
||||
if (!funcp->dpiImport()) { // DPI is prototyped in __Dpi.h
|
||||
ofp()->putsPrivate(funcp->declPrivate());
|
||||
if (funcp->ifdef()!="") puts("#ifdef "+funcp->ifdef()+"\n");
|
||||
if (funcp->isStatic()) puts("static ");
|
||||
puts(funcp->rtnTypeVoid()); puts(" ");
|
||||
puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n");
|
||||
if (funcp->ifdef()!="") puts("#endif // "+funcp->ifdef()+"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1881,9 +1905,14 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
|
||||
// Declare foreign instances up front to make C++ happy
|
||||
puts("class "+symClassName()+";\n");
|
||||
vl_unordered_set<string> didClassName;
|
||||
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstCell* cellp=nodep->castCell()) {
|
||||
puts("class "+modClassName(cellp->modp())+";\n");
|
||||
string className = modClassName(cellp->modp());
|
||||
if (didClassName.find(className)==didClassName.end()) {
|
||||
puts("class "+className+";\n");
|
||||
didClassName.insert(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
@@ -2046,8 +2075,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
|
||||
// Save/restore
|
||||
if (v3Global.opt.savable() && modp->isTop()) {
|
||||
puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) { rhs.__Vserialize(os); return os; }\n");
|
||||
puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) { rhs.__Vdeserialize(os); return os; }\n");
|
||||
puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) {\n"
|
||||
"Verilated::quiesce(); rhs.__Vserialize(os); return os; }\n");
|
||||
puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) {\n"
|
||||
"Verilated::quiesce(); rhs.__Vdeserialize(os); return os; }\n");
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
@@ -2205,7 +2236,7 @@ class EmitCTrace : EmitCStmts {
|
||||
putsDecoration("// Callback from vcd->open()\n");
|
||||
puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n");
|
||||
puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n");
|
||||
puts("if (!Verilated::calcUnusedSigs()) vl_fatal(__FILE__,__LINE__,__FILE__,\"Turning on wave traces requires Verilated::traceEverOn(true) call before time 0.\");\n");
|
||||
puts("if (!Verilated::calcUnusedSigs()) VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Turning on wave traces requires Verilated::traceEverOn(true) call before time 0.\");\n");
|
||||
|
||||
puts("vcdp->scopeEscape(' ');\n");
|
||||
puts("t->traceInitThis (vlSymsp, vcdp, code);\n");
|
||||
|
||||
+1
-1
@@ -339,7 +339,7 @@ void EmitCSyms::emitSymHdr() {
|
||||
|
||||
puts("\n// CREATORS\n");
|
||||
puts(symClassName()+"("+topClassName()+"* topp, const char* namep);\n");
|
||||
puts((string)"~"+symClassName()+"() {};\n");
|
||||
puts((string)"~"+symClassName()+"() {}\n");
|
||||
|
||||
puts("\n// METHODS\n");
|
||||
puts("inline const char* name() { return __Vm_namep; }\n");
|
||||
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
of.puts("\n### Switches...\n");
|
||||
of.puts("# Coverage output mode? 0/1 (from --coverage)\n");
|
||||
of.puts("VM_COVERAGE = "); of.puts(v3Global.opt.coverage()?"1":"0"); of.puts("\n");
|
||||
of.puts("# Threaded output mode? 0/1/N threads (from --threads)\n");
|
||||
of.puts("VM_THREADS = "); of.puts(cvtToStr(v3Global.opt.threads())); of.puts("\n");
|
||||
of.puts("# Tracing output mode? 0/1 (from --trace)\n");
|
||||
of.puts("VM_TRACE = "); of.puts(v3Global.opt.trace()?"1":"0"); of.puts("\n");
|
||||
|
||||
|
||||
@@ -493,6 +493,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
}
|
||||
puts("]");
|
||||
}
|
||||
virtual void visit(AstSliceSel* nodep) {
|
||||
nodep->fromp()->iterateAndNext(*this);
|
||||
puts(cvtToStr(nodep->declRange()));
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
putfs(nodep,"typedef ");
|
||||
nodep->dtypep()->iterateAndNext(*this); puts(" ");
|
||||
|
||||
+19
-42
@@ -37,8 +37,13 @@
|
||||
// Emit statements and math operators
|
||||
|
||||
class EmitXmlFileVisitor : public AstNVisitor {
|
||||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// AstNode::user1 -> uint64_t, number to connect crossrefs
|
||||
|
||||
// MEMBERS
|
||||
V3OutFile* m_ofp;
|
||||
uint64_t m_id;
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
@@ -64,11 +69,21 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
||||
}
|
||||
|
||||
// XML methods
|
||||
void outputId(AstNode* nodep) {
|
||||
if (!nodep->user1()) { nodep->user1(++m_id); }
|
||||
puts("\""+cvtToStr(nodep->user1())+"\"");
|
||||
}
|
||||
void outputTag(AstNode* nodep, string tag) {
|
||||
if (tag=="") tag = VString::downcase(nodep->typeName());
|
||||
puts("<"+tag+" "+nodep->fileline()->xml());
|
||||
if (nodep->castNodeDType()) { puts(" id="); outputId(nodep); }
|
||||
if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); }
|
||||
if (nodep->tag()!="") { puts(" tag="); putsQuoted(nodep->tag()); }
|
||||
if (AstNodeDType* dtp = nodep->castNodeDType()) {
|
||||
if (dtp->subDTypep()) { puts(" sub_dtype_id="); outputId(dtp->subDTypep()->skipRefp()); }
|
||||
} else {
|
||||
if (nodep->dtypep()) { puts(" dtype_id="); outputId(nodep->dtypep()->skipRefp()); }
|
||||
}
|
||||
}
|
||||
void outputChildrenEnd(AstNode* nodep, string tag) {
|
||||
if (tag=="") tag = VString::downcase(nodep->typeName());
|
||||
@@ -111,13 +126,13 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
||||
outputChildrenEnd(nodep, "port");
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) {
|
||||
outputTag(nodep, "contAssign"); // IEEE: vpiContAssign
|
||||
outputChildrenEnd(nodep, "contAssign");
|
||||
outputTag(nodep, "contassign"); // IEEE: vpiContAssign
|
||||
outputChildrenEnd(nodep, "contassign");
|
||||
}
|
||||
|
||||
// Data types
|
||||
virtual void visit(AstBasicDType* nodep) {
|
||||
outputTag(nodep, "basicDType ");
|
||||
outputTag(nodep, "basicdtype ");
|
||||
if (nodep->isRanged()) {
|
||||
puts(" left=\""+cvtToStr(nodep->left())+"\"");
|
||||
puts(" right=\""+cvtToStr(nodep->right())+"\"");
|
||||
@@ -133,50 +148,12 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
||||
public:
|
||||
EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) {
|
||||
m_ofp = ofp;
|
||||
m_id = 0;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~EmitXmlFileVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Emit to a stream (perhaps stringstream)
|
||||
|
||||
class EmitXmlPrefixedFormatter : public V3OutFormatter {
|
||||
ostream& m_os;
|
||||
string m_prefix; // What to print at beginning of each line
|
||||
int m_flWidth; // Padding of fileline
|
||||
int m_column; // Rough location; need just zero or non-zero
|
||||
FileLine* m_prefixFl;
|
||||
// METHODS
|
||||
virtual void putcOutput(char chr) {
|
||||
if (chr == '\n') {
|
||||
m_column = 0;
|
||||
m_os<<chr;
|
||||
} else {
|
||||
if (m_column == 0) {
|
||||
m_column = 10;
|
||||
m_os<<m_prefixFl->ascii()+":";
|
||||
m_os<<V3OutFile::indentSpaces(m_flWidth-(m_prefixFl->ascii().length()+1));
|
||||
m_os<<" ";
|
||||
m_os<<m_prefix;
|
||||
}
|
||||
m_column++;
|
||||
m_os<<chr;
|
||||
}
|
||||
}
|
||||
public:
|
||||
void prefixFl(FileLine* fl) { m_prefixFl = fl; }
|
||||
FileLine* prefixFl() const { return m_prefixFl; }
|
||||
int column() const { return m_column; }
|
||||
EmitXmlPrefixedFormatter(ostream& os, const string& prefix, int flWidth)
|
||||
: V3OutFormatter("__STREAM", V3OutFormatter::LA_VERILOG)
|
||||
, m_os(os), m_prefix(prefix), m_flWidth(flWidth) {
|
||||
m_column = 0;
|
||||
m_prefixFl = v3Global.rootp()->fileline(); // NETLIST's fileline instead of NULL to avoid NULL checks
|
||||
}
|
||||
virtual ~EmitXmlPrefixedFormatter() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// EmitXml class functions
|
||||
|
||||
|
||||
+7
-3
@@ -61,6 +61,7 @@ public:
|
||||
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
|
||||
BLKLOOPINIT, // Delayed assignment to array inside for loops
|
||||
BLKSEQ, // Blocking assignments in sequential block
|
||||
BSSPACE, // Backslash space
|
||||
CASEINCOMPLETE, // Case statement has missing values
|
||||
CASEOVERLAP, // Case statements overlap
|
||||
CASEWITHX, // Case with X values
|
||||
@@ -68,6 +69,7 @@ public:
|
||||
CDCRSTLOGIC, // Logic in async reset path
|
||||
CLKDATA, // Clock used as data
|
||||
CMPCONST, // Comparison is constant due to limited range
|
||||
COLONPLUS, // :+ instead of +:
|
||||
COMBDLY, // Combinatorial delayed assignment
|
||||
DEFPARAM, // Style: Defparam
|
||||
DECLFILENAME, // Declaration doesn't match filename
|
||||
@@ -125,9 +127,9 @@ public:
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
|
||||
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ",
|
||||
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CLKDATA",
|
||||
"CMPCONST", "COMBDLY", "DEFPARAM", "DECLFILENAME",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "DEFPARAM", "DECLFILENAME",
|
||||
"ENDLABEL", "GENCLK",
|
||||
"IFDEPTH", "IMPERFECTSCH", "IMPLICIT", "IMPURE",
|
||||
"INCABSPATH", "INITIALDLY",
|
||||
@@ -158,9 +160,11 @@ public:
|
||||
|
||||
// Warnings that are lint only
|
||||
bool lintError() const { return ( m_e==ALWCOMBORDER
|
||||
|| m_e==BSSPACE
|
||||
|| m_e==CASEINCOMPLETE || m_e==CASEOVERLAP
|
||||
|| m_e==CASEWITHX || m_e==CASEX
|
||||
|| m_e==CMPCONST
|
||||
|| m_e==COLONPLUS
|
||||
|| m_e==ENDLABEL
|
||||
|| m_e==IMPLICIT
|
||||
|| m_e==LITENDIAN
|
||||
@@ -215,7 +219,7 @@ class V3Error {
|
||||
V3Error() { cerr<<("Static class"); abort(); }
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
// ACCESSORS
|
||||
static void debugDefault(int level) { s_debugDefault = level; }
|
||||
static int debugDefault() { return s_debugDefault; }
|
||||
|
||||
+4
-4
@@ -134,7 +134,7 @@ V3FileDependImp dependImp; // Depend implementation class
|
||||
// V3FileDependImp
|
||||
|
||||
inline void V3FileDependImp::writeDepend(const string& filename) {
|
||||
const VL_UNIQUE_PTR<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
|
||||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
@@ -170,7 +170,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
|
||||
}
|
||||
|
||||
inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) {
|
||||
const VL_UNIQUE_PTR<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
|
||||
string cmdline = stripQuotes(cmdlineIn);
|
||||
@@ -200,7 +200,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
|
||||
}
|
||||
|
||||
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
|
||||
const VL_UNIQUE_PTR<ifstream> ifp (V3File::new_ifstream_nodepend(filename));
|
||||
const vl_unique_ptr<ifstream> ifp (V3File::new_ifstream_nodepend(filename));
|
||||
if (ifp->fail()) {
|
||||
UINFO(2," --check-times failed: no input "<<filename<<endl);
|
||||
return false;
|
||||
@@ -591,7 +591,7 @@ const char* V3OutFormatter::indentStr(int num) {
|
||||
static char str[MAXSPACE+20];
|
||||
char* cp = str;
|
||||
if (num>MAXSPACE) num=MAXSPACE;
|
||||
if (m_lang!=LA_VERILOG) { // verilogPrefixedTree doesn't want tabs
|
||||
if (m_lang!=LA_VERILOG && m_lang!=LA_XML) { // verilogPrefixedTree doesn't want tabs
|
||||
while (num>=8) {
|
||||
*cp++ = '\t';
|
||||
num -= 8;
|
||||
|
||||
+9
-6
@@ -76,19 +76,22 @@ public:
|
||||
class V3InFilterImp;
|
||||
|
||||
class V3InFilter {
|
||||
V3InFilterImp* m_impp;
|
||||
V3InFilter(const V3InFilter&); ///< N/A, no copy constructor
|
||||
public:
|
||||
// TYPES
|
||||
typedef list<string> StrList;
|
||||
|
||||
private:
|
||||
V3InFilterImp* m_impp;
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(V3InFilter);
|
||||
public:
|
||||
explicit V3InFilter(const string& command);
|
||||
~V3InFilter();
|
||||
|
||||
// METHODS
|
||||
// Read file contents and return it. Return true on success.
|
||||
bool readWholefile(const string& filename, StrList& outl);
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit V3InFilter(const string& command);
|
||||
~V3InFilter();
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
+5
-2
@@ -18,10 +18,13 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#ifndef _V3ERROR_NO_GLOBAL_
|
||||
@@ -254,7 +257,7 @@ string FileLine::warnMore() const {
|
||||
}
|
||||
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
typedef set<FileLine*> FileLineCheckSet;
|
||||
typedef vl_unordered_set<FileLine*> FileLineCheckSet;
|
||||
FileLineCheckSet fileLineLeakChecks;
|
||||
|
||||
void* FileLine::operator new(size_t size) {
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Gate.h"
|
||||
@@ -668,7 +669,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
bool GateVisitor::elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor) {
|
||||
// Return true if can optimize
|
||||
// Return false if the consuming logic has an output signal that the replacement logic has as an input
|
||||
typedef set<AstVarScope*> VarScopeSet;
|
||||
typedef vl_unordered_set<AstVarScope*> VarScopeSet;
|
||||
// Use map to find duplicates between two lists
|
||||
VarScopeSet varscopes;
|
||||
// Replacement logic usually has shorter input list, so faster to build list based on it
|
||||
|
||||
+22
-8
@@ -149,9 +149,10 @@ private:
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstActive* m_activep; // Inside activate statement
|
||||
AstNodeAssign* m_assignp; // Inside assigndly statement
|
||||
AstNodeModule* m_topModp; // Top module
|
||||
AstActive* m_activep; // Inside activate statement
|
||||
AstCFunc* m_tracingCallp; // Currently tracing a call to this cfunc
|
||||
AstNodeAssign* m_assignp; // Inside assigndly statement
|
||||
AstNodeModule* m_topModp; // Top module
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
@@ -173,8 +174,20 @@ private:
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
// Enter the function and trace it
|
||||
|
||||
m_tracingCallp = nodep->funcp();
|
||||
nodep->funcp()->accept(*this);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
if (m_tracingCallp != nodep) {
|
||||
// Only consider logic within a CFunc when looking
|
||||
// at the call to it, and not when scanning whatever
|
||||
// scope it happens to live beneath.
|
||||
return;
|
||||
}
|
||||
m_tracingCallp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
//----
|
||||
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
@@ -213,11 +226,12 @@ private:
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit GenClkReadVisitor(AstNetlist* nodep) {
|
||||
m_activep = NULL;
|
||||
m_assignp = NULL;
|
||||
m_topModp = NULL;
|
||||
nodep->accept(*this);
|
||||
explicit GenClkReadVisitor(AstNetlist* nodep)
|
||||
: m_activep(NULL)
|
||||
, m_tracingCallp(NULL)
|
||||
, m_assignp(NULL)
|
||||
, m_topModp(NULL) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~GenClkReadVisitor() {}
|
||||
};
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ public:
|
||||
V3Options opt; // All options; let user see them directly
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
V3Global() {
|
||||
m_debugFileNumber = 0;
|
||||
m_widthMinUsage = VWidthMinUsage::LINT_WIDTH;
|
||||
|
||||
+4
-4
@@ -274,21 +274,21 @@ void V3Graph::dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep)
|
||||
}
|
||||
}
|
||||
|
||||
void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph) {
|
||||
void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph) const {
|
||||
if (v3Global.opt.dumpTree()) {
|
||||
dumpDotFile(v3Global.debugFilename(nameComment)+".dot", colorAsSubgraph);
|
||||
}
|
||||
}
|
||||
|
||||
//! Variant of dumpDotFilePrefixed without --dump option check
|
||||
void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph) {
|
||||
void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph) const {
|
||||
dumpDotFile(v3Global.debugFilename(nameComment)+".dot", colorAsSubgraph);
|
||||
}
|
||||
|
||||
void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) {
|
||||
void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
|
||||
// This generates a file used by graphviz, http://www.graphviz.org
|
||||
// "hardcoded" parameters:
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
|
||||
// Header
|
||||
|
||||
+6
-5
@@ -65,7 +65,7 @@ public:
|
||||
V3Graph();
|
||||
virtual ~V3Graph();
|
||||
static void debug(int level) { s_debug = level; }
|
||||
virtual string dotRankDir() { return "TB"; } // rankdir for dot plotting
|
||||
virtual string dotRankDir() const { return "TB"; } // rankdir for dot plotting
|
||||
|
||||
// METHODS
|
||||
void clear(); // Empty it of all vertices/edges, as if making a new object
|
||||
@@ -127,12 +127,12 @@ public:
|
||||
|
||||
/// Debugging
|
||||
void dump(ostream& os=cout);
|
||||
void dumpDotFile(const string& filename, bool colorAsSubgraph);
|
||||
void dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph=false);
|
||||
void dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph=false);
|
||||
void dumpDotFile(const string& filename, bool colorAsSubgraph) const;
|
||||
void dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph=false) const;
|
||||
void dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph=false) const;
|
||||
void userClearVertices();
|
||||
void userClearEdges();
|
||||
static void test();
|
||||
static void selfTest();
|
||||
|
||||
// CALLBACKS
|
||||
virtual void loopsMessageCb(V3GraphVertex* vertexp) { v3fatalSrc("Loops detected in graph: "<<vertexp); }
|
||||
@@ -181,6 +181,7 @@ public:
|
||||
virtual string dotShape() const { return ""; }
|
||||
virtual string dotStyle() const { return ""; }
|
||||
virtual string dotName() const { return ""; }
|
||||
virtual uint32_t rankAdder() const { return 1; }
|
||||
virtual int sortCmp(const V3GraphVertex* rhsp) const {
|
||||
// LHS goes first if of lower rank, or lower fanout
|
||||
if (m_rank < rhsp->m_rank) return -1;
|
||||
|
||||
+1
-1
@@ -300,7 +300,7 @@ private:
|
||||
vertexp->rank(currentRank);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
if (followEdge(edgep)) {
|
||||
vertexIterate(edgep->top(),currentRank+1);
|
||||
vertexIterate(edgep->top(), currentRank + vertexp->rankAdder());
|
||||
}
|
||||
}
|
||||
vertexp->user(2);
|
||||
|
||||
+1
-1
@@ -351,7 +351,7 @@ void V3GraphTestImport::dotImport() {
|
||||
|
||||
//======================================================================
|
||||
|
||||
void V3Graph::test() {
|
||||
void V3Graph::selfTest() {
|
||||
// Execute all of the tests
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{ V3GraphTestStrong test; test.run(); }
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ void V3Hashed::dumpFilePrefixed(const string& nameComment, bool tree) {
|
||||
}
|
||||
|
||||
void V3Hashed::dumpFile(const string& filename, bool tree) {
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
|
||||
map<int,int> dist;
|
||||
|
||||
+62
-12
@@ -35,6 +35,7 @@
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3LinkCells.h"
|
||||
@@ -53,7 +54,6 @@ public:
|
||||
virtual void loopsMessageCb(V3GraphVertex* vertexp);
|
||||
};
|
||||
|
||||
|
||||
class LinkCellsVertex : public V3GraphVertex {
|
||||
AstNodeModule* m_modp;
|
||||
public:
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
virtual ~LinkCellsVertex() {}
|
||||
AstNodeModule* modp() const { return m_modp; }
|
||||
virtual string name() const { return modp()->name(); }
|
||||
// Recursive modules get space for maximum recursion
|
||||
virtual uint32_t rankAdder() const { return m_modp->recursiveClone() ? (1+v3Global.opt.moduleRecursionDepth()) : 1; }
|
||||
};
|
||||
|
||||
class LibraryVertex : public V3GraphVertex {
|
||||
@@ -74,8 +76,9 @@ public:
|
||||
|
||||
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
|
||||
if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
|
||||
vvertexp->modp()->v3error("Recursive module (module instantiates itself): "
|
||||
vvertexp->modp()->v3error("Unsupported: Recursive multiple modules (module instantiates something leading back to itself): "
|
||||
<<vvertexp->modp()->prettyName());
|
||||
vvertexp->modp()->v3error("Note self-recursion (module instantiating itself directly) is supported.");
|
||||
V3Error::abortIfErrors();
|
||||
} else { // Everything should match above, but...
|
||||
v3fatalSrc("Recursive instantiations");
|
||||
@@ -90,10 +93,13 @@ private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNodeModule::user1p() // V3GraphVertex* Vertex describing this module
|
||||
// AstCell::user1() // bool Did it.
|
||||
// AstNodeModule::user2p() // AstNodeModule* clone used for de-recursing
|
||||
// AstCell::user1p() // ==V3NodeModule* if done, != if unprocessed
|
||||
// AstCell::user2() // bool clone renaming completed
|
||||
// Allocated across all readFiles in V3Global::readFiles:
|
||||
// AstNode::user4p() // VSymEnt* Package and typedef symbol names
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// STATE
|
||||
V3InFilter* m_filterp; // Parser filter
|
||||
@@ -105,7 +111,7 @@ private:
|
||||
LinkCellsGraph m_graph; // Linked graph of all cell interconnects
|
||||
LibraryVertex* m_libVertexp; // Vertex at root of all libraries
|
||||
V3GraphVertex* m_topVertexp; // Vertex of top module
|
||||
set<string> m_declfnWarned; // Files we issued DECLFILENAME on
|
||||
vl_unordered_set<string> m_declfnWarned; // Files we issued DECLFILENAME on
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
@@ -188,6 +194,7 @@ private:
|
||||
UINFO(2,"Link Module: "<<nodep<<endl);
|
||||
if (nodep->fileline()->filebasenameNoExt() != nodep->prettyName()
|
||||
&& !v3Global.opt.isLibraryFile(nodep->fileline()->filename())
|
||||
&& !nodep->recursiveClone()
|
||||
&& !nodep->internal()) {
|
||||
// We only complain once per file, otherwise library-like files have a huge mess of warnings
|
||||
if (m_declfnWarned.find(nodep->fileline()->filename()) == m_declfnWarned.end()) {
|
||||
@@ -266,16 +273,58 @@ private:
|
||||
|
||||
virtual void visit(AstCell* nodep) {
|
||||
// Cell: Resolve its filename. If necessary, parse it.
|
||||
if (nodep->user1SetOnce()) return; // AstBind and AstNodeModule may call a cell twice
|
||||
if (!nodep->modp()) {
|
||||
// Execute only once. Complication is that cloning may result in user1 being set (for pre-clone)
|
||||
// so check if user1() matches the m_mod, if 0 never did it, if !=, it is an unprocessed clone
|
||||
bool cloned = (nodep->user1p() && nodep->user1p()!=m_modp);
|
||||
if (nodep->user1p()==m_modp) return; // AstBind and AstNodeModule may call a cell twice
|
||||
nodep->user1p(m_modp);
|
||||
//
|
||||
if (!nodep->modp() || cloned) {
|
||||
UINFO(4,"Link Cell: "<<nodep<<endl);
|
||||
// Use findIdFallback instead of findIdFlat; it doesn't matter for now
|
||||
// but we might support modules-under-modules someday.
|
||||
AstNodeModule* modp = resolveModule(nodep,nodep->modName());
|
||||
if (modp) {
|
||||
nodep->modp(modp);
|
||||
// Track module depths, so can sort list from parent down to children
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
|
||||
AstNodeModule* cellmodp = resolveModule(nodep, nodep->modName());
|
||||
if (cellmodp) {
|
||||
if (cellmodp == m_modp
|
||||
|| cellmodp->user2p() == m_modp) {
|
||||
UINFO(1,"Self-recursive module "<<cellmodp<<endl);
|
||||
cellmodp->recursive(true);
|
||||
nodep->recursive(true);
|
||||
if (!cellmodp->recursiveClone()) {
|
||||
// In the non-Vrcm, which needs to point to Vrcm flavor
|
||||
//
|
||||
// Make a clone which this cell points to
|
||||
// Later, the clone's cells will also point clone'd name
|
||||
// This lets us link the XREFs between the (uncloned) children so
|
||||
// they don't point to the same module which would
|
||||
// break parameter resolution.
|
||||
AstNodeModule* otherModp = cellmodp->user2p()->castNodeModule();
|
||||
if (!otherModp) {
|
||||
otherModp = cellmodp->cloneTree(false);
|
||||
otherModp->name(otherModp->name()+"__Vrcm");
|
||||
otherModp->user1p(NULL); // Need new vertex
|
||||
otherModp->user2p(cellmodp);
|
||||
otherModp->recursiveClone(true);
|
||||
// user1 etc will retain its pre-clone value
|
||||
cellmodp->user2p(otherModp);
|
||||
v3Global.rootp()->addModulep(otherModp);
|
||||
new V3GraphEdge(&m_graph, vertex(cellmodp), vertex(otherModp), 1, false);
|
||||
}
|
||||
cellmodp = otherModp;
|
||||
nodep->modp(cellmodp);
|
||||
}
|
||||
else {
|
||||
// In the Vrcm, which needs to point back to Vrcm flavor
|
||||
// The cell already has the correct resolution (to Vrcm)
|
||||
nodep->modp(cellmodp);
|
||||
// We don't create a V3GraphEdge (as it would be circular)
|
||||
}
|
||||
}
|
||||
else { // Non-recursive
|
||||
// Track module depths, so can sort list from parent down to children
|
||||
nodep->modp(cellmodp);
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(cellmodp), 1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove AstCell(AstPin("",NULL)), it's a side effect of how we parse "()"
|
||||
@@ -310,8 +359,9 @@ private:
|
||||
if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum()));
|
||||
}
|
||||
if (nodep->modp()) {
|
||||
nodep->modName(nodep->modp()->name());
|
||||
// Note what pins exist
|
||||
set<string> ports; // Symbol table of all connected port names
|
||||
vl_unordered_set<string> ports; // Symbol table of all connected port names
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells");
|
||||
if (!pinp->exprp()) {
|
||||
|
||||
+40
-10
@@ -87,6 +87,7 @@ private:
|
||||
// NODE STATE
|
||||
// Cleared on Netlist
|
||||
// AstNodeModule::user1p() // VSymEnt*. Last symbol created for this node
|
||||
// AstNodeModule::user2() // bool. Currently processing for recursion check
|
||||
// ... Note maybe more than one, as can be multiple hierarchy places
|
||||
// AstVarScope::user2p() // AstVarScope*. Base alias for AstInline of this signal
|
||||
// AstVar::user2p() // AstFTask*. If a function variable, the task that links to the variable
|
||||
@@ -134,7 +135,7 @@ public:
|
||||
void dump(const string& nameComment="linkdot", bool force=false) {
|
||||
if (debug()>=6 || force) {
|
||||
string filename = v3Global.debugFilename(nameComment)+".txt";
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
ostream& os = *logp;
|
||||
m_syms.dump(os);
|
||||
@@ -595,6 +596,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
AstBegin* m_beginp; // Current Begin/end block
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
bool m_inGenerate; // Inside a generate
|
||||
bool m_inRecursion; // Inside a recursive module
|
||||
int m_paramNum; // Parameter number, for position based connection
|
||||
int m_beginNum; // Begin block number, 0=none seen
|
||||
int m_modBeginNum; // Begin block number in module, 0=none seen
|
||||
@@ -682,8 +684,11 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
int oldParamNum = m_paramNum;
|
||||
int oldBeginNum = m_beginNum;
|
||||
int oldModBeginNum = m_modBeginNum;
|
||||
if (doit) {
|
||||
UINFO(2," Link Module: "<<nodep<<endl);
|
||||
if (doit && nodep->user2()) {
|
||||
nodep->v3error("Unsupported: Identically recursive module (module instantiates itself, without changing parameters): "
|
||||
<<AstNode::prettyName(nodep->origName()));
|
||||
} else if (doit) {
|
||||
UINFO(4," Link Module: "<<nodep<<endl);
|
||||
if (nodep->dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?");
|
||||
VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
|
||||
m_packagep = nodep->castPackage();
|
||||
@@ -703,7 +708,9 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
m_modBeginNum = 0;
|
||||
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
|
||||
// Iterate
|
||||
nodep->user2(true);
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->user2(false);
|
||||
nodep->user4(true);
|
||||
// Interfaces need another pass when signals are resolved
|
||||
if (AstIface* ifacep = nodep->castIface()) {
|
||||
@@ -730,6 +737,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
virtual void visit(AstCell* nodep) {
|
||||
UINFO(5," CELL under "<<m_scope<<" is "<<nodep<<endl);
|
||||
// Process XREFs/etc inside pins
|
||||
if (nodep->recursive() && m_inRecursion) return;
|
||||
nodep->iterateChildren(*this);
|
||||
// Recurse in, preserving state
|
||||
string oldscope = m_scope;
|
||||
@@ -737,6 +745,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
VSymEnt* oldModSymp = m_modSymp;
|
||||
VSymEnt* oldCurSymp = m_curSymp;
|
||||
int oldParamNum = m_paramNum;
|
||||
bool oldRecursion = m_inRecursion;
|
||||
// Where do we add it?
|
||||
VSymEnt* aboveSymp = m_curSymp;
|
||||
string origname = AstNode::dedotName(nodep->name());
|
||||
@@ -755,6 +764,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
m_scope = m_scope+"."+nodep->name();
|
||||
m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope);
|
||||
m_beginp = NULL;
|
||||
m_inRecursion = nodep->recursive();
|
||||
// We don't report NotFoundModule, as may be a unused module in a generate
|
||||
if (nodep->modp()) nodep->modp()->accept(*this);
|
||||
}
|
||||
@@ -763,6 +773,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
m_modSymp = oldModSymp;
|
||||
m_curSymp = oldCurSymp;
|
||||
m_paramNum = oldParamNum;
|
||||
m_inRecursion = oldRecursion;
|
||||
}
|
||||
virtual void visit(AstCellInline* nodep) {
|
||||
UINFO(5," CELLINLINE under "<<m_scope<<" is "<<nodep<<endl);
|
||||
@@ -1005,7 +1016,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
}
|
||||
}
|
||||
virtual void visit(AstPackageImport* nodep) {
|
||||
UINFO(2," Link: "<<nodep<<endl);
|
||||
UINFO(4," Link: "<<nodep<<endl);
|
||||
VSymEnt* srcp = m_statep->getNodeSym(nodep->packagep());
|
||||
if (nodep->name()!="*") {
|
||||
VSymEnt* impp = srcp->findIdFlat(nodep->name());
|
||||
@@ -1031,7 +1042,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
// No longer needed, but can't delete until any multi-instantiated modules are expanded
|
||||
}
|
||||
virtual void visit(AstPackageExportStarStar* nodep) {
|
||||
UINFO(2," Link: "<<nodep<<endl);
|
||||
UINFO(4," Link: "<<nodep<<endl);
|
||||
m_curSymp->exportStarStar(m_statep->symsp());
|
||||
// No longer needed, but can't delete until any multi-instantiated modules are expanded
|
||||
}
|
||||
@@ -1051,6 +1062,7 @@ public:
|
||||
m_beginp = NULL;
|
||||
m_ftaskp = NULL;
|
||||
m_inGenerate = false;
|
||||
m_inRecursion = false;
|
||||
m_paramNum = 0;
|
||||
m_beginNum = 0;
|
||||
m_modBeginNum = 0;
|
||||
@@ -1394,9 +1406,10 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
} else if (AstVar* varp = symp->nodep()->castVar()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
// Make symbol under modport that points at the _interface_'s var via the modport.
|
||||
// (Need modport still to test input/output markings)
|
||||
nodep->varp(varp);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), varp, NULL/*package*/);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, NULL/*package*/);
|
||||
} else if (AstVarScope* vscp = symp->nodep()->castVarScope()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(vscp->varp());
|
||||
@@ -1514,6 +1527,23 @@ private:
|
||||
m_statep->insertSym(moduleSymp, newp->name(), newp, NULL/*packagep*/);
|
||||
}
|
||||
}
|
||||
AstVar* foundToVarp(const VSymEnt* symp, AstNode* nodep, bool lvalue) {
|
||||
// Return a variable if possible, auto converting a modport to variable
|
||||
if (!symp) {
|
||||
return NULL;
|
||||
} else if (symp->nodep()->castVar()) {
|
||||
return symp->nodep()->castVar();
|
||||
} else if (symp->nodep()->castModportVarRef()) {
|
||||
AstModportVarRef* snodep = symp->nodep()->castModportVarRef();
|
||||
AstVar* varp = snodep->varp();
|
||||
if (lvalue && snodep->isInput()) {
|
||||
nodep->v3error("Attempt to drive input-only modport: "<<nodep->prettyName());
|
||||
} // else other simulators don't warn about reading, and IEEE doesn't say illegal
|
||||
return varp;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
|
||||
if (nodep->taskp() && nodep->taskp()->castTask()
|
||||
&& nodep->castFuncRef()) nodep->v3error("Illegal call of a task as a function: "<<nodep->prettyName());
|
||||
@@ -1805,7 +1835,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (AstVar* varp = foundp->nodep()->castVar()) {
|
||||
else if (AstVar* varp = foundToVarp(foundp, nodep, false)) {
|
||||
AstIfaceRefDType* ifacerefp = LinkDotState::ifaceRefFromArray(varp->subDTypep());
|
||||
if (ifacerefp) {
|
||||
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
@@ -1926,7 +1956,7 @@ private:
|
||||
UINFO(9," linkVarRef se"<<(void*)m_curSymp<<" n="<<nodep<<endl);
|
||||
if (!m_curSymp) nodep->v3fatalSrc("NULL lookup symbol table");
|
||||
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (AstVar* varp = foundp ? foundp->nodep()->castVar() : NULL) {
|
||||
if (AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL) {
|
||||
nodep->varp(varp);
|
||||
nodep->packagep(foundp->packagep()); // Generally set by parse, but might be an import
|
||||
}
|
||||
@@ -1960,7 +1990,7 @@ private:
|
||||
dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL
|
||||
if (!m_statep->forScopeCreation()) {
|
||||
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
AstVar* varp = foundp ? foundp->nodep()->castVar() : NULL;
|
||||
AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL;
|
||||
nodep->varp(varp);
|
||||
UINFO(7," Resolved "<<nodep<<endl); // Also prints varp
|
||||
if (!nodep->varp()) {
|
||||
|
||||
+1
-1
@@ -336,7 +336,7 @@ private:
|
||||
AstNode* lastVarsp = firstVarsp;
|
||||
while (lastVarsp->nextp()) { lastVarsp = lastVarsp->nextp(); dimension++; }
|
||||
for (AstNode* varsp = lastVarsp; varsp; varsp=varsp->backp()) {
|
||||
UINFO(0,"foreachVar "<<varsp<<endl);
|
||||
UINFO(9,"foreachVar "<<varsp<<endl);
|
||||
FileLine* fl = varsp->fileline();
|
||||
AstNode* varp = new AstVar(fl, AstVarType::BLOCKTEMP,
|
||||
varsp->name(), nodep->findSigned32DType());
|
||||
|
||||
+8
-1
@@ -984,6 +984,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
shift; m_prefix = argv[i];
|
||||
if (m_modPrefix=="") m_modPrefix = m_prefix;
|
||||
}
|
||||
else if ( !strcmp (sw, "-no-threads") ) { m_threads = 0; } // Undocumented until functional
|
||||
else if ( !strcmp (sw, "-threads") && (i+1)<argc ) { // Undocumented until functional
|
||||
shift; m_threads = atoi(argv[i]);
|
||||
if (m_threads < 0) fl->v3fatal("--threads must be >= 0: "<<argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-top-module") && (i+1)<argc ) {
|
||||
shift; m_topModule = argv[i];
|
||||
}
|
||||
@@ -1047,7 +1052,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
||||
// Read the specified -f filename and process as arguments
|
||||
UINFO(1,"Reading Options File "<<filename<<endl);
|
||||
|
||||
const VL_UNIQUE_PTR<ifstream> ifp (V3File::new_ifstream(filename));
|
||||
const vl_unique_ptr<ifstream> ifp (V3File::new_ifstream(filename));
|
||||
if (ifp->fail()) {
|
||||
fl->v3error("Cannot open -f command file: "+filename);
|
||||
return;
|
||||
@@ -1218,6 +1223,7 @@ V3Options::V3Options() {
|
||||
m_stats = false;
|
||||
m_statsVars = false;
|
||||
m_systemC = false;
|
||||
m_threads = 0;
|
||||
m_trace = false;
|
||||
m_traceDups = false;
|
||||
m_traceParams = true;
|
||||
@@ -1232,6 +1238,7 @@ V3Options::V3Options() {
|
||||
m_dumpTree = 0;
|
||||
m_ifDepth = 0;
|
||||
m_inlineMult = 2000;
|
||||
m_moduleRecursion = 100;
|
||||
m_outputSplit = 0;
|
||||
m_outputSplitCFuncs = 0;
|
||||
m_outputSplitCTrace = 0;
|
||||
|
||||
+6
-3
@@ -109,10 +109,12 @@ class V3Options {
|
||||
int m_dumpTree; // main switch: --dump-tree
|
||||
int m_ifDepth; // main switch: --if-depth
|
||||
int m_inlineMult; // main switch: --inline-mult
|
||||
int m_moduleRecursion;// main switch: --module-recursion-depth
|
||||
int m_outputSplit; // main switch: --output-split
|
||||
int m_outputSplitCFuncs;// main switch: --output-split-cfuncs
|
||||
int m_outputSplitCTrace;// main switch: --output-split-ctrace
|
||||
int m_pinsBv; // main switch: --pins-bv
|
||||
int m_threads; // main switch: --threads (0 == --no-threads)
|
||||
int m_traceDepth; // main switch: --trace-depth
|
||||
int m_traceMaxArray;// main switch: --trace-max-array
|
||||
int m_traceMaxWidth;// main switch: --trace-max-width
|
||||
@@ -179,10 +181,9 @@ class V3Options {
|
||||
bool parseLangExt(const char* swp, const char* langswp, const V3LangCode& lc);
|
||||
string filePathCheckOneDir(const string& modname, const string& dirname);
|
||||
|
||||
V3Options(const V3Options&); ///< N/A, no copy constructor
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(V3Options);
|
||||
public:
|
||||
// CREATORS
|
||||
V3Options();
|
||||
~V3Options();
|
||||
void setDebugMode(int level);
|
||||
@@ -254,10 +255,12 @@ class V3Options {
|
||||
int dumpTree() const { return m_dumpTree; }
|
||||
int ifDepth() const { return m_ifDepth; }
|
||||
int inlineMult() const { return m_inlineMult; }
|
||||
int moduleRecursionDepth() const { return m_moduleRecursion; }
|
||||
int outputSplit() const { return m_outputSplit; }
|
||||
int outputSplitCFuncs() const { return m_outputSplitCFuncs; }
|
||||
int outputSplitCTrace() const { return m_outputSplitCTrace; }
|
||||
int pinsBv() const { return m_pinsBv; }
|
||||
int threads() const { return m_threads; }
|
||||
int traceDepth() const { return m_traceDepth; }
|
||||
int traceMaxArray() const { return m_traceMaxArray; }
|
||||
int traceMaxWidth() const { return m_traceMaxWidth; }
|
||||
|
||||
+83
-54
@@ -127,21 +127,19 @@ public:
|
||||
V3List<OrderMoveVertex*> m_readyVertices; // Ready vertices with same domain & scope
|
||||
private:
|
||||
bool m_onReadyList; // True if DomScope is already on list of ready dom/scopes
|
||||
AstSenTree* m_domainp; // Domain all vertices belong to
|
||||
AstScope* m_scopep; // Scope all vertices belong to
|
||||
OrderLoopId m_inLoop; // Loop member of
|
||||
const AstSenTree* m_domainp; // Domain all vertices belong to
|
||||
const AstScope* m_scopep; // Scope all vertices belong to
|
||||
|
||||
typedef pair<pair<OrderLoopId, AstSenTree*>, AstScope*> DomScopeKey;
|
||||
typedef pair<const AstSenTree*, const AstScope*> DomScopeKey;
|
||||
typedef std::map<DomScopeKey, OrderMoveDomScope*> DomScopeMap;
|
||||
static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing
|
||||
|
||||
public:
|
||||
OrderMoveDomScope(OrderLoopId inLoop, AstSenTree* domainp, AstScope* scopep)
|
||||
: m_onReadyList(false), m_domainp(domainp), m_scopep(scopep), m_inLoop(inLoop) {}
|
||||
OrderMoveDomScope(const AstSenTree* domainp, const AstScope* scopep)
|
||||
: m_onReadyList(false), m_domainp(domainp), m_scopep(scopep) {}
|
||||
OrderMoveDomScope* readyDomScopeNextp() const { return m_readyDomScopeE.nextp(); }
|
||||
OrderLoopId inLoop() const { return m_inLoop; }
|
||||
AstSenTree* domainp() const { return m_domainp; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
const AstSenTree* domainp() const { return m_domainp; }
|
||||
const AstScope* scopep() const { return m_scopep; }
|
||||
void ready(OrderVisitor* ovp); // Check the domScope is on ready list, add if not
|
||||
void movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp); // Mark one vertex as finished, remove from ready list if done
|
||||
// STATIC MEMBERS (for lookup)
|
||||
@@ -152,20 +150,20 @@ public:
|
||||
s_dsMap.clear();
|
||||
}
|
||||
V3List<OrderMoveVertex*>& readyVertices() { return m_readyVertices; }
|
||||
static OrderMoveDomScope* findCreate (OrderLoopId inLoop, AstSenTree* domainp, AstScope* scopep) {
|
||||
const DomScopeKey key = make_pair(make_pair(inLoop,domainp),scopep);
|
||||
static OrderMoveDomScope* findCreate(const AstSenTree* domainp,
|
||||
const AstScope* scopep) {
|
||||
const DomScopeKey key = make_pair(domainp,scopep);
|
||||
DomScopeMap::iterator iter = s_dsMap.find(key);
|
||||
if (iter != s_dsMap.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
OrderMoveDomScope* domScopep = new OrderMoveDomScope(inLoop, domainp, scopep);
|
||||
OrderMoveDomScope* domScopep = new OrderMoveDomScope(domainp, scopep);
|
||||
s_dsMap.insert(make_pair(key, domScopep));
|
||||
return domScopep;
|
||||
}
|
||||
}
|
||||
string name() const {
|
||||
return (string("MDS:")
|
||||
+" lp="+cvtToStr(inLoop())
|
||||
+" d="+cvtToStr((void*)domainp())
|
||||
+" s="+cvtToStr((void*)scopep()));
|
||||
}
|
||||
@@ -520,28 +518,6 @@ private:
|
||||
return varVxp;
|
||||
}
|
||||
|
||||
V3GraphEdge* findEndEdge(V3GraphVertex* vertexp, AstNode* errnodep, OrderLoopEndVertex*& evertexpr) {
|
||||
// Given a vertex, find the end block corresponding to it
|
||||
// Every vertex should have a pointer to the end block (one hopes)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (OrderLoopEndVertex* evertexp = dynamic_cast<OrderLoopEndVertex*>(edgep->top())) {
|
||||
evertexpr = evertexp;
|
||||
return edgep;
|
||||
}
|
||||
}
|
||||
errnodep->v3fatalSrc("Loop-broken vertex doesn't have pointer to LoopEndVertex: "<<vertexp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool isClkAssign(AstNodeAssign* nodep) {
|
||||
if (AstVarRef* varrefp = nodep->lhsp()->castVarRef()) {
|
||||
if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void process();
|
||||
void processCircular();
|
||||
typedef deque<OrderEitherVertex*> VertexVec;
|
||||
@@ -562,6 +538,7 @@ private:
|
||||
void processMoveReadyOne(OrderMoveVertex* vertexp);
|
||||
void processMoveDoneOne(OrderMoveVertex* vertexp);
|
||||
void processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* domScopep, int level);
|
||||
static bool domainsExclusive(const AstSenTree* fromp, const AstSenTree* top);
|
||||
|
||||
string cfuncName(AstNodeModule* modp, AstSenTree* domainp, AstScope* scopep, AstNode* forWhatp) {
|
||||
modp->user3Inc();
|
||||
@@ -921,7 +898,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) {
|
||||
// Having a node derived from the sentree isn't required for
|
||||
// correctness, it mearly makes the graph better connected
|
||||
// correctness, it merely makes the graph better connected
|
||||
// and improves graph algorithmic performance
|
||||
if (m_scopep) { // Else TOPSCOPE's SENTREE list
|
||||
m_inSenTree = true;
|
||||
@@ -1197,7 +1174,6 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
// Combo logic may be pushed into a seq domain if all its inputs are the same domain,
|
||||
// else, if all inputs are from flops, it's end-of-sequential code
|
||||
// else, it's full combo code
|
||||
if (!vertexp->inLoop()) vertexp->inLoop(LOOPID_NOTLOOPED);
|
||||
if (vertexp->domainp()) return; // Already processed, or sequential logic
|
||||
UINFO(5," pdi: "<<vertexp<<endl);
|
||||
OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp);
|
||||
@@ -1286,7 +1262,7 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
void OrderVisitor::processEdgeReport() {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
string filename = v3Global.debugFilename("order_edges.txt");
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
//Testing emitter: V3EmitV::verilogForTree(v3Global.rootp(), *logp);
|
||||
|
||||
@@ -1350,21 +1326,76 @@ void OrderVisitor::processMoveBuildGraph() {
|
||||
}
|
||||
}
|
||||
|
||||
bool OrderVisitor::domainsExclusive(const AstSenTree* fromp,
|
||||
const AstSenTree* top) {
|
||||
// Return 'true' if we can prove that both 'from' and 'to' cannot both
|
||||
// be active on the same eval pass, or false if we can't prove this.
|
||||
//
|
||||
// For now, this only detects the case of 'always @(posedge clk)'
|
||||
// and 'always @(negedge clk)' being exclusive.
|
||||
//
|
||||
// Are there any other cases we need to handle? Maybe not,
|
||||
// because these are not exclusive:
|
||||
// always @(posedge A or posedge B)
|
||||
// always @(negedge A)
|
||||
//
|
||||
// ... unless you know more about A and B, which sounds hard.
|
||||
const AstSenItem* fromSenListp = fromp->sensesp()->castSenItem();
|
||||
const AstSenItem* toSenListp = top->sensesp()->castSenItem();
|
||||
// If clk gating is ever reenabled, we may need to update this to handle
|
||||
// AstSenGate also.
|
||||
if (!fromSenListp) fromp->v3fatalSrc("sensitivity list item is not an AstSenItem");
|
||||
if (!toSenListp) top->v3fatalSrc("sensitivity list item is not an AstSenItem");
|
||||
|
||||
if (fromSenListp->nextp()) return false;
|
||||
if (toSenListp->nextp()) return false;
|
||||
|
||||
const AstNodeVarRef* fromVarrefp = fromSenListp->varrefp();
|
||||
const AstNodeVarRef* toVarrefp = toSenListp->varrefp();
|
||||
if (!fromVarrefp || !toVarrefp) return false;
|
||||
|
||||
// We know nothing about the relationship between different clocks here,
|
||||
// so give up on proving anything.
|
||||
if (fromVarrefp->varScopep() != toVarrefp->varScopep()) return false;
|
||||
|
||||
return fromSenListp->edgeType().exclusiveEdge(toSenListp->edgeType());
|
||||
}
|
||||
|
||||
void OrderVisitor::processMoveBuildGraphIterate (OrderMoveVertex* moveVxp, V3GraphVertex* vertexp, int weightmin) {
|
||||
// Search forward from given logic vertex, making new edges based on moveVxp
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
if (edgep->weight()!=0) { // was cut
|
||||
int weight = weightmin;
|
||||
if (weight==0 || weight>edgep->weight()) weight=edgep->weight();
|
||||
if (OrderLogicVertex* toLVertexp = dynamic_cast<OrderLogicVertex*>(edgep->top())) {
|
||||
// Path from vertexp to a logic vertex; new edge
|
||||
// Note we use the last edge's weight, not some function of multiple edges
|
||||
new OrderEdge(&m_pomGraph, moveVxp, toLVertexp->moveVxp(), weight);
|
||||
}
|
||||
else { // Keep hunting forward for a logic node
|
||||
processMoveBuildGraphIterate(moveVxp, edgep->top(), weight);
|
||||
}
|
||||
}
|
||||
if (edgep->weight()!=0) { // was cut
|
||||
int weight = weightmin;
|
||||
if (weight==0 || weight>edgep->weight()) weight=edgep->weight();
|
||||
if (OrderLogicVertex* toLVertexp = dynamic_cast<OrderLogicVertex*>(edgep->top())) {
|
||||
// "Initial" and "settle" domains run at time 0 and never
|
||||
// again. Everything else runs at time >0 and never before.
|
||||
// Ignore deps that cross the time-zero/nonzero boundary.
|
||||
// We'll never run both vertices in the same eval() pass
|
||||
// so there's no need to order them.
|
||||
bool toInitial = (toLVertexp->domainp()->hasInitial()
|
||||
|| toLVertexp->domainp()->hasSettle());
|
||||
bool fromInitial = (moveVxp->logicp()->domainp()->hasInitial()
|
||||
|| moveVxp->logicp()->domainp()->hasSettle());
|
||||
if (toInitial != fromInitial) {
|
||||
continue;
|
||||
}
|
||||
// Sometimes we get deps between 'always @(posedge clk)' and
|
||||
// 'always @(negedge clk)' domains. Discard these. We'll never
|
||||
// evaluate both logic vertices on the same eval() pass so
|
||||
// there's no need to order them.
|
||||
if (domainsExclusive(moveVxp->logicp()->domainp(),
|
||||
toLVertexp->domainp())) {
|
||||
continue;
|
||||
}
|
||||
// Path from vertexp to a logic vertex; new edge
|
||||
// Note we use the last edge's weight, not some function of multiple edges
|
||||
new OrderEdge(&m_pomGraph, moveVxp, toLVertexp->moveVxp(), weight);
|
||||
}
|
||||
else { // Keep hunting forward for a logic node
|
||||
processMoveBuildGraphIterate(moveVxp, edgep->top(), weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1425,9 +1456,8 @@ void OrderVisitor::processMovePrepScopes() {
|
||||
for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp; vertexp=vertexp->pomWaitingNextp()) {
|
||||
AstSenTree* domainp = vertexp->logicp()->domainp();
|
||||
AstScope* scopep = vertexp->logicp()->scopep();
|
||||
OrderLoopId inLoop = vertexp->logicp()->inLoop();
|
||||
// Create the dom pairing for later lookup
|
||||
OrderMoveDomScope* domScopep = OrderMoveDomScope::findCreate(inLoop, domainp, scopep);
|
||||
OrderMoveDomScope* domScopep = OrderMoveDomScope::findCreate(domainp, scopep);
|
||||
vertexp->domScopep(domScopep);
|
||||
}
|
||||
}
|
||||
@@ -1539,7 +1569,6 @@ void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* d
|
||||
inline void OrderMoveDomScope::ready(OrderVisitor* ovp) { // Check the domScope is on ready list, add if not
|
||||
if (!m_onReadyList) {
|
||||
m_onReadyList = true;
|
||||
UASSERT (inLoop()!=0, "Loop# 0 is illegal, perhaps should be LOOPID_NOTLOOPED?");
|
||||
m_readyDomScopeE.pushBack(ovp->m_pomReadyDomScope, this);
|
||||
}
|
||||
}
|
||||
@@ -1605,7 +1634,7 @@ void OrderVisitor::process() {
|
||||
m_graph.dumpDotFilePrefixed("orderg_done");
|
||||
if (0 && debug()) {
|
||||
string dfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_INT_order";
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(dfilename));
|
||||
const vl_unique_ptr<ofstream> logp (V3File::new_ofstream(dfilename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<dfilename);
|
||||
m_graph.dump(*logp);
|
||||
}
|
||||
|
||||
+27
-101
@@ -25,8 +25,6 @@
|
||||
// OrderInputsVertex
|
||||
// OrderSettleVertex
|
||||
// OrderLogicVertex
|
||||
// OrderLoopBeginVertex
|
||||
// OrderLoopEndVertex
|
||||
// OrderVarVertex
|
||||
// OrderVarStdVertex
|
||||
// OrderVarPreVertex
|
||||
@@ -36,12 +34,15 @@
|
||||
//
|
||||
// V3GraphEdge
|
||||
// OrderEdge
|
||||
// OrderChangeDetEdge
|
||||
// OrderComboCutEdge
|
||||
// OrderPostCutEdge
|
||||
// OrderPreCutEdge
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
#ifndef _V3ORDERGRAPH_H_
|
||||
#define _V3ORDERGRAPH_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3Ast.h"
|
||||
@@ -62,13 +63,6 @@ enum OrderWeights {
|
||||
WEIGHT_MEDIUM = 8, // Medium weight just so dot graph looks nice
|
||||
WEIGHT_NORMAL = 32 }; // High weight just so dot graph looks nice
|
||||
|
||||
enum OrderLoopId {
|
||||
LOOPID_UNKNOWN = 0, // Not assigned yet
|
||||
LOOPID_NOTLOOPED=1, // Not looped
|
||||
LOOPID_FIRST = 2, // First assigned id (numbers increment from here)
|
||||
LOOPID_MAX = (1<<30)
|
||||
};
|
||||
|
||||
struct OrderVEdgeType {
|
||||
enum en {
|
||||
VERTEX_UNKNOWN = 0,
|
||||
@@ -80,8 +74,6 @@ struct OrderVEdgeType {
|
||||
VERTEX_VARPOST,
|
||||
VERTEX_VARPORD,
|
||||
VERTEX_VARSETTLE,
|
||||
VERTEX_LOOPBEGIN,
|
||||
VERTEX_LOOPEND,
|
||||
VERTEX_MOVE,
|
||||
EDGE_STD,
|
||||
EDGE_CHANGEDET,
|
||||
@@ -94,11 +86,9 @@ struct OrderVEdgeType {
|
||||
static const char* const names[] = {
|
||||
"%E-vedge", "VERTEX_INPUTS", "VERTEX_SETTLE", "VERTEX_LOGIC",
|
||||
"VERTEX_VARSTD", "VERTEX_VARPRE", "VERTEX_VARPOST",
|
||||
"VERTEX_VARPORD", "VERTEX_VARSETTLE", "VERTEX_LOOPBEGIN",
|
||||
"VERTEX_LOOPEND", "VERTEX_MOVE",
|
||||
"VERTEX_VARPORD", "VERTEX_VARSETTLE", "VERTEX_MOVE",
|
||||
"EDGE_STD", "EDGE_CHANGEDET", "EDGE_COMBOCUT",
|
||||
"EDGE_PRECUT", "EDGE_POSTCUT", "_ENUM_END"
|
||||
|
||||
};
|
||||
return names[m_e];
|
||||
}
|
||||
@@ -139,16 +129,15 @@ public:
|
||||
class OrderEitherVertex : public V3GraphVertex {
|
||||
AstScope* m_scopep; // Scope the vertex is in
|
||||
AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate)
|
||||
OrderLoopId m_inLoop; // Loop number vertex is in
|
||||
bool m_isFromInput; // From input, or derrived therefrom (conservatively false)
|
||||
bool m_isFromInput; // From input, or derived therefrom (conservatively false)
|
||||
protected:
|
||||
OrderEitherVertex(V3Graph* graphp, const OrderEitherVertex& old)
|
||||
: V3GraphVertex(graphp, old), m_scopep(old.m_scopep), m_domainp(old.m_domainp)
|
||||
, m_inLoop(old.m_inLoop), m_isFromInput(old.m_isFromInput) {}
|
||||
, m_isFromInput(old.m_isFromInput) {}
|
||||
public:
|
||||
OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp)
|
||||
: V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp)
|
||||
, m_inLoop(LOOPID_UNKNOWN), m_isFromInput(false) {}
|
||||
, m_isFromInput(false) {}
|
||||
virtual ~OrderEitherVertex() {}
|
||||
virtual OrderEitherVertex* clone(V3Graph* graphp) const = 0;
|
||||
// Methods
|
||||
@@ -159,8 +148,6 @@ public:
|
||||
void domainp(AstSenTree* domainp) { m_domainp = domainp; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstSenTree* domainp() const { return m_domainp; }
|
||||
OrderLoopId inLoop() const { return m_inLoop; }
|
||||
void inLoop(OrderLoopId inloop) { m_inLoop = inloop; }
|
||||
void isFromInput(bool flag) { m_isFromInput=flag; }
|
||||
bool isFromInput() const { return m_isFromInput; }
|
||||
};
|
||||
@@ -319,62 +306,6 @@ public:
|
||||
virtual bool domainMatters() { return false; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
//--- Looping constructs
|
||||
|
||||
class OrderLoopBeginVertex : public OrderLogicVertex {
|
||||
// A vertex can never be under two loops...
|
||||
// However, a LoopBeginVertex is not "under" the loop per se, and it may be under another loop.
|
||||
OrderLoopId m_loopId; // Arbitrary # to ID this loop
|
||||
uint32_t m_loopColor; // Color # of loop (for debug)
|
||||
OrderLoopBeginVertex(V3Graph* graphp, const OrderLoopBeginVertex& old)
|
||||
: OrderLogicVertex(graphp, *this)
|
||||
, m_loopId(old.m_loopId), m_loopColor(old.m_loopColor) {}
|
||||
public:
|
||||
OrderLoopBeginVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstUntilStable* nodep,
|
||||
OrderLoopId loopId, uint32_t loopColor)
|
||||
: OrderLogicVertex(graphp, scopep, domainp, nodep)
|
||||
, m_loopId(loopId), m_loopColor(loopColor) {}
|
||||
virtual ~OrderLoopBeginVertex() {}
|
||||
virtual OrderLoopBeginVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderLoopBeginVertex(graphp, *this);
|
||||
}
|
||||
// Methods
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOOPBEGIN; }
|
||||
virtual string name() const { return "LoopBegin_"+cvtToStr(loopId())+"_c"+cvtToStr(loopColor()); }
|
||||
virtual bool domainMatters() { return true; }
|
||||
virtual string dotColor() const { return "blue"; }
|
||||
AstUntilStable* untilp() const { return nodep()->castUntilStable(); }
|
||||
OrderLoopId loopId() const { return m_loopId; }
|
||||
uint32_t loopColor() const { return m_loopColor; }
|
||||
};
|
||||
|
||||
class OrderLoopEndVertex : public OrderLogicVertex {
|
||||
// A end vertex points to the *same nodep* as the Begin, as we need it to
|
||||
// be a logic vertex for moving, but don't need a permanent node. We
|
||||
// won't add to the output graph though, so it shouldn't matter.
|
||||
OrderLoopBeginVertex* m_beginVertexp; // Corresponding loop begin
|
||||
OrderLoopEndVertex(V3Graph* graphp, const OrderLoopEndVertex& old)
|
||||
: OrderLogicVertex(graphp, old), m_beginVertexp(old.m_beginVertexp) {}
|
||||
public:
|
||||
OrderLoopEndVertex(V3Graph* graphp, OrderLoopBeginVertex* beginVertexp)
|
||||
: OrderLogicVertex(graphp, beginVertexp->scopep(),
|
||||
beginVertexp->domainp(), beginVertexp->nodep())
|
||||
, m_beginVertexp(beginVertexp) {
|
||||
inLoop(beginVertexp->loopId());
|
||||
}
|
||||
virtual ~OrderLoopEndVertex() {}
|
||||
virtual OrderLoopEndVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderLoopEndVertex(graphp, *this); }
|
||||
// Methods
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOOPEND; }
|
||||
virtual string name() const { return "LoopEnd_"+cvtToStr(inLoop())+"_c"+cvtToStr(loopColor()); }
|
||||
virtual bool domainMatters() { return false; }
|
||||
virtual string dotColor() const { return "blue"; }
|
||||
OrderLoopBeginVertex* beginVertexp() const { return m_beginVertexp; }
|
||||
uint32_t loopColor() const { return beginVertexp()->loopColor(); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
//--- Following only under the move graph, not the main graph
|
||||
|
||||
@@ -403,14 +334,24 @@ public:
|
||||
return new OrderMoveVertex(graphp, *this);
|
||||
}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; }
|
||||
virtual string dotColor() const { return logicp()->dotColor(); }
|
||||
virtual string dotColor() const {
|
||||
if (logicp()) {
|
||||
return logicp()->dotColor();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
virtual string name() const {
|
||||
string nm = logicp()->name();
|
||||
nm += (string("\\nMV:")
|
||||
+" lp="+cvtToStr(logicp()->inLoop())
|
||||
+" d="+cvtToStr((void*)logicp()->domainp())
|
||||
+" s="+cvtToStr((void*)logicp()->scopep()));
|
||||
return nm;
|
||||
string nm;
|
||||
if (logicp()) {
|
||||
nm = logicp()->name();
|
||||
nm += (string("\\nMV:")
|
||||
+" d="+cvtToStr((void*)logicp()->domainp())
|
||||
+" s="+cvtToStr((void*)logicp()->scopep()));
|
||||
} else {
|
||||
nm = "nul";
|
||||
}
|
||||
return nm;
|
||||
}
|
||||
// ACCESSORS
|
||||
OrderLogicVertex* logicp() const { return m_logicp; }
|
||||
@@ -460,23 +401,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class OrderChangeDetEdge : public OrderEdge {
|
||||
// Edge created from variable to OrderLoopEndVertex
|
||||
// Indicates a change detect will be required for this loop construct
|
||||
OrderChangeDetEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const OrderChangeDetEdge& old)
|
||||
: OrderEdge(graphp, fromp, top, old) {}
|
||||
public:
|
||||
OrderChangeDetEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
||||
: OrderEdge(graphp, fromp, top, WEIGHT_MEDIUM, false) {}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_CHANGEDET; }
|
||||
virtual ~OrderChangeDetEdge() {}
|
||||
virtual OrderChangeDetEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderChangeDetEdge(graphp, fromp, top, *this);
|
||||
}
|
||||
virtual string dotColor() const { return "blue"; }
|
||||
};
|
||||
|
||||
class OrderComboCutEdge : public OrderEdge {
|
||||
// Edge created from output of combo logic
|
||||
// Breakable if the output var is also a input,
|
||||
@@ -536,3 +460,5 @@ public:
|
||||
virtual bool followComboConnected() const { return false; }
|
||||
virtual bool followSequentConnected() const { return false; }
|
||||
};
|
||||
|
||||
#endif // _V3ORDERGRAPH_H_
|
||||
|
||||
+52
-26
@@ -108,6 +108,8 @@ private:
|
||||
typedef deque<AstCell*> CellList;
|
||||
CellList m_cellps; // Cells left to process (in this module)
|
||||
|
||||
AstNodeModule* m_modp; // Current module being processed
|
||||
|
||||
string m_unlinkedTxt; // Text for AstUnlinkedRef
|
||||
|
||||
// METHODS
|
||||
@@ -218,6 +220,7 @@ private:
|
||||
AstNodeModule* nodep = it->second;
|
||||
m_todoModps.erase(it);
|
||||
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
|
||||
m_modp = nodep;
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
// Note above iterate may add to m_todoModps
|
||||
@@ -233,6 +236,7 @@ private:
|
||||
}
|
||||
}
|
||||
m_cellps.clear();
|
||||
m_modp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,10 +249,13 @@ private:
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
if (nodep->dead()) {
|
||||
UINFO(4," MOD-dead. "<<nodep<<endl); // Marked by LinkDot
|
||||
} else if (nodep->recursiveClone()) {
|
||||
UINFO(4," MOD-recursive-dead. "<<nodep<<endl); // Fake, made for recursive elimination
|
||||
nodep->dead(true); // So Dead checks won't count references to it
|
||||
} else if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top
|
||||
|| nodep->castPackage()) { // Likewise haven't done wrapTopPackages yet
|
||||
// Add request to END of modules left to process
|
||||
m_todoModps.insert(make_pair(nodep->level(),nodep));
|
||||
m_todoModps.insert(make_pair(nodep->level(), nodep));
|
||||
visitModules();
|
||||
} else if (nodep->user5()) {
|
||||
UINFO(4," MOD-done "<<nodep<<endl); // Already did it
|
||||
@@ -511,6 +518,7 @@ public:
|
||||
// CONSTUCTORS
|
||||
explicit ParamVisitor(AstNetlist* nodep) {
|
||||
m_longId = 0;
|
||||
m_modp = NULL;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
}
|
||||
@@ -524,20 +532,22 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
// Cell: Check for parameters in the instantiation.
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->modp()) nodep->v3fatalSrc("Not linked?");
|
||||
if (nodep->paramsp()
|
||||
|| 1 // Need to look for interfaces; could track when one exists, but should be harmless to always do this
|
||||
) {
|
||||
// We always run this, even if no parameters, as need to look for interfaces,
|
||||
// and remove any recursive references
|
||||
{
|
||||
UINFO(4,"De-parameterize: "<<nodep<<endl);
|
||||
// Create new module name with _'s between the constants
|
||||
if (debug()>=10) nodep->dumpTree(cout,"-cell:\t");
|
||||
// Evaluate all module constants
|
||||
V3Const::constifyParamsEdit(nodep);
|
||||
AstNodeModule* srcModp = nodep->modp();
|
||||
|
||||
// Make sure constification worked
|
||||
// Must be a separate loop, as constant conversion may have changed some pointers.
|
||||
//if (debug()) nodep->dumpTree(cout,"-cel2:\t");
|
||||
string longname = nodep->modp()->name();
|
||||
string longname = srcModp->name();
|
||||
bool any_overrides = false;
|
||||
if (nodep->recursive()) any_overrides = true; // Must always clone __Vrcm (recursive modules)
|
||||
longname += "_";
|
||||
if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t");
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
@@ -549,7 +559,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
&& modvarp->subDTypep()->castUnpackArrayDType()) {
|
||||
// Array assigned to array
|
||||
AstNode* exprp = pinp->exprp();
|
||||
longname += "_" + paramSmallName(nodep->modp(),modvarp)+paramValueNumber(exprp);
|
||||
longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
||||
any_overrides = true;
|
||||
} else {
|
||||
AstConst* exprp = pinp->exprp()->castConst();
|
||||
@@ -565,10 +575,10 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
} else if (exprp->num().isDouble()
|
||||
|| exprp->num().isString()
|
||||
|| exprp->num().isFourState()) {
|
||||
longname += "_" + paramSmallName(nodep->modp(),modvarp)+paramValueNumber(exprp);
|
||||
longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
||||
any_overrides = true;
|
||||
} else {
|
||||
longname += "_" + paramSmallName(nodep->modp(),modvarp)+exprp->num().ascii(false);
|
||||
longname += "_" + paramSmallName(srcModp, modvarp) + exprp->num().ascii(false);
|
||||
any_overrides = true;
|
||||
}
|
||||
}
|
||||
@@ -586,7 +596,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
// This prevents making additional modules, and makes coverage more
|
||||
// obvious as it won't show up under a unique module page name.
|
||||
} else {
|
||||
longname += "_" + paramSmallName(nodep->modp(),modvarp)+paramValueNumber(exprp);
|
||||
longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
||||
any_overrides = true;
|
||||
}
|
||||
}
|
||||
@@ -639,7 +649,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
UINFO(9," pinIfaceRef "<<pinIrefp<<endl);
|
||||
if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) {
|
||||
UINFO(9," IfaceRefDType needs reconnect "<<pinIrefp<<endl);
|
||||
longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+paramValueNumber(pinIrefp);
|
||||
longname += "_" + paramSmallName(srcModp, pinp->modVarp()) + paramValueNumber(pinIrefp);
|
||||
any_overrides = true;
|
||||
ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp));
|
||||
}
|
||||
@@ -659,35 +669,49 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
if (iter != m_longMap.end()) {
|
||||
newname = iter->second;
|
||||
} else {
|
||||
newname = nodep->modp()->name();
|
||||
newname = srcModp->name();
|
||||
newname += "__pi"+cvtToStr(++m_longId); // We use all upper case above, so lower here can't conflict
|
||||
m_longMap.insert(make_pair(longname, newname));
|
||||
}
|
||||
}
|
||||
UINFO(4,"Name: "<<nodep->modp()->name()<<"->"<<longname<<"->"<<newname<<endl);
|
||||
UINFO(4,"Name: "<<srcModp->name()<<"->"<<longname<<"->"<<newname<<endl);
|
||||
|
||||
//
|
||||
// Already made this flavor?
|
||||
AstNodeModule* modp = NULL;
|
||||
AstNodeModule* cellmodp = NULL;
|
||||
ModNameMap::iterator iter = m_modNameMap.find(newname);
|
||||
if (iter != m_modNameMap.end()) modp = iter->second.m_modp;
|
||||
if (!modp) {
|
||||
if (iter != m_modNameMap.end()) cellmodp = iter->second.m_modp;
|
||||
if (!cellmodp) {
|
||||
// Deep clone of new module
|
||||
// Note all module internal variables will be re-linked to the new modules by clone
|
||||
// However links outside the module (like on the upper cells) will not.
|
||||
modp = nodep->modp()->cloneTree(false);
|
||||
modp->name(newname);
|
||||
modp->user5(false); // We need to re-recurse this module once changed
|
||||
nodep->modp()->addNextHere(modp); // Keep tree sorted by cell occurrences
|
||||
cellmodp = srcModp->cloneTree(false);
|
||||
cellmodp->name(newname);
|
||||
cellmodp->user5(false); // We need to re-recurse this module once changed
|
||||
cellmodp->recursive(false);
|
||||
cellmodp->recursiveClone(false);
|
||||
nodep->recursive(false);
|
||||
// Recursion may need level cleanups
|
||||
if (cellmodp->level() <= m_modp->level()) cellmodp->level(m_modp->level()+1);
|
||||
if ((cellmodp->level() - srcModp->level()) >= (v3Global.opt.moduleRecursionDepth() - 2)) {
|
||||
nodep->v3error("Exceeded maximum --module-recursion-depth of "<<v3Global.opt.moduleRecursionDepth());
|
||||
}
|
||||
// Keep tree sorted by level
|
||||
AstNodeModule* insertp = srcModp;
|
||||
while (insertp->nextp()->castNodeModule()
|
||||
&& insertp->nextp()->castNodeModule()->level() < cellmodp->level()) {
|
||||
insertp = insertp->nextp()->castNodeModule();
|
||||
}
|
||||
insertp->addNextHere(cellmodp);
|
||||
|
||||
m_modNameMap.insert(make_pair(modp->name(), ModInfo(modp)));
|
||||
m_modNameMap.insert(make_pair(cellmodp->name(), ModInfo(cellmodp)));
|
||||
iter = m_modNameMap.find(newname);
|
||||
CloneMap* clonemapp = &(iter->second.m_cloneMap);
|
||||
UINFO(4," De-parameterize to new: "<<modp<<endl);
|
||||
UINFO(4," De-parameterize to new: "<<cellmodp<<endl);
|
||||
|
||||
// Grab all I/O so we can remap our pins later
|
||||
// Note we allow multiple users of a parameterized model, thus we need to stash this info.
|
||||
collectPins(clonemapp, modp);
|
||||
collectPins(clonemapp, cellmodp);
|
||||
// Relink parameter vars to the new module
|
||||
relinkPins(clonemapp, nodep->paramsp());
|
||||
|
||||
@@ -728,19 +752,21 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
}
|
||||
|
||||
} else {
|
||||
UINFO(4," De-parameterize to old: "<<modp<<endl);
|
||||
UINFO(4," De-parameterize to old: "<<cellmodp<<endl);
|
||||
}
|
||||
|
||||
// Have child use this module instead.
|
||||
nodep->modp(modp);
|
||||
nodep->modp(cellmodp);
|
||||
nodep->modName(newname);
|
||||
|
||||
// We need to relink the pins to the new module
|
||||
CloneMap* clonemapp = &(iter->second.m_cloneMap);
|
||||
relinkPins(clonemapp, nodep->pinsp());
|
||||
UINFO(8," Done with "<<modp<<endl);
|
||||
UINFO(8," Done with "<<cellmodp<<endl);
|
||||
} // if any_overrides
|
||||
|
||||
nodep->recursive(false);
|
||||
|
||||
// Delete the parameters from the cell; they're not relevant any longer.
|
||||
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
UINFO(8," Done with "<<nodep<<endl);
|
||||
@@ -748,7 +774,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
}
|
||||
|
||||
// Now remember to process the child module at the end of the module
|
||||
m_todoModps.insert(make_pair(nodep->modp()->level(),nodep->modp()));
|
||||
m_todoModps.insert(make_pair(nodep->modp()->level(), nodep->modp()));
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
+2
-2
@@ -36,9 +36,9 @@ class V3Parse {
|
||||
private:
|
||||
V3ParseImp* m_impp;
|
||||
|
||||
V3Parse(const V3Parse&); ///< N/A, no copy constructor
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(V3Parse);
|
||||
public:
|
||||
// We must allow reading multiple files into one parser
|
||||
V3Parse(AstNetlist* rootp, V3InFilter* filterp, V3ParseSym* symp);
|
||||
~V3Parse();
|
||||
|
||||
+2
-2
@@ -210,7 +210,7 @@ public:
|
||||
V3ParseSym* symp() { return m_symp; }
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
V3ParseImp(AstNetlist* rootp, V3InFilter* filterp, V3ParseSym* parserSymp)
|
||||
: m_rootp(rootp), m_filterp(filterp), m_symp(parserSymp) {
|
||||
m_fileline = NULL;
|
||||
@@ -223,7 +223,7 @@ public:
|
||||
m_ahead = false;
|
||||
m_curBisonVal.token = 0;
|
||||
m_prevBisonVal.token = 0;
|
||||
// m_aheadVal not used as m_ahead = false
|
||||
// m_aheadVal not used as m_ahead = false, and not all compilers support initing it
|
||||
m_tagNodep = NULL;
|
||||
}
|
||||
~V3ParseImp();
|
||||
|
||||
+11
-10
@@ -43,6 +43,17 @@ private:
|
||||
VSymEnt* m_symCurrentp; // Active symbol table for additions/lookups
|
||||
SymStack m_sympStack; // Stack of upper nodes with pending symbol tables
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit V3ParseSym(AstNetlist* rootp)
|
||||
: m_syms(rootp) {
|
||||
s_anonNum = 0; // Number of next anonymous object
|
||||
pushScope(findNewTable(rootp));
|
||||
m_symTableNextId = NULL;
|
||||
m_symCurrentp = symCurrentp();
|
||||
}
|
||||
~V3ParseSym() {}
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
static VSymEnt* getTable(AstNode* nodep) {
|
||||
@@ -146,16 +157,6 @@ public:
|
||||
// Export *::* from remote packages
|
||||
symCurrentp()->exportStarStar(&m_syms);
|
||||
}
|
||||
public:
|
||||
// CREATORS
|
||||
explicit V3ParseSym(AstNetlist* rootp)
|
||||
: m_syms(rootp) {
|
||||
s_anonNum = 0; // Number of next anonymous object
|
||||
pushScope(findNewTable(rootp));
|
||||
m_symTableNextId = NULL;
|
||||
m_symCurrentp = symCurrentp();
|
||||
}
|
||||
~V3ParseSym() {}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+7
-5
@@ -70,7 +70,6 @@ wsn [ \t\f]
|
||||
crnl [\r]*[\n]
|
||||
quote [\"]
|
||||
tickquote [`][\"]
|
||||
backslash [\\]
|
||||
/* Where we use symb/symbdef, we must also look for a `` join */
|
||||
/* Note in the preprocessor \ESCaped is *not* always special; mantis1537/bug441 */
|
||||
symb ([a-zA-Z_][a-zA-Z0-9_$]*|\\[^ \t\f\r\n]+)
|
||||
@@ -110,8 +109,9 @@ drop [\032]
|
||||
<STRMODE>{crnl} { linenoInc(); yyerrorf("Unterminated string"); BEGIN(INITIAL); }
|
||||
<STRMODE>{word} { yymore(); }
|
||||
<STRMODE>[^\"\\] { yymore(); }
|
||||
<STRMODE>{backslash}{crnl} { linenoInc(); yymore(); }
|
||||
<STRMODE>{backslash}. { yymore(); }
|
||||
<STRMODE>[\\]{crnl} { linenoInc(); yymore(); }
|
||||
<STRMODE>[\\]{wsn}+{crnl} { yyless(1); LEXP->curFilelinep()->v3warn(BSSPACE, "Backslash followed by whitespace, perhaps the whitespace is accidental?"); }
|
||||
<STRMODE>[\\]. { yymore(); }
|
||||
<STRMODE>{quote} { yy_pop_state();
|
||||
if (LEXP->m_parenLevel || LEXP->m_defQuote) { LEXP->m_defQuote=false; appendDefValue(yytext,yyleng); yyleng=0; }
|
||||
else return (VP_STRING); }
|
||||
@@ -144,7 +144,7 @@ drop [\032]
|
||||
<INCMODE><<EOF>> { linenoInc(); yyerrorf("EOF in unterminated include filename"); yyleng=0; yyterminate(); }
|
||||
<INCMODE>{crnl} { linenoInc(); yyerrorf("Unterminated include filename"); BEGIN(INITIAL); }
|
||||
<INCMODE>[^\>\\] { yymore(); }
|
||||
<INCMODE>{backslash}. { yymore(); }
|
||||
<INCMODE>[\\]. { yymore(); }
|
||||
<INCMODE>[\>] { yy_pop_state(); return VP_STRING; }
|
||||
|
||||
/* Reading definition formal parenthesis (or not) to begin formal arguments */
|
||||
@@ -162,6 +162,7 @@ drop [\032]
|
||||
<DEFFORM>{drop} { }
|
||||
<DEFFORM><<EOF>> { linenoInc(); yy_pop_state(); yyerrorf("Unterminated ( in define formal arguments."); yyleng=0; return VP_DEFFORM; }
|
||||
<DEFFORM>{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
|
||||
<DEFFORM>[\\]{wsn}+{crnl} { yyless(1); LEXP->curFilelinep()->v3warn(BSSPACE, "Backslash followed by whitespace, perhaps the whitespace is accidental?"); }
|
||||
<DEFFORM>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\\\n",2); } /* Include return so can maintain output line count */
|
||||
<DEFFORM>{quote} { LEXP->m_defQuote=true; yy_push_state(STRMODE); yymore(); } /* Legal only in default values */
|
||||
<DEFFORM>"`\\`\"" { appendDefValue(yytext,yyleng); } /* Maybe illegal, otherwise in default value */
|
||||
@@ -179,6 +180,7 @@ drop [\032]
|
||||
<DEFVAL>{drop} { }
|
||||
<DEFVAL><<EOF>> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */
|
||||
<DEFVAL>{crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); }
|
||||
<DEFVAL>[\\]{wsn}+{crnl} { yyless(1); LEXP->curFilelinep()->v3warn(BSSPACE, "Backslash followed by whitespace, perhaps the whitespace is accidental?"); }
|
||||
<DEFVAL>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\\\n",2); } /* Return, AND \ is part of define value */
|
||||
<DEFVAL>{quote} { LEXP->m_defQuote=true; yy_push_state(STRMODE); yymore(); }
|
||||
<DEFVAL>[^\/\*\n\r\\\"]+ |
|
||||
@@ -189,6 +191,7 @@ drop [\032]
|
||||
/* - if no \{crnl} ending then the comment belongs to the next line, as a non-embedded comment */
|
||||
/* - if all but (say) 3rd line is missing \ then it's indeterminate */
|
||||
<DEFCMT>"*/" { yy_pop_state(); appendDefValue(yytext,yyleng); }
|
||||
<DEFCMT>[\\]{wsn}+{crnl} { yyless(1); LEXP->curFilelinep()->v3warn(BSSPACE, "Backslash followed by whitespace, perhaps the whitespace is accidental?"); }
|
||||
<DEFCMT>[\\]{crnl} { linenoInc(); LEXP->m_defCmtSlash=true;
|
||||
appendDefValue(yytext,yyleng-2); appendDefValue((char*)"\n",1); } /* Return but not \ */
|
||||
<DEFCMT>{crnl} { linenoInc(); yymore(); if (LEXP->m_defCmtSlash) yyerrorf("One line of /* ... */ is missing \\ before newline");
|
||||
@@ -255,7 +258,6 @@ drop [\032]
|
||||
/* symbdef prevents normal lex rules from making `\`"foo a symbol {`"foo} instead of a BACKQUOTE */
|
||||
<INITIAL>"`"{symbdef} { return (VP_DEFREF); }
|
||||
<INITIAL>"`"{symbdef}`` { yyleng-=2; return (VP_DEFREF_JOIN); }
|
||||
<INITIAL>`` { yyleng-=2; return (VP_JOIN); }
|
||||
|
||||
/* Generics */
|
||||
<INITIAL>{crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); }
|
||||
|
||||
+8
-5
@@ -123,12 +123,15 @@ protected:
|
||||
bool preprocOpen (FileLine* fl, V3InFilter* filterp, const string& modname, const string& lastpath,
|
||||
const string& errmsg) { // Error message or "" to suppress
|
||||
// Returns true if successful
|
||||
// Allow user to put `defined names on the command line instead of filenames,
|
||||
// then convert them properly.
|
||||
string ppmodname = s_preprocp->removeDefines (modname);
|
||||
// Try a pure name in case user has a bogus `filename they don't expect
|
||||
string filename = v3Global.opt.filePath (fl, modname, lastpath, errmsg);
|
||||
if (filename=="") {
|
||||
// Allow user to put `defined names on the command line instead of filenames,
|
||||
// then convert them properly.
|
||||
string ppmodname = s_preprocp->removeDefines (modname);
|
||||
|
||||
// Open include or master file
|
||||
string filename = v3Global.opt.filePath (fl, ppmodname, lastpath, errmsg);
|
||||
filename = v3Global.opt.filePath (fl, ppmodname, lastpath, errmsg);
|
||||
}
|
||||
if (filename=="") return false; // Not found
|
||||
|
||||
UINFO(2," Reading "<<filename<<endl);
|
||||
|
||||
+10
-1
@@ -34,6 +34,9 @@
|
||||
// Clone and iterate the clone:
|
||||
// ARRAYSEL
|
||||
// Modify bitp() for the new value and set ->length(1)
|
||||
//
|
||||
// TODO: This code was written before SLICESEL was a type it might be
|
||||
// simplified to look primarily for SLICESELs.
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
@@ -101,6 +104,12 @@ class SliceVisitor : public AstNVisitor {
|
||||
cloneAndSel(snodep->expr1p(), elements, offset),
|
||||
cloneAndSel(snodep->expr2p(), elements, offset));
|
||||
}
|
||||
else if (AstSliceSel* snodep = nodep->castSliceSel()) {
|
||||
UINFO(9," cloneSliceSel("<<elements<<","<<offset<<") "<<nodep<<endl);
|
||||
int leOffset = (snodep->declRange().lo()
|
||||
+ (!snodep->declRange().littleEndian() ? snodep->declRange().elements()-1-offset : offset));
|
||||
newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset);
|
||||
}
|
||||
else if (nodep->castArraySel()
|
||||
|| nodep->castNodeVarRef()
|
||||
|| nodep->castNodeSel()) {
|
||||
@@ -137,8 +146,8 @@ class SliceVisitor : public AstNVisitor {
|
||||
if (debug()>=9) { newp->dumpTree(cout,"-new "); }
|
||||
newlistp = AstNode::addNextNull(newlistp, newp);
|
||||
}
|
||||
nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
if (debug()>=9) { cout<<endl; nodep->dumpTree(cout," Deslice-Dn: "); }
|
||||
nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
// Normal edit iterator will now iterate on all of the expansion assignments
|
||||
// This will potentially call this function again to resolve next level of slicing
|
||||
return;
|
||||
|
||||
+1
-1
@@ -416,7 +416,7 @@ private:
|
||||
}
|
||||
if (splitAlwaysp) {
|
||||
++m_statSplits;
|
||||
AstAlways* alwaysp = new AstAlways(newListp->fileline(), VAlwaysKwd::ALWAYS, NULL, NULL);
|
||||
AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, NULL, NULL);
|
||||
addAfterp->addNextHere(alwaysp); addAfterp=alwaysp;
|
||||
alwaysp->addStmtp(newListp);
|
||||
} else {
|
||||
|
||||
+48
-37
@@ -45,18 +45,20 @@ private:
|
||||
|
||||
// STATE
|
||||
string m_stage; // Name of the stage we are scanning
|
||||
bool m_fast; // Counting only fastpath
|
||||
/// m_fast = true: Counting only critical branch of fastpath
|
||||
/// m_fast = false: Counting every node, ignoring structure of program
|
||||
bool m_fast;
|
||||
|
||||
AstCFunc* m_cfuncp; // Current CFUNC
|
||||
V3Double0 m_statInstrLong; // Instruction count
|
||||
bool m_counting; // Currently counting
|
||||
double m_instrs; // Current instr count
|
||||
double m_instrs; // Current instr count (for determining branch direction)
|
||||
|
||||
vector<V3Double0> m_statTypeCount; // Nodes of given type
|
||||
V3Double0 m_statAbove[AstType::_ENUM_END][AstType::_ENUM_END]; // Nodes of given type
|
||||
V3Double0 m_statPred[AstBranchPred::_ENUM_END]; // Nodes of given type
|
||||
V3Double0 m_statInstr; // Instruction count
|
||||
V3Double0 m_statInstrFast; // Instruction count
|
||||
V3Double0 m_statInstrFast; // Instruction count, non-slow() eval functions only
|
||||
vector<V3Double0> m_statVarWidths; // Variables of given width
|
||||
vector<NameMap> m_statVarWidthNames; // Var names of given width
|
||||
V3Double0 m_statVarArray; // Statistic tracking
|
||||
@@ -65,6 +67,12 @@ private:
|
||||
V3Double0 m_statVarScpBytes; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
void allNodes(AstNode* nodep) {
|
||||
m_instrs += nodep->instrCount();
|
||||
if (m_counting) {
|
||||
@@ -128,48 +136,52 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
UINFO(4," IF "<<nodep<<endl);
|
||||
UINFO(4," IF i="<<m_instrs<<" "<<nodep<<endl);
|
||||
allNodes(nodep);
|
||||
// Condition is part of PREVIOUS block
|
||||
// Condition is part of cost allocated to PREVIOUS block
|
||||
nodep->condp()->iterateAndNextConst(*this);
|
||||
// Track prediction
|
||||
if (m_counting) {
|
||||
++m_statPred[nodep->branchPred()];
|
||||
}
|
||||
if (!m_fast) {
|
||||
// Count everything
|
||||
nodep->iterateChildrenConst(*this);
|
||||
} else {
|
||||
// See which path we want to take
|
||||
bool takeElse = false;
|
||||
if (!nodep->elsesp() || (nodep->branchPred()==AstBranchPred::BP_LIKELY)) {
|
||||
// Always take the if
|
||||
} else if (!nodep->ifsp() || (nodep->branchPred()==AstBranchPred::BP_UNLIKELY)) {
|
||||
// Always take the else
|
||||
} else {
|
||||
// Take the longer path
|
||||
bool prevCounting = m_counting;
|
||||
// Need to do even if !m_counting because maybe determining upstream if/else
|
||||
double ifInstrs = 0.0;
|
||||
double elseInstrs = 0.0;
|
||||
if (nodep->branchPred() != AstBranchPred::BP_UNLIKELY) { // Check if
|
||||
double prevInstr = m_instrs;
|
||||
m_counting = false;
|
||||
// Check if
|
||||
m_instrs = 0;
|
||||
nodep->ifsp()->iterateAndNextConst(*this);
|
||||
double instrIf = m_instrs;
|
||||
// Check else
|
||||
m_instrs = 0;
|
||||
nodep->elsesp()->iterateAndNextConst(*this);
|
||||
double instrElse = m_instrs;
|
||||
// Max of if or else condition
|
||||
takeElse = (instrElse > instrIf);
|
||||
// Restore
|
||||
m_counting = prevCounting;
|
||||
m_instrs = prevInstr + (takeElse?instrElse:instrIf);
|
||||
}
|
||||
// Count the block
|
||||
if (m_counting) {
|
||||
if (takeElse) {
|
||||
nodep->elsesp()->iterateAndNextConst(*this);
|
||||
} else {
|
||||
bool prevCounting = m_counting;
|
||||
{
|
||||
m_counting = false;
|
||||
m_instrs = 0.0;
|
||||
nodep->ifsp()->iterateAndNextConst(*this);
|
||||
ifInstrs = m_instrs;
|
||||
}
|
||||
m_instrs = prevInstr;
|
||||
m_counting = prevCounting;
|
||||
}
|
||||
if (nodep->branchPred() != AstBranchPred::BP_LIKELY) { // Check else
|
||||
double prevInstr = m_instrs;
|
||||
bool prevCounting = m_counting;
|
||||
{
|
||||
m_counting = false;
|
||||
m_instrs = 0.0;
|
||||
nodep->elsesp()->iterateAndNextConst(*this);
|
||||
elseInstrs = m_instrs;
|
||||
}
|
||||
m_instrs = prevInstr;
|
||||
m_counting = prevCounting;
|
||||
}
|
||||
// Now collect the stats
|
||||
if (m_counting) {
|
||||
if (ifInstrs >= elseInstrs) {
|
||||
nodep->ifsp()->iterateAndNextConst(*this);
|
||||
} else {
|
||||
nodep->elsesp()->iterateAndNextConst(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,7 +190,6 @@ private:
|
||||
//virtual void visit(AstWhile* nodep) {
|
||||
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
//UINFO(4," CCALL "<<nodep<<endl);
|
||||
allNodes(nodep);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
if (m_fast) {
|
||||
@@ -199,8 +210,8 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
StatsVisitor(AstNetlist* nodep, const string& stage, bool fast)
|
||||
: m_stage(stage), m_fast(fast)
|
||||
{
|
||||
: m_stage(stage), m_fast(fast) {
|
||||
UINFO(9,"Starting stats, fast="<<fast<<endl);
|
||||
m_cfuncp = NULL;
|
||||
m_counting = !m_fast;
|
||||
m_instrs = 0;
|
||||
@@ -212,7 +223,7 @@ public:
|
||||
virtual ~StatsVisitor() {
|
||||
// Done. Publish statistics
|
||||
V3Stats::addStat(m_stage, "Instruction count, TOTAL", m_statInstr);
|
||||
V3Stats::addStat(m_stage, "Instruction count, fast", m_statInstrFast);
|
||||
V3Stats::addStat(m_stage, "Instruction count, fast critical", m_statInstrFast);
|
||||
// Vars
|
||||
V3Stats::addStat(m_stage, "Vars, unpacked arrayed", m_statVarArray);
|
||||
V3Stats::addStat(m_stage, "Vars, clock attribute", m_statVarClock);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#include <iomanip>
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
@@ -102,7 +103,7 @@ class StatsReport {
|
||||
os<<endl;
|
||||
|
||||
// Print organized by stage
|
||||
os<<"Peformance Statistics:\n";
|
||||
os<<"Performance Statistics:\n";
|
||||
os<<endl;
|
||||
for (ByName::iterator it = byName.begin(); it!=byName.end(); ++it) {
|
||||
const V3Statistic* repp = it->second;
|
||||
@@ -122,7 +123,7 @@ class StatsReport {
|
||||
size_t maxWidth = 0;
|
||||
typedef vector<string> Stages;
|
||||
Stages stages;
|
||||
map<string,int> stageInt;
|
||||
vl_unordered_map<string,int> stageInt;
|
||||
typedef multimap<string,const V3Statistic*> ByName;
|
||||
ByName byName;
|
||||
// * is always first
|
||||
|
||||
+17
-17
@@ -245,18 +245,24 @@ class VSymGraph {
|
||||
// TYPES
|
||||
typedef vector<VSymEnt*> SymStack;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
VSymEnt* m_symRootp; // Root symbol table
|
||||
SymStack m_symsp; // All symbol tables, to cleanup
|
||||
|
||||
VSymGraph(const VSymGraph&); ///< N/A, no copy constructor
|
||||
|
||||
protected:
|
||||
friend class VSymEnt;
|
||||
void pushNewEnt(VSymEnt* entp) { m_symsp.push_back(entp); }
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VSymGraph);
|
||||
public:
|
||||
explicit VSymGraph(AstNetlist* nodep) {
|
||||
m_symRootp = new VSymEnt(this, nodep);
|
||||
}
|
||||
~VSymGraph() {
|
||||
for (SymStack::iterator it = m_symsp.begin(); it != m_symsp.end(); ++it) {
|
||||
delete (*it);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
VSymEnt* rootp() const { return m_symRootp; }
|
||||
// Debug
|
||||
void dump(ostream& os, const string& indent="") {
|
||||
@@ -275,21 +281,15 @@ public:
|
||||
if (v3Global.opt.dumpTree()) {
|
||||
string filename = v3Global.debugFilename(nameComment)+".txt";
|
||||
UINFO(2,"Dumping "<<filename<<endl);
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const vl_unique_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
dump(*logp, "");
|
||||
}
|
||||
}
|
||||
public:
|
||||
// CREATORS
|
||||
explicit VSymGraph(AstNetlist* nodep) {
|
||||
m_symRootp = new VSymEnt(this, nodep);
|
||||
}
|
||||
~VSymGraph() {
|
||||
for (SymStack::iterator it = m_symsp.begin(); it != m_symsp.end(); ++it) {
|
||||
delete (*it);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class VSymEnt;
|
||||
void pushNewEnt(VSymEnt* entp) { m_symsp.push_back(entp); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -303,6 +303,11 @@ class TristatePinVisitor : public TristateBaseVisitor {
|
||||
if (m_lvalue) nodep->v3fatalSrc("ArraySel conversion to output, under tristate node");
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstSliceSel* nodep) {
|
||||
// Doesn't work because we'd set lvalue on the array index's var
|
||||
if (m_lvalue) nodep->v3fatalSrc("SliceSel conversion to output, under tristate node");
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
+17
-6
@@ -19,7 +19,7 @@
|
||||
//*************************************************************************
|
||||
// V3Undriven's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Netlist:
|
||||
// Make vector for all variables
|
||||
// SEL(VARREF(...))) mark only some bits as used/driven
|
||||
// else VARREF(...) mark all bits as used/driven
|
||||
@@ -175,7 +175,11 @@ public:
|
||||
if (allU) m_usedWhole = true;
|
||||
if (allD) m_drivenWhole = true;
|
||||
// Test results
|
||||
if (allU && allD) {
|
||||
if (nodep->isIfaceRef()) {
|
||||
// For interface top level we don't do any tracking
|
||||
// Ideally we'd report unused instance cells, but presumably a signal inside one
|
||||
// would get reported as unused
|
||||
} else if (allU && allD) {
|
||||
// It's fine
|
||||
} else if (!anyD && !anyU) {
|
||||
// UNDRIVEN is considered more serious - as is more likely a bug,
|
||||
@@ -223,8 +227,11 @@ public:
|
||||
class UndrivenVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVar::user1p -> UndrivenVar* for usage var, 0=not set yet
|
||||
// Netlist:
|
||||
// AstVar::user1p -> UndrivenVar* for usage var, 0=not set yet
|
||||
AstUser1InUse m_inuser1;
|
||||
// Each always:
|
||||
// AstNode::user2p -> UndrivenVar* for usage var, 0=not set yet
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// STATE
|
||||
@@ -255,7 +262,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void warnAlwCombOrder(AstVarRef* nodep) {
|
||||
void warnAlwCombOrder(AstNodeVarRef* nodep) {
|
||||
AstVar* varp = nodep->varp();
|
||||
if (!varp->isParam() && !varp->isGenVar() && !varp->isUsedLoopIdx()
|
||||
&& !m_inBBox // We may have falsely considered a SysIgnore as a driver
|
||||
@@ -288,8 +295,12 @@ private:
|
||||
// Arrays are rarely constant assigned, so for now we punt and do all entries
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstSliceSel* nodep) {
|
||||
// Arrays are rarely constant assigned, so for now we punt and do all entries
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
AstVarRef* varrefp = nodep->fromp()->castVarRef();
|
||||
AstNodeVarRef* varrefp = nodep->fromp()->castNodeVarRef();
|
||||
AstConst* constp = nodep->lsbp()->castConst();
|
||||
if (varrefp && constp && !constp->num().isFourState()) {
|
||||
for (int usr=1; usr<(m_alwaysp?3:2); ++usr) {
|
||||
@@ -310,7 +321,7 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
// Any variable
|
||||
for (int usr=1; usr<(m_alwaysp?3:2); ++usr) {
|
||||
UndrivenVarEntry* entryp = getEntryp (nodep->varp(), usr);
|
||||
|
||||
@@ -375,6 +375,9 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// visit(AstSliceSel) not needed as its bounds are constant and checked
|
||||
// in V3Width.
|
||||
|
||||
virtual void visit(AstArraySel* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
|
||||
+48
-10
@@ -155,12 +155,13 @@ ostream& operator<<(ostream& str, const WidthVP* vup) {
|
||||
class WidthClearVisitor {
|
||||
// Rather than a AstNVisitor, can just quickly touch every node
|
||||
void clearWidthRecurse(AstNode* nodep) {
|
||||
nodep->didWidth(false);
|
||||
if (nodep->op1p()) clearWidthRecurse(nodep->op1p());
|
||||
if (nodep->op2p()) clearWidthRecurse(nodep->op2p());
|
||||
if (nodep->op3p()) clearWidthRecurse(nodep->op3p());
|
||||
if (nodep->op4p()) clearWidthRecurse(nodep->op4p());
|
||||
if (nodep->nextp()) clearWidthRecurse(nodep->nextp());
|
||||
for (; nodep; nodep = nodep->nextp()) {
|
||||
nodep->didWidth(false);
|
||||
if (nodep->op1p()) clearWidthRecurse(nodep->op1p());
|
||||
if (nodep->op2p()) clearWidthRecurse(nodep->op2p());
|
||||
if (nodep->op3p()) clearWidthRecurse(nodep->op3p());
|
||||
if (nodep->op4p()) clearWidthRecurse(nodep->op4p());
|
||||
}
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -699,6 +700,43 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstSliceSel* nodep) {
|
||||
// Always creates as output an unpacked array
|
||||
if (m_vup->prelim()) {
|
||||
userIterateAndNext(nodep->fromp(), WidthVP(SELF,BOTH).p());
|
||||
//
|
||||
// Array indices are always constant
|
||||
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp();
|
||||
AstUnpackArrayDType* adtypep = fromDtp->castUnpackArrayDType();
|
||||
if (!adtypep) {
|
||||
UINFO(1," Related dtype: "<<fromDtp<<endl);
|
||||
nodep->v3fatalSrc("Packed array reference exceeds dimension of array");
|
||||
}
|
||||
// Build new array Dtype based on the original's base type, but with new bounds
|
||||
AstNodeDType* newDtp = new AstUnpackArrayDType(nodep->fileline(),
|
||||
adtypep->subDTypep(),
|
||||
new AstRange(nodep->fileline(),
|
||||
nodep->declRange()));
|
||||
v3Global.rootp()->typeTablep()->addTypesp(newDtp);
|
||||
nodep->dtypeFrom(newDtp);
|
||||
|
||||
if (!m_doGenerate) {
|
||||
// Must check bounds before adding a select that truncates the bound
|
||||
// Note we've already subtracted off LSB
|
||||
if ((nodep->declRange().hi() > adtypep->declRange().hi())
|
||||
|| nodep->declRange().lo() < adtypep->declRange().lo()) {
|
||||
// Other simulators warn too
|
||||
nodep->v3error("Slice selection index '"<< nodep->declRange() << "'"
|
||||
<<" outside data type's '"<< adtypep->declRange() << "'");
|
||||
}
|
||||
else if ((nodep->declRange().littleEndian() != adtypep->declRange().littleEndian())) {
|
||||
nodep->v3error("Slice selection '"<< nodep->declRange() << "'"
|
||||
<<" has backward indexing versus data type's '"<< adtypep->declRange() << "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstSelBit* nodep) {
|
||||
// Just a quick check as after V3Param these nodes instead are AstSel's
|
||||
userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel
|
||||
@@ -1689,7 +1727,7 @@ private:
|
||||
}
|
||||
if (patp) {
|
||||
// Determine initial values
|
||||
vdtypep = memp;
|
||||
vdtypep = memp; if (vdtypep) {}
|
||||
patp->dtypep(memp);
|
||||
userIterate(patp, WidthVP(memp,BOTH).p()); // See visit(AstPatMember*
|
||||
|
||||
@@ -3020,9 +3058,9 @@ private:
|
||||
// Underp is used in a self-determined but boolean context, reduce a multibit number to one bit
|
||||
// stage is always BOTH so not passed as argument
|
||||
// underp may change as a result of replacement
|
||||
if (!underp) underp->v3fatalSrc("Node has no type");
|
||||
if (!underp) nodep->v3fatalSrc("Node has no type");
|
||||
underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,BOTH).p());
|
||||
if (!underp || !underp->dtypep()) underp->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it?
|
||||
if (!underp || !underp->dtypep()) nodep->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it?
|
||||
//
|
||||
// For DOUBLE under a logical op, add implied test against zero, never a warning
|
||||
if (underp && underp->isDouble()) {
|
||||
@@ -3063,7 +3101,7 @@ private:
|
||||
// Returns the new underp
|
||||
// Conversion to/from doubles and integers are before iterating.
|
||||
if (stage != FINAL) nodep->v3fatalSrc("Bad state to iterateCheck");
|
||||
if (!underp || !underp->dtypep()) underp->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it?
|
||||
if (!underp || !underp->dtypep()) nodep->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it?
|
||||
if (expDTypep == underp->dtypep()) { // Perfect
|
||||
underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p());
|
||||
} else if (expDTypep->isDouble() && underp->isDouble()) { // Also good
|
||||
|
||||
+11
-6
@@ -294,19 +294,24 @@ private:
|
||||
AstNode* lsbp = nodep->thsp()->unlinkFrBack();
|
||||
vlsint32_t msb = msbp->castConst()->toSInt();
|
||||
vlsint32_t lsb = lsbp->castConst()->toSInt();
|
||||
vlsint32_t elem = (msb>lsb) ? (msb-lsb+1) : (lsb-msb+1);
|
||||
FromData fromdata = fromDataForArray(nodep, fromp, false);
|
||||
AstNodeDType* ddtypep = fromdata.m_dtypep;
|
||||
VNumRange fromRange = fromdata.m_fromRange;
|
||||
if (ddtypep->castUnpackArrayDType()) {
|
||||
// Slice extraction
|
||||
if (fromRange.elements() == (msb-lsb+1)
|
||||
if (fromRange.elements() == elem
|
||||
&& fromRange.lo() == lsb) { // Extracting whole of original array
|
||||
nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
} else {
|
||||
// TODO when unpacked arrays fully supported probably need new data type here
|
||||
AstArraySel* newp = new AstArraySel (nodep->fileline(), fromp, lsbp);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
} else if (fromRange.elements() == 1) { // Extracting single element
|
||||
AstArraySel* newp = new AstArraySel(nodep->fileline(), fromp, lsbp);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
} else { // Slice
|
||||
AstSliceSel* newp = new AstSliceSel(nodep->fileline(), fromp,
|
||||
VNumRange(VNumRange::LeftRight(),
|
||||
msb, lsb));
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
else if (AstPackArrayDType* adtypep = ddtypep->castPackArrayDType()) {
|
||||
// SELEXTRACT(array, msb, lsb) -> SEL(array, lsb*width-of-subindex, width-of-subindex*(msb-lsb))
|
||||
|
||||
+2
-2
@@ -585,8 +585,8 @@ int main(int argc, char** argv, char** env) {
|
||||
|
||||
// Internal tests (after option parsing as need debug() setting)
|
||||
VHashSha1::selfTest();
|
||||
AstBasicDTypeKwd::test();
|
||||
V3Graph::test();
|
||||
AstBasicDTypeKwd::selfTest();
|
||||
V3Graph::selfTest();
|
||||
|
||||
//--FRONTEND------------------
|
||||
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ int main(int argc, char** argv, char** env) {
|
||||
top.opt.parseOptsList(argc-1, argv+1);
|
||||
|
||||
if (top.opt.readFiles().empty()) {
|
||||
top.opt.addReadFile("vlt_coverage.pl");
|
||||
top.opt.addReadFile("vlt_coverage.dat");
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ private:
|
||||
bool onoff(const char* sw, const char* arg, bool& flag);
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
// CONSTRUCTORS
|
||||
VlcOptions() {
|
||||
m_annotateAll = false;
|
||||
m_annotateMin = 10;
|
||||
|
||||
+2
-2
@@ -24,9 +24,9 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_cov_key.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
//********************************************************************
|
||||
// VlcPoint - A coverage point (across all tests)
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
class VlcPoints {
|
||||
private:
|
||||
// MEMBERS
|
||||
typedef std::map<string,vluint64_t> NameMap;
|
||||
typedef vl_unordered_map<string,vluint64_t> NameMap;
|
||||
NameMap m_nameMap; //< Name to point-number
|
||||
vector<VlcPoint> m_points; //< List of all points
|
||||
vluint64_t m_numPoints; //< Total unique points
|
||||
|
||||
+2
-1
@@ -211,6 +211,7 @@ sub write_report {
|
||||
|
||||
sub write_classes {
|
||||
my $fh = open_file(@_);
|
||||
printf $fh "class AstNode;\n";
|
||||
foreach my $type (sort (keys %Classes)) {
|
||||
printf $fh "class %-20s // ", "Ast${type};";
|
||||
foreach my $subclass (subclasses_of($type)) {
|
||||
@@ -450,7 +451,7 @@ sub _exec_syms_recurse {
|
||||
if (ref $sym) { _exec_syms_recurse($sym); }
|
||||
elsif ($sym =~ /^\$.*/) {
|
||||
if (!defined $_Exec_Syms{$sym}) {
|
||||
$_Exec_Syms{$sym} = "arg".($_Exec_Nsyms++)."p";
|
||||
$_Exec_Syms{$sym} = "arg".(++$_Exec_Nsyms)."p";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +783,9 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||
"+:" { FL; return yP_PLUSCOLON; }
|
||||
"-:" { FL; return yP_MINUSCOLON; }
|
||||
".*" { FL; return yP_DOTSTAR; }
|
||||
":+" { FL; yyless(1);
|
||||
PARSEP->fileline()->v3warn(COLONPLUS, "Perhaps instead of ':+' the intent was '+:'?");
|
||||
return ':'; }
|
||||
}
|
||||
|
||||
/* SystemVerilog Operators */
|
||||
|
||||
+61
-30
@@ -1074,7 +1074,7 @@ program_generate_item<nodep>: // ==IEEE: program_generate_item
|
||||
loop_generate_construct { $$ = $1; }
|
||||
| conditional_generate_construct { $$ = $1; }
|
||||
| generate_region { $$ = $1; }
|
||||
//UNSUP elaboration_system_task { $$ = $1; }
|
||||
| elaboration_system_task { $$ = $1; }
|
||||
;
|
||||
|
||||
modport_declaration<nodep>: // ==IEEE: modport_declaration
|
||||
@@ -1626,15 +1626,15 @@ type_declaration<nodep>: // ==IEEE: type_declaration
|
||||
// // Use idAny, as we can redeclare a typedef on an existing typedef
|
||||
yTYPEDEF data_type idAny variable_dimensionListE dtypeAttrListE ';'
|
||||
/**/ { $$ = new AstTypedef($<fl>1, *$3, $5, VFlagChildDType(), GRAMMARP->createArray($2,$4,false));
|
||||
SYMP->reinsert($$); }
|
||||
SYMP->reinsert($$); PARSEP->tagNodep($$); }
|
||||
//UNSUP yTYPEDEF id/*interface*/ '.' idAny/*type*/ idAny/*type*/ ';' { $$ = NULL; $1->v3error("Unsupported: SystemVerilog 2005 typedef in this context"); } //UNSUP
|
||||
// // Combines into above "data_type id" rule
|
||||
// // Verilator: Not important what it is in the AST, just need to make sure the yaID__aTYPE gets returned
|
||||
| yTYPEDEF id ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$2); SYMP->reinsert($$); }
|
||||
| yTYPEDEF yENUM idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
|
||||
| yTYPEDEF ySTRUCT idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
|
||||
| yTYPEDEF yUNION idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
|
||||
//UNSUP yTYPEDEF yCLASS idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
|
||||
| yTYPEDEF id ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$2); SYMP->reinsert($$); PARSEP->tagNodep($$); }
|
||||
| yTYPEDEF yENUM idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); PARSEP->tagNodep($$); }
|
||||
| yTYPEDEF ySTRUCT idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); PARSEP->tagNodep($$); }
|
||||
| yTYPEDEF yUNION idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); PARSEP->tagNodep($$); }
|
||||
//UNSUP yTYPEDEF yCLASS idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); PARSEP->tagNodep($$); }
|
||||
;
|
||||
|
||||
dtypeAttrListE<nodep>:
|
||||
@@ -1722,6 +1722,7 @@ module_common_item<nodep>: // ==IEEE: module_common_item
|
||||
| yALWAYS_LATCH event_controlE stmtBlock { $$ = new AstAlways($1,VAlwaysKwd::ALWAYS_LATCH, $2,$3); }
|
||||
| loop_generate_construct { $$ = $1; }
|
||||
| conditional_generate_construct { $$ = $1; }
|
||||
| elaboration_system_task { $$ = $1; }
|
||||
//
|
||||
| error ';' { $$ = NULL; }
|
||||
;
|
||||
@@ -2685,34 +2686,32 @@ system_f_call<nodep>: // IEEE: system_tf_call (as func)
|
||||
| yaD_DPI parenE { $$ = new AstFuncRef($<fl>1,*$1,NULL); }
|
||||
| yaD_DPI '(' exprList ')' { $$ = new AstFuncRef($2,*$1,$3); GRAMMARP->argWrapList($$->castFuncRef()); }
|
||||
//
|
||||
| yD_BITS '(' data_type ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3); }
|
||||
| yD_BITS '(' data_type ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3,$5); }
|
||||
| yD_BITS '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3); }
|
||||
| yD_BITS '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3,$5); }
|
||||
| yD_BITS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3); }
|
||||
| yD_BITS '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3,$5); }
|
||||
| yD_BITSTOREAL '(' expr ')' { $$ = new AstBitsToRealD($1,$3); }
|
||||
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? NULL : new AstUCFunc($1,$3)); }
|
||||
| yD_CEIL '(' expr ')' { $$ = new AstCeilD($1,$3); }
|
||||
| yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); }
|
||||
| yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); }
|
||||
| yD_DIMENSIONS '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); }
|
||||
| yD_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); }
|
||||
| yD_EXP '(' expr ')' { $$ = new AstExpD($1,$3); }
|
||||
| yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); }
|
||||
| yD_FGETC '(' expr ')' { $$ = new AstFGetC($1,$3); }
|
||||
| yD_FGETS '(' idClassSel ',' expr ')' { $$ = new AstFGetS($1,$3,$5); }
|
||||
| yD_FLOOR '(' expr ')' { $$ = new AstFloorD($1,$3); }
|
||||
| yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF($1,*$5,$3,$6); }
|
||||
| yD_HIGH '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_HIGH,$3,NULL); }
|
||||
| yD_HIGH '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_HIGH,$3,$5); }
|
||||
| yD_INCREMENT '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_INCREMENT,$3,NULL); }
|
||||
| yD_INCREMENT '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_INCREMENT,$3,$5); }
|
||||
| yD_HIGH '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_HIGH,$3,NULL); }
|
||||
| yD_HIGH '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_HIGH,$3,$5); }
|
||||
| yD_INCREMENT '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_INCREMENT,$3,NULL); }
|
||||
| yD_INCREMENT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_INCREMENT,$3,$5); }
|
||||
| yD_ISUNKNOWN '(' expr ')' { $$ = new AstIsUnknown($1,$3); }
|
||||
| yD_ITOR '(' expr ')' { $$ = new AstIToRD($1,$3); }
|
||||
| yD_LEFT '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LEFT,$3,NULL); }
|
||||
| yD_LEFT '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LEFT,$3,$5); }
|
||||
| yD_LEFT '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LEFT,$3,NULL); }
|
||||
| yD_LEFT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LEFT,$3,$5); }
|
||||
| yD_LN '(' expr ')' { $$ = new AstLogD($1,$3); }
|
||||
| yD_LOG10 '(' expr ')' { $$ = new AstLog10D($1,$3); }
|
||||
| yD_LOW '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,NULL); }
|
||||
| yD_LOW '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,$5); }
|
||||
| yD_LOW '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,NULL); }
|
||||
| yD_LOW '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,$5); }
|
||||
| yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); }
|
||||
| yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); }
|
||||
| yD_POW '(' expr ',' expr ')' { $$ = new AstPowD($1,$3,$5); }
|
||||
@@ -2720,28 +2719,48 @@ system_f_call<nodep>: // IEEE: system_tf_call (as func)
|
||||
| yD_RANDOM parenE { $$ = new AstRand($1); }
|
||||
| yD_REALTIME parenE { $$ = new AstTimeD($1); }
|
||||
| yD_REALTOBITS '(' expr ')' { $$ = new AstRealToBits($1,$3); }
|
||||
| yD_RIGHT '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_RIGHT,$3,NULL); }
|
||||
| yD_RIGHT '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_RIGHT,$3,$5); }
|
||||
| yD_RIGHT '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_RIGHT,$3,NULL); }
|
||||
| yD_RIGHT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_RIGHT,$3,$5); }
|
||||
| yD_RTOI '(' expr ')' { $$ = new AstRToIS($1,$3); }
|
||||
| yD_SFORMATF '(' str commaEListE ')' { $$ = new AstSFormatF($1,*$3,false,$4); }
|
||||
| yD_SIGNED '(' expr ')' { $$ = new AstSigned($1,$3); }
|
||||
| yD_SIZE '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_SIZE,$3,NULL); }
|
||||
| yD_SIZE '(' expr ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_SIZE,$3,$5); }
|
||||
| yD_SIZE '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_SIZE,$3,NULL); }
|
||||
| yD_SIZE '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_SIZE,$3,$5); }
|
||||
| yD_SQRT '(' expr ')' { $$ = new AstSqrtD($1,$3); }
|
||||
| yD_SSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstSScanF($1,*$5,$3,$6); }
|
||||
| yD_STIME parenE { $$ = new AstSel($1,new AstTime($1),0,32); }
|
||||
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemF($1,$3); }
|
||||
| yD_TESTPLUSARGS '(' str ')' { $$ = new AstTestPlusArgs($1,*$3); }
|
||||
| yD_TIME parenE { $$ = new AstTime($1); }
|
||||
| yD_UNPACKED_DIMENSIONS '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_UNPK_DIMENSIONS,$3); }
|
||||
| yD_UNPACKED_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_UNPK_DIMENSIONS,$3); }
|
||||
| yD_UNSIGNED '(' expr ')' { $$ = new AstUnsigned($1,$3); }
|
||||
| yD_VALUEPLUSARGS '(' expr ',' expr ')' { $$ = new AstValuePlusArgs($1,$3,$5); }
|
||||
;
|
||||
|
||||
elaboration_system_task<nodep>: // IEEE: elaboration_system_task (1800-2009)
|
||||
// // TODO: These currently just make initial statements, should instead give runtime error
|
||||
elaboration_system_task_guts ';' { $$ = new AstInitial($<fl>1, $1); }
|
||||
;
|
||||
|
||||
elaboration_system_task_guts<nodep>: // IEEE: part of elaboration_system_task (1800-2009)
|
||||
// // $fatal first argument is exit number, must be constant
|
||||
yD_INFO parenE { $$ = new AstDisplay($1,AstDisplayType::DT_INFO, "", NULL,NULL); }
|
||||
| yD_INFO '(' str commaEListE ')' { $$ = new AstDisplay($1,AstDisplayType::DT_INFO, *$3,NULL,$4); }
|
||||
| yD_WARNING parenE { $$ = new AstDisplay($1,AstDisplayType::DT_WARNING,"", NULL,NULL); }
|
||||
| yD_WARNING '(' str commaEListE ')' { $$ = new AstDisplay($1,AstDisplayType::DT_WARNING,*$3,NULL,$4); }
|
||||
| yD_ERROR parenE { $$ = GRAMMARP->createDisplayError($1); }
|
||||
| yD_ERROR '(' str commaEListE ')' { $$ = new AstDisplay($1,AstDisplayType::DT_ERROR, *$3,NULL,$4); $$->addNext(new AstStop($1)); }
|
||||
| yD_FATAL parenE { $$ = new AstDisplay($1,AstDisplayType::DT_FATAL, "", NULL,NULL); $$->addNext(new AstStop($1)); }
|
||||
| yD_FATAL '(' expr ')' { $$ = new AstDisplay($1,AstDisplayType::DT_FATAL, "", NULL,NULL); $$->addNext(new AstStop($1)); DEL($3); }
|
||||
| yD_FATAL '(' expr ',' str commaEListE ')' { $$ = new AstDisplay($1,AstDisplayType::DT_FATAL, *$5,NULL,$6); $$->addNext(new AstStop($1)); DEL($3); }
|
||||
;
|
||||
|
||||
exprOrDataType<nodep>: // expr | data_type: combined to prevent conflicts
|
||||
expr { $$ = $1; }
|
||||
// // data_type includes id that overlaps expr, so special flavor
|
||||
| data_type { $$ = $1; }
|
||||
// // not in spec, but needed for $past(sig,1,,@(posedge clk))
|
||||
//UNSUP event_control { }
|
||||
;
|
||||
|
||||
list_of_argumentsE<nodep>: // IEEE: [list_of_arguments]
|
||||
@@ -3761,11 +3780,23 @@ int V3ParseImp::bisonParse() {
|
||||
|
||||
const char* V3ParseImp::tokenName(int token) {
|
||||
#if YYDEBUG || YYERROR_VERBOSE
|
||||
if (token >= 255)
|
||||
return yytname[token-255];
|
||||
else {
|
||||
static char ch[2]; ch[0]=token; ch[1]='\0';
|
||||
return ch;
|
||||
static const char** nameTablep = NULL;
|
||||
if (!nameTablep) {
|
||||
int size;
|
||||
for (size=0; yytname[size]; ++size) ;
|
||||
nameTablep = new const char* [size];
|
||||
// Workaround bug in bison's which have '!' in yytname but not token values
|
||||
int iout = 0;
|
||||
for (int i=0; yytname[i]; ++i) {
|
||||
if (yytname[i][0] == '\'') continue;
|
||||
nameTablep[iout++] = yytname[i];
|
||||
}
|
||||
}
|
||||
if (token >= 255) {
|
||||
return nameTablep[token-255];
|
||||
} else {
|
||||
static char ch[2]; ch[0]=token; ch[1]='\0';
|
||||
return ch;
|
||||
}
|
||||
#else
|
||||
return "";
|
||||
|
||||
@@ -73,6 +73,12 @@ random_forever:
|
||||
echo ; \
|
||||
done
|
||||
|
||||
#######################################################################
|
||||
# Informational - used by some tests
|
||||
|
||||
print-cxx-version:
|
||||
$(CXX) --version
|
||||
|
||||
######################################################################
|
||||
maintainer-copy::
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
|
||||
@@ -31,6 +31,7 @@ CPPFLAGS += -DVERILATOR=1
|
||||
# Needed by tracing routines
|
||||
CPPFLAGS += -DVL_DEBUG=1
|
||||
CPPFLAGS += -DVM_PREFIX=$(VM_PREFIX)
|
||||
CPPFLAGS += -DVM_PREFIX_INCLUDE="<$(VM_PREFIX).h>"
|
||||
CPPFLAGS += $(CPPFLAGS_DRIVER)
|
||||
CPPFLAGS += $(CPPFLAGS_DRIVER2)
|
||||
CPPFLAGS += $(CPPFLAGS_ADD)
|
||||
@@ -40,8 +41,6 @@ ifeq ($(CFG_WITH_LONGTESTS),yes)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
else ifeq ($(DRIVER_STD),oldest)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_STD_OLDEST)
|
||||
else ifneq ($(findstring VL_THREADED,$(CPPFLAGS)),)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
+22
-6
@@ -3,14 +3,17 @@
|
||||
######################################################################
|
||||
|
||||
require 5.006_001;
|
||||
use Cwd;
|
||||
BEGIN {
|
||||
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
|
||||
# Magic to allow author testing of perl packages in local directory
|
||||
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
|
||||
}
|
||||
if (!$ENV{VERILATOR_ROOT} && -x "../bin/verilator") {
|
||||
$ENV{VERILATOR_ROOT} = Cwd::getcwd()."/..";
|
||||
}
|
||||
}
|
||||
|
||||
use Cwd;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
@@ -678,8 +681,9 @@ sub compile {
|
||||
if (!$param{fails} && $param{verilator_make_gcc}) {
|
||||
$self->oprint("GCC\n");
|
||||
$self->_run(logfile=>"$self->{obj_dir}/vlt_gcc.log",
|
||||
cmd=>["cd $self->{obj_dir} && ",
|
||||
"make", "-f".getcwd()."/Makefile_obj",
|
||||
cmd=>["make",
|
||||
"-C ".$self->{obj_dir},
|
||||
"-f ".getcwd()."/Makefile_obj",
|
||||
"VM_PREFIX=$self->{VM_PREFIX}",
|
||||
"CPPFLAGS_DRIVER=-D".uc($self->{name}),
|
||||
($opt_verbose ? "CPPFLAGS_DRIVER2=-DTEST_VERBOSE=1":""),
|
||||
@@ -1253,7 +1257,7 @@ sub _make_top_v {
|
||||
print $fh " t t (\n";
|
||||
my $comma="";
|
||||
foreach my $inp (sort (keys %{$self->{inputs}})) {
|
||||
print $fh "\t${comma}.${inp} (${inp})\n";
|
||||
print $fh " ${comma}.${inp} (${inp})\n";
|
||||
$comma=",";
|
||||
}
|
||||
print $fh " );\n";
|
||||
@@ -1304,7 +1308,7 @@ sub _make_top_vhdl {
|
||||
print $fh " port(\n";
|
||||
my $semi = "";
|
||||
foreach my $inp (@ports) {
|
||||
print $fh "\t${semi}${inp} : in std_logic\n";
|
||||
print $fh " ${semi}${inp} : in std_logic\n";
|
||||
$semi=";";
|
||||
}
|
||||
print $fh " );\n";
|
||||
@@ -1318,7 +1322,7 @@ sub _make_top_vhdl {
|
||||
print $fh "architecture t_beh of t_ent is\n";
|
||||
if ($#ports >= 0) {
|
||||
foreach my $inp (@ports) {
|
||||
print $fh "\tsignal ${inp} : std_logic := '0';\n";
|
||||
print $fh " signal ${inp} : std_logic := '0';\n";
|
||||
}
|
||||
}
|
||||
print $fh " begin\n";
|
||||
@@ -1503,6 +1507,18 @@ sub _vcd_read {
|
||||
return $data;
|
||||
}
|
||||
|
||||
our $_Cxx_Version;
|
||||
sub cxx_version {
|
||||
$_Cxx_Version ||= `make -f Makefile print-cxx-version`;
|
||||
return $_Cxx_Version;
|
||||
}
|
||||
|
||||
our $_Cfg_With_Threaded;
|
||||
sub cfg_with_threaded {
|
||||
my $out = `make -f ../Makefile print-cfg-with-threaded`;
|
||||
return ($out =~ /yes/i) ? 1:0;
|
||||
}
|
||||
|
||||
sub file_grep_not {
|
||||
my $self = (ref $_[0]? shift : $Self);
|
||||
my $filename = shift;
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
compile (
|
||||
verilator_flags2 => ["--stats"],
|
||||
);
|
||||
|
||||
if ($Self->{vlt}) {
|
||||
file_grep ($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0);
|
||||
}
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,158 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2017 by Wilson Snyder.
|
||||
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc=0;
|
||||
reg [63:0] crc;
|
||||
reg [63:0] sum;
|
||||
|
||||
// Take CRC data and apply to testblock inputs
|
||||
wire [3:0] in = crc[3:0];
|
||||
wire clken = crc[4];
|
||||
wire rstn = !(cyc < 20 || (crc[11:8]==0));
|
||||
|
||||
/*AUTOWIRE*/
|
||||
// Beginning of automatic wires (for undeclared instantiated-module outputs)
|
||||
wire [3:0] ff_out; // From test of Test.v
|
||||
wire [3:0] fg_out; // From test of Test.v
|
||||
wire [3:0] fh_out; // From test of Test.v
|
||||
// End of automatics
|
||||
|
||||
Test test (/*AUTOINST*/
|
||||
// Outputs
|
||||
.ff_out (ff_out[3:0]),
|
||||
.fg_out (fg_out[3:0]),
|
||||
.fh_out (fh_out[3:0]),
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.clken (clken),
|
||||
.rstn (rstn),
|
||||
.in (in[3:0]));
|
||||
|
||||
// Aggregate outputs into a single result vector
|
||||
wire [63:0] result = {52'h0, ff_out, fg_out, fh_out};
|
||||
|
||||
// Test loop
|
||||
always @ (posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result);
|
||||
`endif
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]};
|
||||
if (cyc==0) begin
|
||||
// Setup
|
||||
crc <= 64'h5aef0c8d_d70a4497;
|
||||
sum <= '0;
|
||||
end
|
||||
else if (cyc<10) begin
|
||||
sum <= '0;
|
||||
end
|
||||
else if (cyc<90) begin
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
|
||||
if (crc !== 64'hc77bb9b3784ea091) $stop;
|
||||
// What checksum will we end up with (above print should match)
|
||||
`define EXPECTED_SUM 64'h77979747fd1b3a5a
|
||||
if (sum !== `EXPECTED_SUM) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
module Test
|
||||
(/*AUTOARG*/
|
||||
// Outputs
|
||||
ff_out, fg_out, fh_out,
|
||||
// Inputs
|
||||
clk, clken, rstn, in
|
||||
);
|
||||
|
||||
input clk;
|
||||
input clken;
|
||||
input rstn;
|
||||
|
||||
input [3:0] in;
|
||||
|
||||
output reg [3:0] ff_out;
|
||||
reg [3:0] ff_10;
|
||||
reg [3:0] ff_11;
|
||||
reg [3:0] ff_12;
|
||||
reg [3:0] ff_13;
|
||||
always @(posedge clk) begin
|
||||
if ((rstn == 0)) begin
|
||||
ff_10 <= 0;
|
||||
ff_11 <= 0;
|
||||
ff_12 <= 0;
|
||||
ff_13 <= 0;
|
||||
end
|
||||
else begin
|
||||
ff_10 <= in;
|
||||
ff_11 <= ff_10;
|
||||
ff_12 <= ff_11;
|
||||
ff_13 <= ff_12;
|
||||
ff_out <= ff_13;
|
||||
end
|
||||
end
|
||||
|
||||
output reg [3:0] fg_out;
|
||||
reg [3:0] fg_10;
|
||||
reg [3:0] fg_11;
|
||||
reg [3:0] fg_12;
|
||||
reg [3:0] fg_13;
|
||||
always @(posedge clk) begin
|
||||
if (clken) begin
|
||||
if ((rstn == 0)) begin
|
||||
fg_10 <= 0;
|
||||
fg_11 <= 0;
|
||||
fg_12 <= 0;
|
||||
fg_13 <= 0;
|
||||
end
|
||||
else begin
|
||||
fg_10 <= in;
|
||||
fg_11 <= fg_10;
|
||||
fg_12 <= fg_11;
|
||||
fg_13 <= fg_12;
|
||||
fg_out <= fg_13;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
output reg [3:0] fh_out;
|
||||
reg [3:0] fh_10;
|
||||
reg [3:0] fh_11;
|
||||
reg [3:0] fh_12;
|
||||
reg [3:0] fh_13;
|
||||
always @(posedge clk) begin
|
||||
if ((rstn == 0)) begin
|
||||
fh_10 <= 0;
|
||||
fh_11 <= 0;
|
||||
fh_12 <= 0;
|
||||
fh_13 <= 0;
|
||||
end
|
||||
else begin
|
||||
if (clken) begin
|
||||
fh_10 <= in;
|
||||
fh_11 <= fh_10;
|
||||
fh_12 <= fh_11;
|
||||
fh_13[3:1] <= fh_12[3:1];
|
||||
fh_13[0] <= fh_12[0];
|
||||
fh_out <= fh_13;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
endmodule
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
compile (
|
||||
fails=>1,
|
||||
expect=>
|
||||
q{%Error: t/t_array_backw_index_bad.v:\d+: Slice selection '\[1:3\]' has backward indexing versus data type's '\[3:0\]'
|
||||
%Error: t/t_array_backw_index_bad.v:\d+: Slice selection '\[3:1\]' has backward indexing versus data type's '\[0:3\]'
|
||||
%Error: t/t_array_backw_index_bad.v:\d+: Slice selection index '\[4:3\]' outside data type's '\[3:0\]'
|
||||
%Error: t/t_array_backw_index_bad.v:\d+: Slice selection index '\[1:-1\]' outside data type's '\[3:0\]'
|
||||
.*%Error: Exiting due to.*},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,22 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2017 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
|
||||
logic [31:0] array_assign [3:0];
|
||||
|
||||
logic [31:0] larray_assign [0:3];
|
||||
|
||||
initial begin
|
||||
array_assign[1:3] = '{32'd4, 32'd3, 32'd2};
|
||||
larray_assign[3:1] = '{32'd4, 32'd3, 32'd2};
|
||||
|
||||
array_assign[4:3] = '{32'd4, 32'd3};
|
||||
array_assign[1:-1] = '{32'd4, 32'd3};
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user